diff --git a/.eslintrc b/.eslintrc index 17a4f64b77..e99b547ffc 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,10 +1,11 @@ { "root": true, - "env": { - "node": true, - }, "extends": [ - "habitrpg", - "habitrpg/esnext" + "habitrpg/lib/node" ], + "rules": { + 'no-param-reassign': ['error', { + props: false, + }], + } } diff --git a/gulp/gulp-apidoc.js b/gulp/gulp-apidoc.js index 6d34e6f026..55709fbdc2 100644 --- a/gulp/gulp-apidoc.js +++ b/gulp/gulp-apidoc.js @@ -4,12 +4,12 @@ import apidoc from 'apidoc'; const APIDOC_DEST_PATH = './apidoc_build'; const APIDOC_SRC_PATH = './website/server'; -gulp.task('apidoc:clean', (done) => { +gulp.task('apidoc:clean', done => { clean(APIDOC_DEST_PATH, done); }); -gulp.task('apidoc', gulp.series('apidoc:clean', (done) => { - let result = apidoc.createDoc({ +gulp.task('apidoc', gulp.series('apidoc:clean', done => { + const result = apidoc.createDoc({ src: APIDOC_SRC_PATH, dest: APIDOC_DEST_PATH, }); @@ -21,6 +21,4 @@ gulp.task('apidoc', gulp.series('apidoc:clean', (done) => { } })); -gulp.task('apidoc:watch', gulp.series('apidoc', (done) => { - return gulp.watch(`${APIDOC_SRC_PATH}/**/*.js`, gulp.series('apidoc', done)); -})); +gulp.task('apidoc:watch', gulp.series('apidoc', done => gulp.watch(`${APIDOC_SRC_PATH}/**/*.js`, gulp.series('apidoc', done)))); diff --git a/gulp/gulp-build.js b/gulp/gulp-build.js index 87d2492902..535b0178b5 100644 --- a/gulp/gulp-build.js +++ b/gulp/gulp-build.js @@ -1,32 +1,28 @@ import gulp from 'gulp'; import babel from 'gulp-babel'; -gulp.task('build:src', () => { - return gulp.src('website/server/**/*.js') - .pipe(babel()) - .pipe(gulp.dest('website/transpiled-babel/')); -}); +gulp.task('build:src', () => gulp.src('website/server/**/*.js') + .pipe(babel()) + .pipe(gulp.dest('website/transpiled-babel/'))); -gulp.task('build:common', () => { - return gulp.src('website/common/script/**/*.js') - .pipe(babel()) - .pipe(gulp.dest('website/common/transpiled-babel/')); -}); +gulp.task('build:common', () => gulp.src('website/common/script/**/*.js') + .pipe(babel()) + .pipe(gulp.dest('website/common/transpiled-babel/'))); gulp.task('build:server', gulp.series('build:src', 'build:common', done => done())); gulp.task('build:prod', gulp.series( 'build:server', 'apidoc', - done => done() + done => done(), )); -let buildArgs = []; +const buildArgs = []; if (process.env.NODE_ENV === 'production') { // eslint-disable-line no-process-env buildArgs.push('build:prod'); } -gulp.task('build', gulp.series(buildArgs, (done) => { +gulp.task('build', gulp.series(buildArgs, done => { done(); -})); \ No newline at end of file +})); diff --git a/gulp/gulp-console.js b/gulp/gulp-console.js index 3fc83bb793..15038fd6b5 100644 --- a/gulp/gulp-console.js +++ b/gulp/gulp-console.js @@ -1,26 +1,30 @@ import mongoose from 'mongoose'; -import logger from '../website/server/libs/logger'; -import nconf from 'nconf'; -import repl from 'repl'; -import gulp from 'gulp'; +import nconf from 'nconf'; +import repl from 'repl'; +import gulp from 'gulp'; +import logger from '../website/server/libs/logger'; // Add additional properties to the repl's context -let improveRepl = (context) => { +const improveRepl = context => { // Let "exit" and "quit" terminate the console - ['exit', 'quit'].forEach((term) => { - Object.defineProperty(context, term, { get () { - process.exit(); - }}); + ['exit', 'quit'].forEach(term => { + Object.defineProperty(context, term, { + get () { // eslint-disable-line getter-return + process.exit(); + }, + }); }); // "clear" clears the screen - Object.defineProperty(context, 'clear', { get () { - process.stdout.write('\u001B[2J\u001B[0;0f'); - }}); + Object.defineProperty(context, 'clear', { + get () { // eslint-disable-line getter-return + process.stdout.write('\u001B[2J\u001B[0;0f'); + }, + }); context.Challenge = require('../website/server/models/challenge').model; // eslint-disable-line global-require - context.Group = require('../website/server/models/group').model; // eslint-disable-line global-require - context.User = require('../website/server/models/user').model; // eslint-disable-line global-require + context.Group = require('../website/server/models/group').model; // eslint-disable-line global-require + context.User = require('../website/server/models/user').model; // eslint-disable-line global-require const isProd = nconf.get('NODE_ENV') === 'production'; const mongooseOptions = !isProd ? {} : { @@ -30,14 +34,14 @@ let improveRepl = (context) => { mongoose.connect( nconf.get('NODE_DB_URI'), mongooseOptions, - (err) => { + err => { if (err) throw err; logger.info('Connected with Mongoose'); - } + }, ); }; -gulp.task('console', (done) => { +gulp.task('console', done => { improveRepl(repl.start({ prompt: 'Habitica > ', }).context); diff --git a/gulp/gulp-sprites.js b/gulp/gulp-sprites.js index 0327721af0..fce0bae980 100644 --- a/gulp/gulp-sprites.js +++ b/gulp/gulp-sprites.js @@ -4,9 +4,9 @@ import spritesmith from 'gulp.spritesmith'; import clean from 'rimraf'; import sizeOf from 'image-size'; import mergeStream from 'merge-stream'; -import {basename} from 'path'; -import {sync} from 'glob'; -import {each} from 'lodash'; +import { basename } from 'path'; +import { sync } from 'glob'; +import { each } from 'lodash'; import vinylBuffer from 'vinyl-buffer'; // https://github.com/Ensighten/grunt-spritesmith/issues/67#issuecomment-34786248 @@ -16,17 +16,17 @@ const IMG_DIST_PATH = 'website/client/src/assets/images/sprites/'; const CSS_DIST_PATH = 'website/client/src/assets/css/sprites/'; function checkForSpecialTreatment (name) { - let regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame|^eyewear_special_\w+HalfMoon/; + const regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame|^eyewear_special_\w+HalfMoon/; return name.match(regex) || name === 'head_0'; } function calculateImgDimensions (img, addPadding) { let dims = sizeOf(img); - let requiresSpecialTreatment = checkForSpecialTreatment(img); + const requiresSpecialTreatment = checkForSpecialTreatment(img); if (requiresSpecialTreatment) { - let newWidth = dims.width < 90 ? 90 : dims.width; - let newHeight = dims.height < 90 ? 90 : dims.height; + const newWidth = dims.width < 90 ? 90 : dims.width; + const newHeight = dims.height < 90 ? 90 : dims.height; dims = { width: newWidth, height: newHeight, @@ -41,17 +41,17 @@ function calculateImgDimensions (img, addPadding) { if (!dims.width || !dims.height) console.error('MISSING DIMENSIONS:', dims); // eslint-disable-line no-console - let totalPixelSize = dims.width * dims.height + padding; + const totalPixelSize = dims.width * dims.height + padding; return totalPixelSize; } function calculateSpritesheetsSrcIndicies (src) { let totalPixels = 0; - let slices = [0]; + const slices = [0]; each(src, (img, index) => { - let imageSize = calculateImgDimensions(img, true); + const imageSize = calculateImgDimensions(img, true); totalPixels += imageSize; if (totalPixels > MAX_SPRITESHEET_SIZE) { @@ -64,37 +64,35 @@ function calculateSpritesheetsSrcIndicies (src) { } function cssVarMap (sprite) { - // For hair, skins, beards, etc. we want to output a '.customize-options.WHATEVER' class, which works as a - // 60x60 image pointing at the proper part of the 90x90 sprite. + // For hair, skins, beards, etc. we want to output a '.customize-options.WHATEVER' class, + // which works as a 60x60 image pointing at the proper part of the 90x90 sprite. // We set up the custom info here, and the template makes use of it. - let requiresSpecialTreatment = checkForSpecialTreatment(sprite.name); + const requiresSpecialTreatment = checkForSpecialTreatment(sprite.name); if (requiresSpecialTreatment) { sprite.custom = { px: { - offsetX: `-${ sprite.x + 25 }px`, - offsetY: `-${ sprite.y + 15 }px`, + offsetX: `-${sprite.x + 25}px`, + offsetY: `-${sprite.y + 15}px`, width: '60px', height: '60px', }, }; } - if (sprite.name.indexOf('shirt') !== -1) - sprite.custom.px.offsetY = `-${ sprite.y + 35 }px`; // even more for shirts + if (sprite.name.indexOf('shirt') !== -1) sprite.custom.px.offsetY = `-${sprite.y + 35}px`; // even more for shirts if (sprite.name.indexOf('hair_base') !== -1) { - let styleArray = sprite.name.split('_').slice(2, 3); - if (Number(styleArray[0]) > 14) - sprite.custom.px.offsetY = `-${ sprite.y }px`; // don't crop updos + const styleArray = sprite.name.split('_').slice(2, 3); + if (Number(styleArray[0]) > 14) sprite.custom.px.offsetY = `-${sprite.y}px`; // don't crop updos } } function createSpritesStream (name, src) { - let spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src); - let stream = mergeStream(); + const spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src); + const stream = mergeStream(); each(spritesheetSliceIndicies, (start, index) => { - let slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]); + const slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]); - let spriteData = gulp.src(slicedSrc) + const spriteData = gulp.src(slicedSrc) .pipe(spritesmith({ imgName: `spritesmith-${name}-${index}.png`, cssName: `spritesmith-${name}-${index}.css`, @@ -104,12 +102,12 @@ function createSpritesStream (name, src) { cssVarMap, })); - let imgStream = spriteData.img + const imgStream = spriteData.img .pipe(vinylBuffer()) .pipe(imagemin()) .pipe(gulp.dest(IMG_DIST_PATH)); - let cssStream = spriteData.css + const cssStream = spriteData.css .pipe(gulp.dest(CSS_DIST_PATH)); stream.add(imgStream); @@ -120,32 +118,32 @@ function createSpritesStream (name, src) { } gulp.task('sprites:main', () => { - let mainSrc = sync('website/raw_sprites/spritesmith/**/*.png'); + const mainSrc = sync('website/raw_sprites/spritesmith/**/*.png'); return createSpritesStream('main', mainSrc); }); gulp.task('sprites:largeSprites', () => { - let largeSrc = sync('website/raw_sprites/spritesmith_large/**/*.png'); + const largeSrc = sync('website/raw_sprites/spritesmith_large/**/*.png'); return createSpritesStream('largeSprites', largeSrc); }); -gulp.task('sprites:clean', (done) => { +gulp.task('sprites:clean', done => { clean(`${IMG_DIST_PATH}spritesmith*,${CSS_DIST_PATH}spritesmith*}`, done); }); -gulp.task('sprites:checkCompiledDimensions', gulp.series('sprites:main', 'sprites:largeSprites', (done) => { +gulp.task('sprites:checkCompiledDimensions', gulp.series('sprites:main', 'sprites:largeSprites', done => { console.log('Verifiying that images do not exceed max dimensions'); // eslint-disable-line no-console let numberOfSheetsThatAreTooBig = 0; - let distSpritesheets = sync(`${IMG_DIST_PATH}*.png`); + const distSpritesheets = sync(`${IMG_DIST_PATH}*.png`); - each(distSpritesheets, (img) => { - let spriteSize = calculateImgDimensions(img); + each(distSpritesheets, img => { + const spriteSize = calculateImgDimensions(img); if (spriteSize > MAX_SPRITESHEET_SIZE) { - numberOfSheetsThatAreTooBig++; - let name = basename(img, '.png'); + numberOfSheetsThatAreTooBig += 1; + const name = basename(img, '.png'); console.error(`WARNING: ${name} might be too big - ${spriteSize} > ${MAX_SPRITESHEET_SIZE}`); // eslint-disable-line no-console } }); @@ -155,7 +153,8 @@ gulp.task('sprites:checkCompiledDimensions', gulp.series('sprites:main', 'sprite console.error( // eslint-disable-line no-console `${numberOfSheetsThatAreTooBig} sheets might too big for mobile Safari to be able to handle them, but there is a margin of error in these calculations so it is probably okay. Mention - this to an admin so they can test a staging site on mobile Safari after your PR is merged.`); + this to an admin so they can test a staging site on mobile Safari after your PR is merged.`, + ); } else { console.log('All images are within the correct dimensions'); // eslint-disable-line no-console } diff --git a/gulp/gulp-start.js b/gulp/gulp-start.js index cca07d433d..22132bfbda 100644 --- a/gulp/gulp-start.js +++ b/gulp/gulp-start.js @@ -1,9 +1,9 @@ import gulp from 'gulp'; import nodemon from 'gulp-nodemon'; -let pkg = require('../package.json'); +const pkg = require('../package.json'); -gulp.task('nodemon', (done) => { +gulp.task('nodemon', done => { nodemon({ script: pkg.main, ignore: [ diff --git a/gulp/gulp-tests.js b/gulp/gulp-tests.js index 7294a90574..00fa172bb9 100644 --- a/gulp/gulp-tests.js +++ b/gulp/gulp-tests.js @@ -1,60 +1,59 @@ +import mongoose from 'mongoose'; +import { exec } from 'child_process'; +import gulp from 'gulp'; +import os from 'os'; +import nconf from 'nconf'; import { pipe, -} from './taskHelper'; -import mongoose from 'mongoose'; -import { exec } from 'child_process'; -import gulp from 'gulp'; -import os from 'os'; -import nconf from 'nconf'; +} from './taskHelper'; // TODO rewrite -const TEST_SERVER_PORT = 3003; +const TEST_SERVER_PORT = 3003; let server; -const TEST_DB_URI = nconf.get('TEST_DB_URI'); +const TEST_DB_URI = nconf.get('TEST_DB_URI'); const SANITY_TEST_COMMAND = 'npm run test:sanity'; const COMMON_TEST_COMMAND = 'npm run test:common'; const CONTENT_TEST_COMMAND = 'npm run test:content'; -const CONTENT_OPTIONS = {maxBuffer: 1024 * 500}; +const CONTENT_OPTIONS = { maxBuffer: 1024 * 500 }; /* Helper methods for reporting test summary */ -let testResults = []; -let testCount = (stdout, regexp) => { - let match = stdout.match(regexp); - return parseInt(match && match[1] || 0, 10); +const testResults = []; +const testCount = (stdout, regexp) => { + const match = stdout.match(regexp); + return parseInt(match && (match[1] || 0), 10); }; -let testBin = (string, additionalEnvVariables = '') => { +const testBin = (string, additionalEnvVariables = '') => { if (os.platform() === 'win32') { if (additionalEnvVariables !== '') { additionalEnvVariables = additionalEnvVariables.split(' ').join('&&set '); additionalEnvVariables = `set ${additionalEnvVariables}&&`; } return `set NODE_ENV=test&&${additionalEnvVariables}${string}`; - } else { - return `NODE_ENV=test ${additionalEnvVariables} ${string}`; } + return `NODE_ENV=test ${additionalEnvVariables} ${string}`; }; -gulp.task('test:nodemon', gulp.series(function setupNodemon (done) { +gulp.task('test:nodemon', gulp.series(done => { process.env.PORT = TEST_SERVER_PORT; // eslint-disable-line no-process-env process.env.NODE_DB_URI = TEST_DB_URI; // eslint-disable-line no-process-env done(); }, 'nodemon')); -gulp.task('test:prepare:mongo', (cb) => { - mongoose.connect(TEST_DB_URI, (err) => { +gulp.task('test:prepare:mongo', cb => { + mongoose.connect(TEST_DB_URI, err => { if (err) return cb(`Unable to connect to mongo database. Are you sure it's running? \n\n${err}`); - mongoose.connection.dropDatabase((err2) => { + mongoose.connection.dropDatabase(err2 => { if (err2) return cb(err2); mongoose.connection.close(cb); }); }); }); -gulp.task('test:prepare:server', gulp.series('test:prepare:mongo', (done) => { +gulp.task('test:prepare:server', gulp.series('test:prepare:mongo', done => { if (!server) { server = exec(testBin('node ./website/server/index.js', `NODE_DB_URI=${TEST_DB_URI} PORT=${TEST_SERVER_PORT}`), (error, stdout, stderr) => { if (error) { @@ -73,45 +72,43 @@ gulp.task('test:prepare:build', gulp.series('build', done => done())); gulp.task('test:prepare', gulp.series( 'test:prepare:build', 'test:prepare:mongo', - done => done() + done => done(), )); -gulp.task('test:sanity', (cb) => { - let runner = exec( +gulp.task('test:sanity', cb => { + const runner = exec( testBin(SANITY_TEST_COMMAND), - (err) => { + err => { if (err) { process.exit(1); } cb(); - } + }, ); pipe(runner); }); -gulp.task('test:common', gulp.series('test:prepare:build', (cb) => { - let runner = exec( +gulp.task('test:common', gulp.series('test:prepare:build', cb => { + const runner = exec( testBin(COMMON_TEST_COMMAND), - (err) => { + err => { if (err) { process.exit(1); } cb(); - } + }, ); pipe(runner); })); -gulp.task('test:common:clean', (cb) => { +gulp.task('test:common:clean', cb => { pipe(exec(testBin(COMMON_TEST_COMMAND), () => cb())); }); -gulp.task('test:common:watch', gulp.series('test:common:clean', () => { - return gulp.watch(['common/script/**/*', 'test/common/**/*'], gulp.series('test:common:clean', done => done())); -})); +gulp.task('test:common:watch', gulp.series('test:common:clean', () => gulp.watch(['common/script/**/*', 'test/common/**/*'], gulp.series('test:common:clean', done => done())))); -gulp.task('test:common:safe', gulp.series('test:prepare:build', (cb) => { - let runner = exec( +gulp.task('test:common:safe', gulp.series('test:prepare:build', cb => { + const runner = exec( testBin(COMMON_TEST_COMMAND), (err, stdout) => { // eslint-disable-line handle-callback-err testResults.push({ @@ -121,38 +118,36 @@ gulp.task('test:common:safe', gulp.series('test:prepare:build', (cb) => { pend: testCount(stdout, /(\d+) pending/), }); cb(); - } + }, ); pipe(runner); })); -gulp.task('test:content', gulp.series('test:prepare:build', (cb) => { - let runner = exec( +gulp.task('test:content', gulp.series('test:prepare:build', cb => { + const runner = exec( testBin(CONTENT_TEST_COMMAND), CONTENT_OPTIONS, - (err) => { + err => { if (err) { process.exit(1); } cb(); - } + }, ); pipe(runner); })); -gulp.task('test:content:clean', (cb) => { +gulp.task('test:content:clean', cb => { pipe(exec(testBin(CONTENT_TEST_COMMAND), CONTENT_OPTIONS, () => cb())); }); -gulp.task('test:content:watch', gulp.series('test:content:clean', () => { - return gulp.watch(['common/script/content/**', 'test/**'], gulp.series('test:content:clean', done => done())); -})); +gulp.task('test:content:watch', gulp.series('test:content:clean', () => gulp.watch(['common/script/content/**', 'test/**'], gulp.series('test:content:clean', done => done())))); -gulp.task('test:content:safe', gulp.series('test:prepare:build', (cb) => { - let runner = exec( +gulp.task('test:content:safe', gulp.series('test:prepare:build', cb => { + const runner = exec( testBin(CONTENT_TEST_COMMAND), CONTENT_OPTIONS, - (err, stdout) => { // eslint-disable-line handle-callback-err + (err, stdout) => { // eslint-disable-line handle-callback-err testResults.push({ suite: 'Content Specs\t', pass: testCount(stdout, /(\d+) passing/), @@ -160,81 +155,77 @@ gulp.task('test:content:safe', gulp.series('test:prepare:build', (cb) => { pend: testCount(stdout, /(\d+) pending/), }); cb(); - } + }, ); pipe(runner); })); -gulp.task('test:api:unit', (done) => { - let runner = exec( +gulp.task('test:api:unit', done => { + const runner = exec( testBin('istanbul cover --dir coverage/api-unit node_modules/mocha/bin/_mocha -- test/api/unit --recursive --require ./test/helpers/start-server'), - (err) => { + err => { if (err) { process.exit(1); } done(); - } + }, ); pipe(runner); }); -gulp.task('test:api:unit:watch', () => { - return gulp.watch(['website/server/libs/*', 'test/api/unit/**/*', 'website/server/controllers/**/*'], gulp.series('test:api:unit', done => done())); -}); +gulp.task('test:api:unit:watch', () => gulp.watch(['website/server/libs/*', 'test/api/unit/**/*', 'website/server/controllers/**/*'], gulp.series('test:api:unit', done => done()))); -gulp.task('test:api-v3:integration', (done) => { - let runner = exec( +gulp.task('test:api-v3:integration', done => { + const runner = exec( testBin('istanbul cover --dir coverage/api-v3-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/integration --recursive --require ./test/helpers/start-server'), - {maxBuffer: 500 * 1024}, - (err) => { + { maxBuffer: 500 * 1024 }, + err => { if (err) { process.exit(1); } done(); - } + }, ); pipe(runner); }); -gulp.task('test:api-v3:integration:watch', () => { - return gulp.watch([ - 'website/server/controllers/api-v3/**/*', 'common/script/ops/*', 'website/server/libs/*.js', - 'test/api/v3/integration/**/*', - ], gulp.series('test:api-v3:integration', done => done())); -}); +gulp.task('test:api-v3:integration:watch', () => gulp.watch([ + 'website/server/controllers/api-v3/**/*', 'common/script/ops/*', 'website/server/libs/*.js', + 'test/api/v3/integration/**/*', +], gulp.series('test:api-v3:integration', done => done()))); -gulp.task('test:api-v3:integration:separate-server', (done) => { - let runner = exec( +gulp.task('test:api-v3:integration:separate-server', done => { + const runner = exec( testBin('mocha test/api/v3/integration --recursive --require ./test/helpers/start-server', 'LOAD_SERVER=0'), - {maxBuffer: 500 * 1024}, - (err) => done(err) + { maxBuffer: 500 * 1024 }, + err => done(err), ); pipe(runner); }); -gulp.task('test:api-v4:integration', (done) => { - let runner = exec( +gulp.task('test:api-v4:integration', done => { + const runner = exec( testBin('istanbul cover --dir coverage/api-v4-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v4 --recursive --require ./test/helpers/start-server'), - {maxBuffer: 500 * 1024}, - (err) => { + { maxBuffer: 500 * 1024 }, + err => { if (err) { process.exit(1); } done(); - } + }, ); pipe(runner); }); -gulp.task('test:api-v4:integration:separate-server', (done) => { - let runner = exec( +gulp.task('test:api-v4:integration:separate-server', done => { + const runner = exec( testBin('mocha test/api/v4 --recursive --require ./test/helpers/start-server', 'LOAD_SERVER=0'), - {maxBuffer: 500 * 1024}, - (err) => done(err) + { maxBuffer: 500 * 1024 }, + err => done(err), ); pipe(runner); @@ -247,11 +238,11 @@ gulp.task('test', gulp.series( 'test:api:unit', 'test:api-v3:integration', 'test:api-v4:integration', - done => done() + done => done(), )); gulp.task('test:api-v3', gulp.series( 'test:api:unit', 'test:api-v3:integration', - done => done() + done => done(), )); diff --git a/gulp/gulp-transifex-test.js b/gulp/gulp-transifex-test.js index 2403707ecf..b90f9d441e 100644 --- a/gulp/gulp-transifex-test.js +++ b/gulp/gulp-transifex-test.js @@ -1,6 +1,6 @@ -import fs from 'fs'; -import _ from 'lodash'; -import gulp from 'gulp'; +import fs from 'fs'; +import _ from 'lodash'; +import gulp from 'gulp'; import { postToSlack, conf } from './taskHelper'; const SLACK_CONFIG = { @@ -14,7 +14,7 @@ const ENGLISH_LOCALE = `${LOCALES}en/`; function getArrayOfLanguages () { - let languages = fs.readdirSync(LOCALES); + const languages = fs.readdirSync(LOCALES); languages.shift(); // Remove README.md from array of languages return languages; @@ -23,18 +23,16 @@ function getArrayOfLanguages () { const ALL_LANGUAGES = getArrayOfLanguages(); function stripOutNonJsonFiles (collection) { - let onlyJson = _.filter(collection, (file) => { - return file.match(/[a-zA-Z]*\.json/); - }); + const onlyJson = _.filter(collection, file => file.match(/[a-zA-Z]*\.json/)); return onlyJson; } function eachTranslationFile (languages, cb) { - let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE)); + const jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE)); - _.each(languages, (lang) => { - _.each(jsonFiles, (filename) => { + _.each(languages, lang => { + _.each(jsonFiles, filename => { let parsedTranslationFile; try { const translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`); @@ -43,8 +41,8 @@ function eachTranslationFile (languages, cb) { return cb(err); } - let englishFile = fs.readFileSync(ENGLISH_LOCALE + filename); - let parsedEnglishFile = JSON.parse(englishFile); + const englishFile = fs.readFileSync(ENGLISH_LOCALE + filename); + const parsedEnglishFile = JSON.parse(englishFile); cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile); }); @@ -71,9 +69,9 @@ function formatMessageForPosting (msg, items) { } function getStringsWith (json, interpolationRegex) { - let strings = {}; + const strings = {}; - _.each(json, (fileName) => { + _.each(json, fileName => { const rawFile = fs.readFileSync(ENGLISH_LOCALE + fileName); const parsedJson = JSON.parse(rawFile); @@ -93,66 +91,69 @@ const malformedStringExceptions = { feedPet: true, }; -gulp.task('transifex:missingFiles', (done) => { - let missingStrings = []; +gulp.task('transifex:missingFiles', done => { + const missingStrings = []; - eachTranslationFile(ALL_LANGUAGES, (error) => { + eachTranslationFile(ALL_LANGUAGES, error => { if (error) { missingStrings.push(error.path); } }); if (!_.isEmpty(missingStrings)) { - let message = 'the following files were missing from the translations folder'; - let formattedMessage = formatMessageForPosting(message, missingStrings); + const message = 'the following files were missing from the translations folder'; + const formattedMessage = formatMessageForPosting(message, missingStrings); postToSlack(formattedMessage, SLACK_CONFIG); } done(); }); -gulp.task('transifex:missingStrings', (done) => { - let missingStrings = []; +gulp.task('transifex:missingStrings', done => { + const missingStrings = []; - eachTranslationString(ALL_LANGUAGES, (language, filename, key, englishString, translationString) => { + eachTranslationString(ALL_LANGUAGES, (lang, filename, key, englishString, translationString) => { if (!translationString) { - let errorString = `${language} - ${filename} - ${key} - ${englishString}`; + const errorString = `${lang} - ${filename} - ${key} - ${englishString}`; missingStrings.push(errorString); } }); if (!_.isEmpty(missingStrings)) { - let message = 'The following strings are not translated'; - let formattedMessage = formatMessageForPosting(message, missingStrings); + const message = 'The following strings are not translated'; + const formattedMessage = formatMessageForPosting(message, missingStrings); postToSlack(formattedMessage, SLACK_CONFIG); } done(); }); -gulp.task('transifex:malformedStrings', (done) => { - let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE)); - let interpolationRegex = /<%= [a-zA-Z]* %>/g; - let stringsToLookFor = getStringsWith(jsonFiles, interpolationRegex); +gulp.task('transifex:malformedStrings', done => { + const jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE)); + const interpolationRegex = /<%= [a-zA-Z]* %>/g; + const stringsToLookFor = getStringsWith(jsonFiles, interpolationRegex); - let stringsWithMalformedInterpolations = []; - let stringsWithIncorrectNumberOfInterpolations = []; + const stringsWithMalformedInterpolations = []; + const stringsWithIncorrectNumberOfInterpolations = []; - _.each(ALL_LANGUAGES, (lang) => { + _.each(ALL_LANGUAGES, lang => { _.each(stringsToLookFor, (strings, filename) => { - let translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`); - let parsedTranslationFile = JSON.parse(translationFile); + const translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`); + const parsedTranslationFile = JSON.parse(translationFile); _.each(strings, (value, key) => { // eslint-disable-line max-nested-callbacks - let translationString = parsedTranslationFile[key]; + const translationString = parsedTranslationFile[key]; if (!translationString) return; - let englishOccurences = stringsToLookFor[filename][key]; - let translationOccurences = translationString.match(interpolationRegex); + const englishOccurences = stringsToLookFor[filename][key]; + const translationOccurences = translationString.match(interpolationRegex); if (!translationOccurences) { - let malformedString = `${lang} - ${filename} - ${key} - ${translationString}`; + const malformedString = `${lang} - ${filename} - ${key} - ${translationString}`; stringsWithMalformedInterpolations.push(malformedString); - } else if (englishOccurences.length !== translationOccurences.length && !malformedStringExceptions[key]) { - let missingInterpolationString = `${lang} - ${filename} - ${key} - ${translationString}`; + } else if ( + englishOccurences.length !== translationOccurences.length + && !malformedStringExceptions[key] + ) { + const missingInterpolationString = `${lang} - ${filename} - ${key} - ${translationString}`; stringsWithIncorrectNumberOfInterpolations.push(missingInterpolationString); } }); @@ -160,14 +161,17 @@ gulp.task('transifex:malformedStrings', (done) => { }); if (!_.isEmpty(stringsWithMalformedInterpolations)) { - let message = 'The following strings have malformed or missing interpolations'; - let formattedMessage = formatMessageForPosting(message, stringsWithMalformedInterpolations); + const message = 'The following strings have malformed or missing interpolations'; + const formattedMessage = formatMessageForPosting(message, stringsWithMalformedInterpolations); postToSlack(formattedMessage, SLACK_CONFIG); } if (!_.isEmpty(stringsWithIncorrectNumberOfInterpolations)) { - let message = 'The following strings have a different number of string interpolations'; - let formattedMessage = formatMessageForPosting(message, stringsWithIncorrectNumberOfInterpolations); + const message = 'The following strings have a different number of string interpolations'; + const formattedMessage = formatMessageForPosting( + message, + stringsWithIncorrectNumberOfInterpolations, + ); postToSlack(formattedMessage, SLACK_CONFIG); } done(); @@ -176,5 +180,5 @@ gulp.task('transifex:malformedStrings', (done) => { gulp.task( 'transifex', gulp.series('transifex:missingFiles', 'transifex:missingStrings', 'transifex:malformedStrings'), - (done) => done() -); \ No newline at end of file + done => done(), +); diff --git a/gulp/taskHelper.js b/gulp/taskHelper.js index 270573ba70..b22897747e 100644 --- a/gulp/taskHelper.js +++ b/gulp/taskHelper.js @@ -1,11 +1,11 @@ -import { exec } from 'child_process'; -import psTree from 'ps-tree'; -import nconf from 'nconf'; -import net from 'net'; -import { post } from 'superagent'; -import { sync as glob } from 'glob'; -import Mocha from 'mocha'; -import { resolve } from 'path'; +import { exec } from 'child_process'; +import psTree from 'ps-tree'; +import nconf from 'nconf'; +import net from 'net'; +import { post } from 'superagent'; +import { sync as glob } from 'glob'; +import Mocha from 'mocha'; // eslint-disable-line import/no-extraneous-dependencies +import { resolve } from 'path'; /* * Get access to configruable values @@ -19,15 +19,15 @@ export const conf = nconf; * its tasks. */ export function kill (proc) { - let killProcess = (pid) => { + const killProcess = pid => { psTree(pid, (_, pids) => { if (pids.length) { pids.forEach(kill); return; } try { - exec(/^win/.test(process.platform) ? - `taskkill /PID ${pid} /T /F` : - `kill -9 ${pid}`); + exec(/^win/.test(process.platform) + ? `taskkill /PID ${pid} /T /F` + : `kill -9 ${pid}`); } catch (e) { console.log(e); // eslint-disable-line no-console } @@ -46,16 +46,15 @@ export function kill (proc) { export function awaitPort (port, max = 60) { return new Promise((rej, res) => { let socket; - let timeout; let interval; - timeout = setTimeout(() => { + const timeout = setTimeout(() => { clearInterval(interval); rej(`Timed out after ${max} seconds`); }, max * 1000); interval = setInterval(() => { - socket = net.connect({port}, () => { + socket = net.connect({ port }, () => { clearInterval(interval); clearTimeout(timeout); socket.destroy(); @@ -71,10 +70,10 @@ export function awaitPort (port, max = 60) { * Pipe the child's stdin and stderr to the parent process. */ export function pipe (child) { - child.stdout.on('data', (data) => { + child.stdout.on('data', data => { process.stdout.write(data); }); - child.stderr.on('data', (data) => { + child.stderr.on('data', data => { process.stderr.write(data); }); } @@ -83,7 +82,7 @@ export function pipe (child) { * Post request to notify configured slack channel */ export function postToSlack (msg, config = {}) { - let slackUrl = nconf.get('SLACK_URL'); + const slackUrl = nconf.get('SLACK_URL'); if (!slackUrl) { console.error('No slack post url specified. Your message was:'); // eslint-disable-line no-console @@ -99,7 +98,7 @@ export function postToSlack (msg, config = {}) { text: msg, icon_emoji: `:${config.emoji || 'gulp'}:`, // eslint-disable-line camelcase }) - .end((err) => { + .end(err => { if (err) console.error('Unable to post to slack', err); // eslint-disable-line no-console }); } @@ -107,15 +106,15 @@ export function postToSlack (msg, config = {}) { export function runMochaTests (files, server, cb) { require('../test/helpers/globals.helper'); // eslint-disable-line global-require - let mocha = new Mocha({reporter: 'spec'}); - let tests = glob(files); + const mocha = new Mocha({ reporter: 'spec' }); + const tests = glob(files); - tests.forEach((test) => { + tests.forEach(test => { delete require.cache[resolve(test)]; mocha.addFile(test); }); - mocha.run((numberOfFailures) => { + mocha.run(numberOfFailures => { if (!process.env.RUN_INTEGRATION_TEST_FOREVER) { // eslint-disable-line no-process-env if (server) kill(server); process.exit(numberOfFailures); diff --git a/migrations/challenges/sync-all-challenges.js b/migrations/challenges/sync-all-challenges.js index 7998aecdd1..519865e267 100644 --- a/migrations/challenges/sync-all-challenges.js +++ b/migrations/challenges/sync-all-challenges.js @@ -2,14 +2,14 @@ import { model as Challenges } from '../../website/server/models/challenge'; import { model as User } from '../../website/server/models/user'; async function syncChallengeToMembers (challenges) { - let challengSyncPromises = challenges.map(async (challenge) => { - let users = await User.find({ + const challengSyncPromises = challenges.map(async challenge => { + const users = await User.find({ // _id: '', challenges: challenge._id, }).exec(); - let promises = []; - users.forEach((user) => { + const promises = []; + users.forEach(user => { promises.push(challenge.syncToUser(user)); promises.push(challenge.save()); promises.push(user.save()); @@ -22,7 +22,7 @@ async function syncChallengeToMembers (challenges) { } async function syncChallenges (lastChallengeDate) { - let query = { + const query = { // _id: '', }; @@ -30,14 +30,14 @@ async function syncChallenges (lastChallengeDate) { query.createdOn = { $lte: lastChallengeDate }; } - let challengesFound = await Challenges.find(query) + const challengesFound = await Challenges.find(query) .limit(10) .sort('-createdAt') .exec(); - let syncedChallenges = await syncChallengeToMembers(challengesFound) + const syncedChallenges = await syncChallengeToMembers(challengesFound) .catch(reason => console.error(reason)); - let lastChallenge = challengesFound[challengesFound.length - 1]; + const lastChallenge = challengesFound[challengesFound.length - 1]; if (lastChallenge) syncChallenges(lastChallenge.createdAt); return syncedChallenges; } diff --git a/migrations/command-line/apology_gems.js b/migrations/command-line/apology_gems.js index 347a63d141..f56b8e6323 100644 --- a/migrations/command-line/apology_gems.js +++ b/migrations/command-line/apology_gems.js @@ -1 +1 @@ -db.users.update({_id: {$in: ['']}}, {$inc: {balance: 0.5}}, {multi: true}); \ No newline at end of file +db.users.update({ _id: { $in: [''] } }, { $inc: { balance: 0.5 } }, { multi: true }); diff --git a/migrations/command-line/cancelSubscription.js b/migrations/command-line/cancelSubscription.js index f823bed40f..682568239e 100644 --- a/migrations/command-line/cancelSubscription.js +++ b/migrations/command-line/cancelSubscription.js @@ -4,8 +4,10 @@ // the FAQ (http://goo.gl/1uoPGQ) they insist... db.users.update( - {_id: ''}, - {$set: { - 'purchased.plan.dateTerminated': moment().add('month', 1).toDate(), - }} -); \ No newline at end of file + { _id: '' }, + { + $set: { + 'purchased.plan.dateTerminated': moment().add('month', 1).toDate(), + }, + }, +); diff --git a/migrations/command-line/contribs_plan.js b/migrations/command-line/contribs_plan.js index 548ff6f18f..a08b8acc0b 100644 --- a/migrations/command-line/contribs_plan.js +++ b/migrations/command-line/contribs_plan.js @@ -2,7 +2,7 @@ db.users.update( { - 'contributor.level': {$gte: 7}, + 'contributor.level': { $gte: 7 }, 'purchased.plan.customerId': null, }, @@ -18,6 +18,6 @@ db.users.update( }, }, - {multi: true} + { multi: true }, -); \ No newline at end of file +); diff --git a/migrations/command-line/current_period_end.js b/migrations/command-line/current_period_end.js index 56c9e8b45d..d91ff5a250 100644 --- a/migrations/command-line/current_period_end.js +++ b/migrations/command-line/current_period_end.js @@ -1,5 +1,5 @@ // mongo habitrpg ./node_modules/moment/moment.js ./migrations/current_period_end.js db.users.update( - {_id: ''}, - {$set: {'purchased.plan.dateTerminated': moment().add({days: 7}).toDate()}} -); \ No newline at end of file + { _id: '' }, + { $set: { 'purchased.plan.dateTerminated': moment().add({ days: 7 }).toDate() } }, +); diff --git a/migrations/command-line/duplicatedTasksFindAndRemove.js b/migrations/command-line/duplicatedTasksFindAndRemove.js index 2dcdb10e5c..6fa6d3d1fb 100644 --- a/migrations/command-line/duplicatedTasksFindAndRemove.js +++ b/migrations/command-line/duplicatedTasksFindAndRemove.js @@ -39,38 +39,52 @@ // needed. Do not miss any of them! -let uuid = '30fb2640-7121-4968-ace5-f385e60ea6c5'; +const uuid = '30fb2640-7121-4968-ace5-f385e60ea6c5'; db.users.aggregate([ - {$match: { - _id: uuid, - }}, - {$project: { - _id: 0, todos: 1, - }}, - {$unwind: '$todos'}, - {$group: { - _id: { taskid: '$todos.id' }, - count: { $sum: 1 }, - }}, - {$match: { - count: { $gt: 1 }, - }}, - {$project: { - '_id.taskid': 1, - }}, - {$group: { - _id: { taskid: '$todos.id' }, - troublesomeIds: { $addToSet: '$_id.taskid' }, - }}, - {$project: { - _id: 0, - troublesomeIds: 1, - }}, -]).forEach((data) => { + { + $match: { + _id: uuid, + }, + }, + { + $project: { + _id: 0, todos: 1, + }, + }, + { $unwind: '$todos' }, + { + $group: { + _id: { taskid: '$todos.id' }, + count: { $sum: 1 }, + }, + }, + { + $match: { + count: { $gt: 1 }, + }, + }, + { + $project: { + '_id.taskid': 1, + }, + }, + { + $group: { + _id: { taskid: '$todos.id' }, + troublesomeIds: { $addToSet: '$_id.taskid' }, + }, + }, + { + $project: { + _id: 0, + troublesomeIds: 1, + }, + }, +]).forEach(data => { // print( "\n" ); printjson(data); - data.troublesomeIds.forEach((taskid) => { - print(`non-unique task: ${ taskid}`); + data.troublesomeIds.forEach(taskid => { + print(`non-unique task: ${taskid}`); db.users.update({ _id: uuid, todos: { $elemMatch: { id: taskid } }, @@ -81,8 +95,7 @@ db.users.aggregate([ }); db.users.update( - {_id: uuid}, - {$pull: { todos: { id: 'de666' } } }, - {multi: false } + { _id: uuid }, + { $pull: { todos: { id: 'de666' } } }, + { multi: false }, ); - diff --git a/migrations/command-line/facebook_to_local.js b/migrations/command-line/facebook_to_local.js index de707c4f05..04fd254965 100644 --- a/migrations/command-line/facebook_to_local.js +++ b/migrations/command-line/facebook_to_local.js @@ -1,10 +1,9 @@ -let oldId = ''; -let newId = ''; -let newUser = db.users.findOne({_id: newId}); +const oldId = ''; +const newId = ''; +const newUser = db.users.findOne({ _id: newId }); -db.users.update({_id: oldId}, {$set: {auth: newUser.auth}}); +db.users.update({ _id: oldId }, { $set: { auth: newUser.auth } }); // remove the auth on the new user (which is a template account). The account will be preened automatically later, // this allows us to keep the account around a few days in case there was a mistake -db.users.update({_id: newId}, {$unset: {auth: 1}}); - +db.users.update({ _id: newId }, { $unset: { auth: 1 } }); diff --git a/migrations/command-line/find_unique_user.js b/migrations/command-line/find_unique_user.js index 378c6c8708..bb3e590df0 100644 --- a/migrations/command-line/find_unique_user.js +++ b/migrations/command-line/find_unique_user.js @@ -5,8 +5,8 @@ * Past in the text of a unique habit here to find the user, then you can restore their UUID */ -db.users.find().forEach((user) => { +db.users.find().forEach(user => { user.tasks = user.habits.concat(user.dailys).concat(user.todos).concat(user.rewards); - let found = _.some(user.tasks, {text: ''}); - if (found) printjson({id: user._id, auth: user.auth}); -}); \ No newline at end of file + const found = _.some(user.tasks, { text: '' }); + if (found) printjson({ id: user._id, auth: user.auth }); +}); diff --git a/migrations/command-line/freeMonth.js b/migrations/command-line/freeMonth.js index 2b9b19cccd..377e28b726 100644 --- a/migrations/command-line/freeMonth.js +++ b/migrations/command-line/freeMonth.js @@ -1,13 +1,15 @@ // mongo habitrpg ./node_modules/moment/moment.js ./migrations/freeMonth.js db.users.update( - {_id: ''}, - {$set: { - 'purchased.plan.customerId': 'temporary', - 'purchased.plan.paymentMethod': 'Stripe', - 'purchased.plan.planId': 'basic_earned', - 'purchased.plan.dateTerminated': moment().add('month', 1).toDate(), - }} + { _id: '' }, + { + $set: { + 'purchased.plan.customerId': 'temporary', + 'purchased.plan.paymentMethod': 'Stripe', + 'purchased.plan.planId': 'basic_earned', + 'purchased.plan.dateTerminated': moment().add('month', 1).toDate(), + }, + }, ); // var m = 12; // db.users.update( @@ -29,4 +31,4 @@ db.users.update( // trinkets: m/3 // } // }}} -// ) \ No newline at end of file +// ) diff --git a/migrations/command-line/habitica_day.js b/migrations/command-line/habitica_day.js index 9e4fd3af5b..61787298e1 100644 --- a/migrations/command-line/habitica_day.js +++ b/migrations/command-line/habitica_day.js @@ -1,5 +1,5 @@ db.users.update( {}, - {$inc: {'achievements.habiticaDays': 1}}, - {multi: 1} + { $inc: { 'achievements.habiticaDays': 1 } }, + { multi: 1 }, ); diff --git a/migrations/command-line/missing_gems.js b/migrations/command-line/missing_gems.js index 080bfbd687..fad6c750dd 100644 --- a/migrations/command-line/missing_gems.js +++ b/migrations/command-line/missing_gems.js @@ -1 +1 @@ -db.users.update({_id: ''}, {$inc: {balance: 5}}); \ No newline at end of file +db.users.update({ _id: '' }, { $inc: { balance: 5 } }); diff --git a/migrations/groups/add-unlimited-subscription.js b/migrations/groups/add-unlimited-subscription.js index 43fbe55b5d..2b2d99ecac 100644 --- a/migrations/groups/add-unlimited-subscription.js +++ b/migrations/groups/add-unlimited-subscription.js @@ -13,7 +13,7 @@ import { model as Group } from '../../website/server/models/group'; // @TODO: this should probably be a GroupManager library method async function addUnlimitedSubscription (groupId, dateTerminated) { - let group = await Group.findOne({_id: groupId}); + const group = await Group.findOne({ _id: groupId }); group.purchased.plan.customerId = 'group-unlimited'; group.purchased.plan.dateCreated = new Date(); @@ -22,7 +22,7 @@ async function addUnlimitedSubscription (groupId, dateTerminated) { group.purchased.plan.planId = 'group_monthly'; group.purchased.plan.dateTerminated = null; if (dateTerminated) { - let dateToEnd = moment(dateTerminated).toDate(); + const dateToEnd = moment(dateTerminated).toDate(); group.purchased.plan.dateTerminated = dateToEnd; } // group.purchased.plan.owner = ObjectId(); @@ -32,11 +32,11 @@ async function addUnlimitedSubscription (groupId, dateTerminated) { } module.exports = async function addUnlimitedSubscriptionCreator () { - let groupId = process.argv[2]; + const groupId = process.argv[2]; if (!groupId) throw Error('Group ID is required'); - let dateTerminated = process.argv[3]; + const dateTerminated = process.argv[3]; await addUnlimitedSubscription(groupId, dateTerminated); }; diff --git a/migrations/groups/create-group.js b/migrations/groups/create-group.js index b1d266890f..e81c4af38e 100644 --- a/migrations/groups/create-group.js +++ b/migrations/groups/create-group.js @@ -3,9 +3,9 @@ import { model as User } from '../../website/server/models/user'; // @TODO: this should probably be a GroupManager library method async function createGroup (name, privacy, type, leaderId) { - let user = await User.findOne({_id: leaderId}); + const user = await User.findOne({ _id: leaderId }); - let group = new Group({ + const group = new Group({ name, privacy, type, @@ -18,12 +18,10 @@ async function createGroup (name, privacy, type, leaderId) { } module.exports = async function groupCreator () { - let name = process.argv[2]; - let privacy = process.argv[3]; - let type = process.argv[4]; - let leaderId = process.argv[5]; + const name = process.argv[2]; + const privacy = process.argv[3]; + const type = process.argv[4]; + const leaderId = process.argv[5]; await createGroup(name, privacy, type, leaderId); }; - - diff --git a/migrations/groups/habitrpg-jackalopes.js b/migrations/groups/habitrpg-jackalopes.js index a33ae3a2f3..2eb10a7e93 100644 --- a/migrations/groups/habitrpg-jackalopes.js +++ b/migrations/groups/habitrpg-jackalopes.js @@ -8,29 +8,27 @@ import { model as Group } from '../../website/server/models/group'; import { model as User } from '../../website/server/models/user'; async function handOutJackalopes () { - let promises = []; - let cursor = User.find({ + const promises = []; + const cursor = User.find({ 'purchased.plan.customerId': 'habitrpg', }).cursor(); - cursor.on('data', async (user) => { - console.log(`User: ${ user._id}`); + cursor.on('data', async user => { + console.log(`User: ${user._id}`); let groupList = []; if (user.party._id) groupList.push(user.party._id); groupList = groupList.concat(user.guilds); - let subscribedGroup = - await Group.findOne({ - _id: {$in: groupList}, - 'purchased.plan.planId': 'group_monthly', - 'purchased.plan.dateTerminated': null, - }, - {_id: 1} - ); + const subscribedGroup = await Group.findOne({ + _id: { $in: groupList }, + 'purchased.plan.planId': 'group_monthly', + 'purchased.plan.dateTerminated': null, + }, + { _id: 1 }); if (subscribedGroup) { - User.update({_id: user._id}, {$set: {'items.mounts.Jackalope-RoyalPurple': true}}).exec(); + User.update({ _id: user._id }, { $set: { 'items.mounts.Jackalope-RoyalPurple': true } }).exec(); promises.push(user.save()); } }); diff --git a/migrations/groups/reconcile-group-plan-members.js b/migrations/groups/reconcile-group-plan-members.js index 06296090e4..05407ac16c 100644 --- a/migrations/groups/reconcile-group-plan-members.js +++ b/migrations/groups/reconcile-group-plan-members.js @@ -12,8 +12,8 @@ import stripePayments from '../../website/server/libs/payments/stripe'; const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING'); -let dbGroups = monk(CONNECTION_STRING).get('groups', { castIds: false }); -let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false }); +const dbGroups = monk(CONNECTION_STRING).get('groups', { castIds: false }); +const dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false }); async function fixGroupPlanMembers () { console.info('Group ID, Customer ID, Plan ID, Quantity, Recorded Member Count, Actual Member Count'); @@ -24,15 +24,15 @@ async function fixGroupPlanMembers () { { $and: [ - {'purchased.plan.planId': {$ne: null}}, - {'purchased.plan.planId': {$ne: ''}}, - {'purchased.plan.customerId': {$ne: 'cus_9f0DV4g7WHRzpM'}}, // Demo groups - {'purchased.plan.customerId': {$ne: 'cus_9maalqDOFTrvqx'}}, + { 'purchased.plan.planId': { $ne: null } }, + { 'purchased.plan.planId': { $ne: '' } }, + { 'purchased.plan.customerId': { $ne: 'cus_9f0DV4g7WHRzpM' } }, // Demo groups + { 'purchased.plan.customerId': { $ne: 'cus_9maalqDOFTrvqx' } }, ], $or: [ - {'purchased.plan.dateTerminated': null}, - {'purchased.plan.dateTerminated': ''}, + { 'purchased.plan.dateTerminated': null }, + { 'purchased.plan.dateTerminated': '' }, ], }, { @@ -40,8 +40,8 @@ async function fixGroupPlanMembers () { memberCount: 1, 'purchased.plan': 1, }, - } - ).each(async (group, {close, pause, resume}) => { // eslint-disable-line no-unused-vars + }, + ).each(async (group, { close, pause, resume }) => { // eslint-disable-line no-unused-vars pause(); groupPlanCount++; @@ -49,10 +49,10 @@ async function fixGroupPlanMembers () { { $or: [ - {'party._id': group._id}, - {guilds: group._id}, + { 'party._id': group._id }, + { guilds: group._id }, ], - } + }, ); const incorrectMemberCount = group.memberCount !== canonicalMemberCount; @@ -73,7 +73,7 @@ async function fixGroupPlanMembers () { $set: { memberCount: canonicalMemberCount, }, - } + }, ); if (!groupUpdate) return; @@ -82,15 +82,15 @@ async function fixGroupPlanMembers () { if (group.purchased.plan.paymentMethod === 'Stripe') { await stripePayments.chargeForAdditionalGroupMember(group); await dbGroups.update( - {_id: group._id}, - {$set: {'purchased.plan.quantity': canonicalMemberCount + 2}} + { _id: group._id }, + { $set: { 'purchased.plan.quantity': canonicalMemberCount + 2 } }, ); } if (incorrectQuantity) { await dbGroups.update( - {_id: group._id}, - {$set: {'purchased.plan.quantity': canonicalMemberCount + 2}} + { _id: group._id }, + { $set: { 'purchased.plan.quantity': canonicalMemberCount + 2 } }, ); } @@ -98,7 +98,7 @@ async function fixGroupPlanMembers () { }).then(() => { console.info(`Fixed ${fixedGroupCount} out of ${groupPlanCount} active Group Plans`); return process.exit(0); - }).catch((err) => { + }).catch(err => { console.log(err); return process.exit(1); }); diff --git a/migrations/groups/update-groups-with-group-plans.js b/migrations/groups/update-groups-with-group-plans.js index d544f389ab..ac369609fa 100644 --- a/migrations/groups/update-groups-with-group-plans.js +++ b/migrations/groups/update-groups-with-group-plans.js @@ -13,21 +13,19 @@ import { model as Group } from '../../website/server/models/group'; import * as payments from '../../website/server/libs/payments'; async function updateGroupsWithGroupPlans () { - let cursor = Group.find({ + const cursor = Group.find({ 'purchased.plan.planId': 'group_monthly', 'purchased.plan.dateTerminated': null, }).cursor(); - let promises = []; + const promises = []; - cursor.on('data', (group) => { + cursor.on('data', group => { promises.push(payments.addSubscriptionToGroupUsers(group)); promises.push(group.save()); }); - cursor.on('close', async () => { - return await Promise.all(promises); - }); + cursor.on('close', async () => await Promise.all(promises)); } module.exports = updateGroupsWithGroupPlans; diff --git a/migrations/migration-runner.js b/migrations/migration-runner.js index 39cec8b1aa..0b502c155b 100644 --- a/migrations/migration-runner.js +++ b/migrations/migration-runner.js @@ -18,11 +18,12 @@ setUpServer(); // Replace this with your migration const processUsers = require(''); + processUsers() - .then(function success () { + .then(() => { process.exit(0); }) - .catch(function failure (err) { + .catch(err => { console.log(err); process.exit(1); }); diff --git a/migrations/new_stuff.js b/migrations/new_stuff.js index 3ee124ab6b..b367500d87 100644 --- a/migrations/new_stuff.js +++ b/migrations/new_stuff.js @@ -1,19 +1,20 @@ /* let migrationName = 'new_stuff.js'; */ -let authorName = 'Sabe'; // in case script author needs to know when their ... -let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done +const authorName = 'Sabe'; // in case script author needs to know when their ... +const authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done /* * set the newStuff flag in all user accounts so they see a Bailey message */ -let monk = require('monk'); -let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -let dbUsers = monk(connectionString).get('users', { castIds: false }); +const monk = require('monk'); + +const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +const dbUsers = monk(connectionString).get('users', { castIds: false }); function processUsers (lastId) { // specify a query to limit the affected users (empty for all users): - let query = { - 'flags.newStuff': {$ne: true}, + const query = { + 'flags.newStuff': { $ne: true }, }; if (lastId) { @@ -23,18 +24,18 @@ function processUsers (lastId) { } dbUsers.find(query, { - sort: {_id: 1}, + sort: { _id: 1 }, limit: 250, fields: [], // specify fields we are interested in to limit retrieved data (empty if we're not reading data): }) .then(updateUsers) - .catch((err) => { + .catch(err => { console.log(err); - return exiting(1, `ERROR! ${ err}`); + return exiting(1, `ERROR! ${err}`); }); } -let progressCount = 1000; +const progressCount = 1000; let count = 0; function updateUsers (users) { @@ -44,8 +45,8 @@ function updateUsers (users) { return; } - let userPromises = users.map(updateUser); - let lastUser = users[users.length - 1]; + const userPromises = users.map(updateUser); + const lastUser = users[users.length - 1]; return Promise.all(userPromises) .then(() => { @@ -56,16 +57,16 @@ function updateUsers (users) { function updateUser (user) { count++; - let set = {'flags.newStuff': true}; + const set = { 'flags.newStuff': true }; - dbUsers.update({_id: user._id}, {$set: set}); + dbUsers.update({ _id: user._id }, { $set: set }); - if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); - if (user._id === authorUuid) console.warn(`${authorName } processed`); + if (count % progressCount === 0) console.warn(`${count} ${user._id}`); + if (user._id === authorUuid) console.warn(`${authorName} processed`); } function displayData () { - console.warn(`\n${ count } users processed\n`); + console.warn(`\n${count} users processed\n`); return exiting(0); } @@ -77,7 +78,7 @@ function exiting (code, msg) { if (msg) { if (code) { console.error(msg); - } else { + } else { console.log(msg); } } diff --git a/migrations/restock_armoire.js b/migrations/restock_armoire.js index 5046998a3b..7e6471118f 100644 --- a/migrations/restock_armoire.js +++ b/migrations/restock_armoire.js @@ -1,18 +1,19 @@ -let migrationName = 'restock_armoire.js'; -let authorName = 'Sabe'; // in case script author needs to know when their ... -let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done +const migrationName = 'restock_armoire.js'; +const authorName = 'Sabe'; // in case script author needs to know when their ... +const authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done /* * Remove flag stating that the Enchanted Armoire is empty, for when new equipment is added */ -let monk = require('monk'); -let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -let dbUsers = monk(connectionString).get('users', { castIds: false }); +const monk = require('monk'); + +const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +const dbUsers = monk(connectionString).get('users', { castIds: false }); function processUsers (lastId) { // specify a query to limit the affected users (empty for all users): - let query = { + const query = { 'flags.armoireEmpty': true, }; @@ -23,18 +24,18 @@ function processUsers (lastId) { } dbUsers.find(query, { - sort: {_id: 1}, + sort: { _id: 1 }, limit: 250, fields: [], // specify fields we are interested in to limit retrieved data (empty if we're not reading data): }) .then(updateUsers) - .catch((err) => { + .catch(err => { console.log(err); - return exiting(1, `ERROR! ${ err}`); + return exiting(1, `ERROR! ${err}`); }); } -let progressCount = 1000; +const progressCount = 1000; let count = 0; function updateUsers (users) { @@ -44,8 +45,8 @@ function updateUsers (users) { return; } - let userPromises = users.map(updateUser); - let lastUser = users[users.length - 1]; + const userPromises = users.map(updateUser); + const lastUser = users[users.length - 1]; return Promise.all(userPromises) .then(() => { @@ -56,16 +57,16 @@ function updateUsers (users) { function updateUser (user) { count++; - let set = {migration: migrationName, 'flags.armoireEmpty': false}; + const set = { migration: migrationName, 'flags.armoireEmpty': false }; - dbUsers.update({_id: user._id}, {$set: set}); + dbUsers.update({ _id: user._id }, { $set: set }); - if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); - if (user._id === authorUuid) console.warn(`${authorName } processed`); + if (count % progressCount === 0) console.warn(`${count} ${user._id}`); + if (user._id === authorUuid) console.warn(`${authorName} processed`); } function displayData () { - console.warn(`\n${ count } users processed\n`); + console.warn(`\n${count} users processed\n`); return exiting(0); } @@ -77,7 +78,7 @@ function exiting (code, msg) { if (msg) { if (code) { console.error(msg); - } else { + } else { console.log(msg); } } diff --git a/migrations/restock_armoire_for_users_that_need_it.js b/migrations/restock_armoire_for_users_that_need_it.js index 5bf0422d04..46dbeeeea1 100644 --- a/migrations/restock_armoire_for_users_that_need_it.js +++ b/migrations/restock_armoire_for_users_that_need_it.js @@ -1,6 +1,6 @@ -let migrationName = 'restock_armoire_for_users_that_need_it.js'; -let authorName = 'Alys (ALittleYellowSpider)'; // in case script author needs to know when their ... -let authorUuid = '3e595299-3d8a-4a10-bfe0-88f555e4aa0c'; // ... own data is done +const migrationName = 'restock_armoire_for_users_that_need_it.js'; +const authorName = 'Alys (ALittleYellowSpider)'; // in case script author needs to know when their ... +const authorUuid = '3e595299-3d8a-4a10-bfe0-88f555e4aa0c'; // ... own data is done /* * Remove flag stating that the Enchanted Armoire is empty, @@ -18,16 +18,17 @@ let authorUuid = '3e595299-3d8a-4a10-bfe0-88f555e4aa0c'; // ... own data is done * */ -let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -let monk = require('monk'); -let dbUsers = monk(connectionString).get('users', { castIds: false }); +const monk = require('monk'); + +const dbUsers = monk(connectionString).get('users', { castIds: false }); function processUsers (lastId) { // specify a query to limit the affected users (empty for all users): - let query = { - 'auth.timestamps.loggedin': {$gt: new Date('2016-01-04')}, + const query = { + 'auth.timestamps.loggedin': { $gt: new Date('2016-01-04') }, // '_id': authorUuid // FOR TESTING }; @@ -35,7 +36,7 @@ function processUsers (lastId) { /* let fields = { 'flags.armoireEmpty': 1, 'items.gear.owned': 1, - };*/ + }; */ if (lastId) { query._id = { @@ -44,7 +45,7 @@ function processUsers (lastId) { } dbUsers.find(query, { - sort: {_id: 1}, + sort: { _id: 1 }, limit: 250, fields: { 'flags.armoireEmpty': 1, @@ -52,13 +53,13 @@ function processUsers (lastId) { }, // specify fields we are interested in to limit retrieved data (empty if we're not reading data): }) .then(updateUsers) - .catch((err) => { + .catch(err => { console.log(err); - return exiting(1, `ERROR! ${ err}`); + return exiting(1, `ERROR! ${err}`); }); } -let progressCount = 1000; +const progressCount = 1000; let count = 0; function updateUsers (users) { @@ -68,8 +69,8 @@ function updateUsers (users) { return; } - let userPromises = users.map(updateUser); - let lastUser = users[users.length - 1]; + const userPromises = users.map(updateUser); + const lastUser = users[users.length - 1]; return Promise.all(userPromises) .then(() => { @@ -80,7 +81,7 @@ function updateUsers (users) { function updateUser (user) { count++; - let set = {migration: migrationName, 'flags.armoireEmpty': false}; + const set = { migration: migrationName, 'flags.armoireEmpty': false }; if (user.flags.armoireEmpty) { @@ -90,7 +91,7 @@ function updateUser (user) { // console.log("don't change: " + user._id); // FOR TESTING } else { // console.log("change: " + user._id); // FOR TESTING - dbUsers.update({_id: user._id}, {$set: set}); + dbUsers.update({ _id: user._id }, { $set: set }); } } else { // this user already has armoire marked as containing items to be bought @@ -98,12 +99,12 @@ function updateUser (user) { // console.log("DON'T CHANGE: " + user._id); // FOR TESTING } - if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); - if (user._id === authorUuid) console.warn(`${authorName } processed`); + if (count % progressCount === 0) console.warn(`${count} ${user._id}`); + if (user._id === authorUuid) console.warn(`${authorName} processed`); } function displayData () { - console.warn(`\n${ count } users processed\n`); + console.warn(`\n${count} users processed\n`); return exiting(0); } @@ -115,7 +116,7 @@ function exiting (code, msg) { if (msg) { if (code) { console.error(msg); - } else { + } else { console.log(msg); } } diff --git a/migrations/restore-profile-data.js b/migrations/restore-profile-data.js index 750a991efb..ae9254031b 100644 --- a/migrations/restore-profile-data.js +++ b/migrations/restore-profile-data.js @@ -1,22 +1,24 @@ /* let migrationName = 'restore_profile_data.js'; */ -let authorName = 'ThehollidayInn'; // in case script author needs to know when their ... -let authorUuid = ''; // ... own data is done +const authorName = 'ThehollidayInn'; // in case script author needs to know when their ... +const authorUuid = ''; // ... own data is done /* * Check if users have empty profile data in new database and update it with old database info */ -let monk = require('monk'); -let connectionString = ''; // FOR TEST DATABASE -let dbUsers = monk(connectionString).get('users', { castIds: false }); +const monk = require('monk'); -let monk2 = require('monk'); -let oldDbConnectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -let olDbUsers = monk2(oldDbConnectionString).get('users', { castIds: false }); +const connectionString = ''; // FOR TEST DATABASE +const dbUsers = monk(connectionString).get('users', { castIds: false }); + +const monk2 = require('monk'); + +const oldDbConnectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +const olDbUsers = monk2(oldDbConnectionString).get('users', { castIds: false }); function processUsers (lastId) { // specify a query to limit the affected users (empty for all users): - let query = { + const query = { // 'profile.name': 'profile name not found', 'profile.blurb': null, // 'auth.timestamps.loggedin': {$gt: new Date('11/30/2016')}, @@ -29,18 +31,18 @@ function processUsers (lastId) { } dbUsers.find(query, { - sort: {_id: 1}, + sort: { _id: 1 }, limit: 250, fields: ['_id', 'profile', 'auth.timestamps.loggedin'], // specify fields we are interested in to limit retrieved data (empty if we're not reading data): }) .then(updateUsers) - .catch((err) => { + .catch(err => { console.log(err); - return exiting(1, `ERROR! ${ err}`); + return exiting(1, `ERROR! ${err}`); }); } -let progressCount = 1000; +const progressCount = 1000; let count = 0; function updateUsers (users) { @@ -50,28 +52,26 @@ function updateUsers (users) { return; } - let userPaymentPromises = users.map(updateUser); - let lastUser = users[users.length - 1]; + const userPaymentPromises = users.map(updateUser); + const lastUser = users[users.length - 1]; return Promise.all(userPaymentPromises) - .then(() => { - return processUsers(lastUser._id); - }); + .then(() => processUsers(lastUser._id)); } function updateUser (user) { count++; if (!user.profile.name || user.profile.name === 'profile name not found' || !user.profile.imageUrl || !user.profile.blurb) { - return olDbUsers.findOne({_id: user._id}, '_id profile') - .then((oldUserData) => { + return olDbUsers.findOne({ _id: user._id }, '_id profile') + .then(oldUserData => { if (!oldUserData) return; // specify user data to change: - let set = {}; + const set = {}; if (oldUserData.profile.name === 'profile name not found') return; - let userNeedsProfileName = !user.profile.name || user.profile.name === 'profile name not found'; + const userNeedsProfileName = !user.profile.name || user.profile.name === 'profile name not found'; if (userNeedsProfileName && oldUserData.profile.name) { set['profile.name'] = oldUserData.profile.name; } @@ -86,17 +86,17 @@ function updateUser (user) { if (Object.keys(set).length !== 0 && set.constructor === Object) { console.log(set); - return dbUsers.update({_id: user._id}, {$set: set}); + return dbUsers.update({ _id: user._id }, { $set: set }); } }); } - if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); - if (user._id === authorUuid) console.warn(`${authorName } processed`); + if (count % progressCount === 0) console.warn(`${count} ${user._id}`); + if (user._id === authorUuid) console.warn(`${authorName} processed`); } function displayData () { - console.warn(`\n${ count } users processed\n`); + console.warn(`\n${count} users processed\n`); return exiting(0); } @@ -108,7 +108,7 @@ function exiting (code, msg) { if (msg) { if (code) { console.error(msg); - } else { + } else { console.log(msg); } } diff --git a/migrations/s3-upload.js b/migrations/s3-upload.js index 4a2d2efcd2..612da82cbc 100644 --- a/migrations/s3-upload.js +++ b/migrations/s3-upload.js @@ -1,8 +1,9 @@ -let request = require('superagent'); -let last = require('lodash/last'); -let AWS = require('aws-sdk'); +const request = require('superagent'); +const last = require('lodash/last'); +const AWS = require('aws-sdk'); + +const config = require('../config'); -let config = require('../config'); const S3_DIRECTORY = 'mobileApp/images'; // config.S3.SPRITES_DIRECTORY; AWS.config.update({ @@ -11,8 +12,8 @@ AWS.config.update({ // region: config.get('S3_REGION'), }); -let BUCKET_NAME = config.S3.bucket; -let s3 = new AWS.S3(); +const BUCKET_NAME = config.S3.bucket; +const s3 = new AWS.S3(); // Adapted from http://stackoverflow.com/a/22210077/2601552 function uploadFile (buffer, fileName) { @@ -21,7 +22,7 @@ function uploadFile (buffer, fileName) { Body: buffer, Key: fileName, Bucket: BUCKET_NAME, - }, (error) => { + }, error => { if (error) { reject(error); } else { @@ -33,9 +34,9 @@ function uploadFile (buffer, fileName) { } function getFileName (file) { - let piecesOfPath = file.split('/'); - let name = last(piecesOfPath); - let fullName = S3_DIRECTORY + name; + const piecesOfPath = file.split('/'); + const name = last(piecesOfPath); + const fullName = S3_DIRECTORY + name; return fullName; } @@ -44,7 +45,7 @@ function getFileFromUrl (url) { return new Promise((resolve, reject) => { request.get(url).end((err, res) => { if (err) return reject(err); - let file = res.body; + const file = res.body; resolve(file); }); }); @@ -52,25 +53,21 @@ function getFileFromUrl (url) { let commit = '78f94e365c72cc58f66857d5941105638db7d35c'; commit = 'df0dbaba636c9ce424cc7040f7bd7fc1aa311015'; -let gihuburl = `https://api.github.com/repos/HabitRPG/habitica/commits/${commit}`; +const gihuburl = `https://api.github.com/repos/HabitRPG/habitica/commits/${commit}`; let currentIndex = 0; function uploadToS3 (start, end, filesUrls) { - let urls = filesUrls.slice(start, end); + const urls = filesUrls.slice(start, end); if (urls.length === 0) { console.log('done'); return; } - let promises = urls.map(fullUrl => { - return getFileFromUrl(fullUrl) - .then((buffer) => { - return uploadFile(buffer, getFileName(fullUrl)); - }); - }); + const promises = urls.map(fullUrl => getFileFromUrl(fullUrl) + .then(buffer => uploadFile(buffer, getFileName(fullUrl)))); console.log(promises.length); return Promise.all(promises) @@ -86,12 +83,10 @@ function uploadToS3 (start, end, filesUrls) { request.get(gihuburl) .end((err, res) => { console.log(err); - let files = res.body.files; + const { files } = res.body; let filesUrls = ['']; - filesUrls = files.map(file => { - return file.raw_url; - }); + filesUrls = files.map(file => file.raw_url); uploadToS3(currentIndex, currentIndex + 50, filesUrls); }); diff --git a/migrations/tasks/habits-one-history-entry-per-day-challenges.js b/migrations/tasks/habits-one-history-entry-per-day-challenges.js index dcb05f48bf..410c34e44d 100644 --- a/migrations/tasks/habits-one-history-entry-per-day-challenges.js +++ b/migrations/tasks/habits-one-history-entry-per-day-challenges.js @@ -9,13 +9,14 @@ const monk = require('monk'); const _ = require('lodash'); const moment = require('moment'); + const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE const dbTasks = monk(connectionString).get('tasks', { castIds: false }); function processChallengeHabits (lastId) { - let query = { - 'challenge.id': {$exists: true}, - userId: {$exists: false}, + const query = { + 'challenge.id': { $exists: true }, + userId: { $exists: false }, type: 'habit', }; @@ -26,17 +27,17 @@ function processChallengeHabits (lastId) { } dbTasks.find(query, { - sort: {_id: 1}, + sort: { _id: 1 }, limit: 500, }) .then(updateChallengeHabits) - .catch((err) => { + .catch(err => { console.log(err); - return exiting(1, `ERROR! ${ err}`); + return exiting(1, `ERROR! ${err}`); }); } -let progressCount = 1000; +const progressCount = 1000; let count = 0; function updateChallengeHabits (habits) { @@ -46,13 +47,11 @@ function updateChallengeHabits (habits) { return; } - let habitsPromises = habits.map(updateChallengeHabit); - let lastHabit = habits[habits.length - 1]; + const habitsPromises = habits.map(updateChallengeHabit); + const lastHabit = habits[habits.length - 1]; return Promise.all(habitsPromises) - .then(() => { - return processChallengeHabits(lastHabit._id); - }); + .then(() => processChallengeHabits(lastHabit._id)); } function updateChallengeHabit (habit) { @@ -76,13 +75,12 @@ function updateChallengeHabit (habit) { entry.scoreDirection = entry.value > previousValue ? 'up' : 'down'; } }) - .groupBy(entry => { // group entries by aggregateBy - return moment(entry.date).format('YYYYMMDD'); - }) + .groupBy(entry => // group entries by aggregateBy + moment(entry.date).format('YYYYMMDD')) .toPairs() // [key, entry] .sortBy(([key]) => key) // sort by date .map(keyEntryPair => { - let entries = keyEntryPair[1]; // 1 is entry, 0 is key + const entries = keyEntryPair[1]; // 1 is entry, 0 is key let scoredUp = 0; let scoredDown = 0; @@ -107,16 +105,16 @@ function updateChallengeHabit (habit) { }) .value(); - return dbTasks.update({_id: habit._id}, { - $set: {history: habit.history}, + return dbTasks.update({ _id: habit._id }, { + $set: { history: habit.history }, }); } - if (count % progressCount === 0) console.warn(`${count } habits processed`); + if (count % progressCount === 0) console.warn(`${count} habits processed`); } function displayData () { - console.warn(`\n${ count } tasks processed\n`); + console.warn(`\n${count} tasks processed\n`); return exiting(0); } @@ -128,7 +126,7 @@ function exiting (code, msg) { if (msg) { if (code) { console.error(msg); - } else { + } else { console.log(msg); } } diff --git a/migrations/tasks/habits-one-history-entry-per-day-users.js b/migrations/tasks/habits-one-history-entry-per-day-users.js index 509aa71eb7..3405dee55a 100644 --- a/migrations/tasks/habits-one-history-entry-per-day-users.js +++ b/migrations/tasks/habits-one-history-entry-per-day-users.js @@ -9,13 +9,14 @@ const authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; // ... own data is do const monk = require('monk'); const _ = require('lodash'); const moment = require('moment'); + const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE const dbTasks = monk(connectionString).get('tasks', { castIds: false }); const dbUsers = monk(connectionString).get('users', { castIds: false }); function processUsers (lastId) { - let query = { - migration: {$ne: migrationName}, + const query = { + migration: { $ne: migrationName }, }; if (lastId) { @@ -25,18 +26,18 @@ function processUsers (lastId) { } dbUsers.find(query, { - sort: {_id: 1}, + sort: { _id: 1 }, limit: 50, // just 50 users per time since we have to process all their habits as well fields: ['_id', 'preferences.timezoneOffset', 'preferences.dayStart'], }) .then(updateUsers) - .catch((err) => { + .catch(err => { console.log(err); - return exiting(1, `ERROR! ${ err}`); + return exiting(1, `ERROR! ${err}`); }); } -let progressCount = 1000; +const progressCount = 1000; let count = 0; function updateUsers (users) { @@ -46,13 +47,11 @@ function updateUsers (users) { return; } - let usersPromises = users.map(updateUser); - let lastUser = users[users.length - 1]; + const usersPromises = users.map(updateUser); + const lastUser = users[users.length - 1]; return Promise.all(usersPromises) - .then(() => { - return processUsers(lastUser._id); - }); + .then(() => processUsers(lastUser._id)); } function updateHabit (habit, timezoneOffset, dayStart) { @@ -82,7 +81,7 @@ function updateHabit (habit, timezoneOffset, dayStart) { .toPairs() // [key, entry] .sortBy(([key]) => key) // sort by date .map(keyEntryPair => { - let entries = keyEntryPair[1]; // 1 is entry, 0 is key + const entries = keyEntryPair[1]; // 1 is entry, 0 is key let scoredUp = 0; let scoredDown = 0; @@ -107,8 +106,8 @@ function updateHabit (habit, timezoneOffset, dayStart) { }) .value(); - return dbTasks.update({_id: habit._id}, { - $set: {history: habit.history}, + return dbTasks.update({ _id: habit._id }, { + $set: { history: habit.history }, }); } } @@ -116,32 +115,28 @@ function updateHabit (habit, timezoneOffset, dayStart) { function updateUser (user) { count++; - const timezoneOffset = user.preferences.timezoneOffset; - const dayStart = user.preferences.dayStart; + const { timezoneOffset } = user.preferences; + const { dayStart } = user.preferences; - if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); - if (user._id === authorUuid) console.warn(`${authorName } being processed`); + if (count % progressCount === 0) console.warn(`${count} ${user._id}`); + if (user._id === authorUuid) console.warn(`${authorName} being processed`); return dbTasks.find({ type: 'habit', userId: user._id, }) - .then(habits => { - return Promise.all(habits.map(habit => updateHabit(habit, timezoneOffset, dayStart))); - }) - .then(() => { - return dbUsers.update({_id: user._id}, { - $set: {migration: migrationName}, - }); - }) - .catch((err) => { + .then(habits => Promise.all(habits.map(habit => updateHabit(habit, timezoneOffset, dayStart)))) + .then(() => dbUsers.update({ _id: user._id }, { + $set: { migration: migrationName }, + })) + .catch(err => { console.log(err); - return exiting(1, `ERROR! ${ err}`); + return exiting(1, `ERROR! ${err}`); }); } function displayData () { - console.warn(`\n${ count } tasks processed\n`); + console.warn(`\n${count} tasks processed\n`); return exiting(0); } @@ -153,7 +148,7 @@ function exiting (code, msg) { if (msg) { if (code) { console.error(msg); - } else { + } else { console.log(msg); } } diff --git a/migrations/tasks/tasks-set-everyX.js b/migrations/tasks/tasks-set-everyX.js index c507f80356..899d847bf7 100644 --- a/migrations/tasks/tasks-set-everyX.js +++ b/migrations/tasks/tasks-set-everyX.js @@ -1,18 +1,19 @@ /* let migrationName = 'tasks-set-everyX'; */ -let authorName = 'Sabe'; // in case script author needs to know when their ... -let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done +const authorName = 'Sabe'; // in case script author needs to know when their ... +const authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done /* * Iterates over all tasks and sets invalid everyX values (less than 0 or more than 9999 or not an int) field to 0 */ -let monk = require('monk'); -let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; -let dbTasks = monk(connectionString).get('tasks', { castIds: false }); +const monk = require('monk'); + +const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; +const dbTasks = monk(connectionString).get('tasks', { castIds: false }); function processTasks (lastId) { // specify a query to limit the affected tasks (empty for all tasks): - let query = { + const query = { type: 'daily', everyX: { $not: { @@ -30,18 +31,18 @@ function processTasks (lastId) { } dbTasks.find(query, { - sort: {_id: 1}, + sort: { _id: 1 }, limit: 250, fields: [], }) .then(updateTasks) - .catch((err) => { + .catch(err => { console.log(err); - return exiting(1, `ERROR! ${ err}`); + return exiting(1, `ERROR! ${err}`); }); } -let progressCount = 1000; +const progressCount = 1000; let count = 0; function updateTasks (tasks) { @@ -51,27 +52,25 @@ function updateTasks (tasks) { return; } - let taskPromises = tasks.map(updatetask); - let lasttask = tasks[tasks.length - 1]; + const taskPromises = tasks.map(updatetask); + const lasttask = tasks[tasks.length - 1]; return Promise.all(taskPromises) - .then(() => { - return processTasks(lasttask._id); - }); + .then(() => processTasks(lasttask._id)); } function updatetask (task) { count++; - let set = {everyX: 0}; + const set = { everyX: 0 }; - dbTasks.update({_id: task._id}, {$set: set}); + dbTasks.update({ _id: task._id }, { $set: set }); - if (count % progressCount === 0) console.warn(`${count } ${ task._id}`); - if (task._id === authorUuid) console.warn(`${authorName } processed`); + if (count % progressCount === 0) console.warn(`${count} ${task._id}`); + if (task._id === authorUuid) console.warn(`${authorName} processed`); } function displayData () { - console.warn(`\n${ count } tasks processed\n`); + console.warn(`\n${count} tasks processed\n`); return exiting(0); } @@ -83,7 +82,7 @@ function exiting (code, msg) { if (msg) { if (code) { console.error(msg); - } else { + } else { console.log(msg); } } diff --git a/migrations/tasks/tasks-set-yesterdailies.js b/migrations/tasks/tasks-set-yesterdailies.js index 253d871612..214ac15f9f 100644 --- a/migrations/tasks/tasks-set-yesterdailies.js +++ b/migrations/tasks/tasks-set-yesterdailies.js @@ -1,6 +1,5 @@ /* let migrationName = 'tasks-set-yesterdaily'; */ -let authorName = 'TheHollidayInn'; // in case script author needs to know when their ... -let authorUuid = ''; // ... own data is done +// ... own data is done /* * Iterates over all tasks and sets the yseterDaily field to True @@ -8,10 +7,13 @@ let authorUuid = ''; // ... own data is done import monk from 'monk'; -let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -let dbTasks = monk(connectionString).get('tasks', { castIds: false }); +const authorName = 'TheHollidayInn'; // in case script author needs to know when their ... +const authorUuid = ''; -let progressCount = 1000; +const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +const dbTasks = monk(connectionString).get('tasks', { castIds: false }); + +const progressCount = 1000; let count = 0; function exiting (code, msg) { @@ -22,7 +24,7 @@ function exiting (code, msg) { if (msg) { if (code) { console.error(msg); - } else { + } else { console.log(msg); } } @@ -30,18 +32,18 @@ function exiting (code, msg) { } function displayData () { - console.warn(`\n${ count } tasks processed\n`); + console.warn(`\n${count} tasks processed\n`); return exiting(0); } function updatetask (task) { count++; - let set = {yesterDaily: true}; + const set = { yesterDaily: true }; - dbTasks.update({_id: task._id}, {$set: set}); + dbTasks.update({ _id: task._id }, { $set: set }); - if (count % progressCount === 0) console.warn(`${count } ${ task._id}`); - if (task._id === authorUuid) console.warn(`${authorName } processed`); + if (count % progressCount === 0) console.warn(`${count} ${task._id}`); + if (task._id === authorUuid) console.warn(`${authorName} processed`); } function updateTasks (tasks) { @@ -51,18 +53,17 @@ function updateTasks (tasks) { return; } - let taskPromises = tasks.map(updatetask); - let lasttask = tasks[tasks.length - 1]; + const taskPromises = tasks.map(updatetask); + const lasttask = tasks[tasks.length - 1]; return Promise.all(taskPromises) - .then(() => { - return processTasks(lasttask._id); // eslint-disable-line no-use-before-define - }); + .then(() => processTasks(lasttask._id), // eslint-disable-line no-use-before-define + ); } function processTasks (lastId) { // specify a query to limit the affected tasks (empty for all tasks): - let query = { + const query = { yesterDaily: false, }; @@ -73,15 +74,15 @@ function processTasks (lastId) { } dbTasks.find(query, { - sort: {_id: 1}, + sort: { _id: 1 }, limit: 250, fields: [ // specify fields we are interested in to limit retrieved data (empty if we're not reading data): ], }) .then(updateTasks) - .catch((err) => { + .catch(err => { console.log(err); - return exiting(1, `ERROR! ${ err}`); + return exiting(1, `ERROR! ${err}`); }); } diff --git a/migrations/users/account-transfer.js b/migrations/users/account-transfer.js index 42a9602aa1..b4f1d270c8 100644 --- a/migrations/users/account-transfer.js +++ b/migrations/users/account-transfer.js @@ -9,6 +9,7 @@ let authorUuid = ''; // ... own data is done */ const monk = require('monk'); + const connectionString = ''; const Users = monk(connectionString).get('users', { castIds: false }); @@ -16,21 +17,21 @@ module.exports = async function accountTransfer () { const fromAccountId = ''; const toAccountId = ''; - const fromAccount = await Users.findOne({_id: fromAccountId}); - const toAccount = await Users.findOne({_id: toAccountId}); + const fromAccount = await Users.findOne({ _id: fromAccountId }); + const toAccount = await Users.findOne({ _id: toAccountId }); - const newMounts = Object.assign({}, fromAccount.items.mounts, toAccount.items.mounts); - const newPets = Object.assign({}, fromAccount.items.pets, toAccount.items.pets); - const newBackgrounds = Object.assign({}, fromAccount.purchased.background, toAccount.purchased.background); + const newMounts = { ...fromAccount.items.mounts, ...toAccount.items.mounts }; + const newPets = { ...fromAccount.items.pets, ...toAccount.items.pets }; + const newBackgrounds = { ...fromAccount.purchased.background, ...toAccount.purchased.background }; - await Users.update({_id: toAccountId}, { + await Users.update({ _id: toAccountId }, { $set: { 'items.pets': newPets, 'items.mounts': newMounts, 'purchased.background': newBackgrounds, }, }) - .then((result) => { + .then(result => { console.log(result); }); }; diff --git a/migrations/users/achievement-restore.js b/migrations/users/achievement-restore.js index 45e7866398..dfe4d9b9db 100644 --- a/migrations/users/achievement-restore.js +++ b/migrations/users/achievement-restore.js @@ -9,6 +9,7 @@ const authorUuid = ''; // ... own data is done */ const monk = require('monk'); + const connectionString = 'mongodb://localhost/new-habit'; const Users = monk(connectionString).get('users', { castIds: false }); @@ -19,13 +20,13 @@ function getAchievementUpdate (newUser, oldUser) { const oldAchievements = oldUser.achievements; const newAchievements = newUser.achievements; - let achievementsUpdate = Object.assign({}, newAchievements); + const achievementsUpdate = { ...newAchievements }; // ultimateGearSets if (!achievementsUpdate.ultimateGearSets && oldAchievements.ultimateGearSets) { achievementsUpdate.ultimateGearSets = oldAchievements.ultimateGearSets; } else if (oldAchievements.ultimateGearSets) { - for (let index in oldAchievements.ultimateGearSets) { + for (const index in oldAchievements.ultimateGearSets) { if (oldAchievements.ultimateGearSets[index]) achievementsUpdate.ultimateGearSets[index] = true; } } @@ -37,7 +38,7 @@ function getAchievementUpdate (newUser, oldUser) { // Quests if (!achievementsUpdate.quests) achievementsUpdate.quests = {}; - for (let index in oldAchievements.quests) { + for (const index in oldAchievements.quests) { if (!achievementsUpdate.quests[index]) { achievementsUpdate.quests[index] = oldAchievements.quests[index]; } else { @@ -54,10 +55,10 @@ function getAchievementUpdate (newUser, oldUser) { // All others const indexsToIgnore = ['ultimateGearSets', 'challenges', 'quests', 'rebirthLevel']; - for (let index in oldAchievements) { + for (const index in oldAchievements) { if (indexsToIgnore.indexOf(index) !== -1) continue; // eslint-disable-line no-continue - if (!achievementsUpdate[index]) { + if (!achievementsUpdate[index]) { achievementsUpdate[index] = oldAchievements[index]; continue; // eslint-disable-line no-continue } @@ -75,18 +76,19 @@ module.exports = async function achievementRestore () { ]; /* eslint-disable no-await-in-loop */ - for (let index in userIds) { + for (const index in userIds) { const userId = userIds[index]; - const oldUser = await UsersOld.findOne({_id: userId}, 'achievements'); - const newUser = await Users.findOne({_id: userId}, 'achievements'); + const oldUser = await UsersOld.findOne({ _id: userId }, 'achievements'); + const newUser = await Users.findOne({ _id: userId }, 'achievements'); const achievementUpdate = getAchievementUpdate(newUser, oldUser); await Users.update( - {_id: userId}, + { _id: userId }, { $set: { achievements: achievementUpdate, }, - }); + }, + ); console.log(`Updated ${userId}`); /* eslint-enable no-await-in-loop */ } diff --git a/migrations/users/bulk-email.js b/migrations/users/bulk-email.js index adba28f805..55a0bcc06c 100644 --- a/migrations/users/bulk-email.js +++ b/migrations/users/bulk-email.js @@ -1,8 +1,9 @@ /* eslint-disable no-console */ -import { sendTxn } from '../../website/server/libs/email'; -import { model as User } from '../../website/server/models/user'; import moment from 'moment'; import nconf from 'nconf'; +import { sendTxn } from '../../website/server/libs/email'; +import { model as User } from '../../website/server/models/user'; + const BASE_URL = nconf.get('BASE_URL'); const EMAIL_SLUG = 'mandrill-email-slug'; // Set email template to send const MIGRATION_NAME = 'bulk-email'; @@ -18,16 +19,16 @@ async function updateUser (user) { sendTxn( user, EMAIL_SLUG, - [{name: 'BASE_URL', content: BASE_URL}] // Add variables from template + [{ name: 'BASE_URL', content: BASE_URL }], // Add variables from template ); - return await User.update({_id: user._id}, {$set: {migration: MIGRATION_NAME}}).exec(); + return await User.update({ _id: user._id }, { $set: { migration: MIGRATION_NAME } }).exec(); } module.exports = async function processUsers () { - let query = { - migration: {$ne: MIGRATION_NAME}, - 'auth.timestamps.loggedin': {$gt: moment().subtract(2, 'weeks').toDate()}, // customize or remove to target different populations + const query = { + migration: { $ne: MIGRATION_NAME }, + 'auth.timestamps.loggedin': { $gt: moment().subtract(2, 'weeks').toDate() }, // customize or remove to target different populations }; const fields = { @@ -41,7 +42,7 @@ module.exports = async function processUsers () { const users = await User // eslint-disable-line no-await-in-loop .find(query) .limit(250) - .sort({_id: 1}) + .sort({ _id: 1 }) .select(fields) .lean() .exec(); diff --git a/migrations/users/full-stable.js b/migrations/users/full-stable.js index f41f76f359..17df7002f5 100644 --- a/migrations/users/full-stable.js +++ b/migrations/users/full-stable.js @@ -1,11 +1,12 @@ /* eslint-disable no-console */ -const MIGRATION_NAME = 'full-stable'; import each from 'lodash/each'; import keys from 'lodash/keys'; import content from '../../website/common/script/content/index'; import { model as User } from '../../website/server/models/user'; +const MIGRATION_NAME = 'full-stable'; + const progressCount = 1000; let count = 0; @@ -20,39 +21,39 @@ async function updateUser (user) { set.migration = MIGRATION_NAME; - each(keys(content.pets), (pet) => { + each(keys(content.pets), pet => { set[`items.pets.${pet}`] = 5; }); - each(keys(content.premiumPets), (pet) => { + each(keys(content.premiumPets), pet => { set[`items.pets.${pet}`] = 5; }); - each(keys(content.questPets), (pet) => { + each(keys(content.questPets), pet => { set[`items.pets.${pet}`] = 5; }); - each(keys(content.specialPets), (pet) => { + each(keys(content.specialPets), pet => { set[`items.pets.${pet}`] = 5; }); - each(keys(content.mounts), (mount) => { + each(keys(content.mounts), mount => { set[`items.mounts.${mount}`] = true; }); - each(keys(content.premiumMounts), (mount) => { + each(keys(content.premiumMounts), mount => { set[`items.mounts.${mount}`] = true; }); - each(keys(content.questMounts), (mount) => { + each(keys(content.questMounts), mount => { set[`items.mounts.${mount}`] = true; }); - each(keys(content.specialMounts), (mount) => { + each(keys(content.specialMounts), mount => { set[`items.mounts.${mount}`] = true; }); if (count % progressCount === 0) console.warn(`${count} ${user._id}`); - return await User.update({_id: user._id}, {$set: set}).exec(); + return await User.update({ _id: user._id }, { $set: set }).exec(); } module.exports = async function processUsers () { - let query = { - migration: {$ne: MIGRATION_NAME}, + const query = { + migration: { $ne: MIGRATION_NAME }, 'auth.local.username': 'olson22', }; @@ -64,7 +65,7 @@ module.exports = async function processUsers () { const users = await User // eslint-disable-line no-await-in-loop .find(query) .limit(250) - .sort({_id: 1}) + .sort({ _id: 1 }) .select(fields) .lean() .exec(); diff --git a/migrations/users/mystery-items.js b/migrations/users/mystery-items.js index d44743e14c..f0b3ad5e96 100644 --- a/migrations/users/mystery-items.js +++ b/migrations/users/mystery-items.js @@ -1,9 +1,10 @@ /* eslint-disable no-console */ -const MIGRATION_NAME = 'mystery_items_201909'; -const MYSTERY_ITEMS = ['armor_mystery_201909', 'head_mystery_201909']; import { model as User } from '../../website/server/models/user'; import { model as UserNotification } from '../../website/server/models/userNotification'; +const MIGRATION_NAME = 'mystery_items_201909'; +const MYSTERY_ITEMS = ['armor_mystery_201909', 'head_mystery_201909']; + const progressCount = 1000; let count = 0; @@ -29,12 +30,12 @@ async function updateUser (user) { if (count % progressCount === 0) console.warn(`${count} ${user._id}`); - return await User.update({_id: user._id}, {$set: set, $push: push, $addToSet: addToSet}).exec(); + return await User.update({ _id: user._id }, { $set: set, $push: push, $addToSet: addToSet }).exec(); } module.exports = async function processUsers () { - let query = { - migration: {$ne: MIGRATION_NAME}, + const query = { + migration: { $ne: MIGRATION_NAME }, 'purchased.plan.customerId': { $ne: null }, $or: [ { 'purchased.plan.dateTerminated': { $gte: new Date() } }, @@ -51,7 +52,7 @@ module.exports = async function processUsers () { const users = await User // eslint-disable-line no-await-in-loop .find(query) .limit(250) - .sort({_id: 1}) + .sort({ _id: 1 }) .select(fields) .lean() .exec(); diff --git a/migrations/users/pi-day.js b/migrations/users/pi-day.js index a954ba4b5a..3586194eab 100644 --- a/migrations/users/pi-day.js +++ b/migrations/users/pi-day.js @@ -1,9 +1,10 @@ /* eslint-disable no-console */ -const MIGRATION_NAME = '20190314_pi_day'; import { v4 as uuid } from 'uuid'; import { model as User } from '../../website/server/models/user'; +const MIGRATION_NAME = '20190314_pi_day'; + const progressCount = 1000; let count = 0; @@ -29,19 +30,19 @@ async function updateUser (user) { set['items.gear.owned.head_special_piDay'] = false; set['items.gear.owned.shield_special_piDay'] = false; const push = [ - {type: 'marketGear', path: 'gear.flat.head_special_piDay', _id: uuid()}, - {type: 'marketGear', path: 'gear.flat.shield_special_piDay', _id: uuid()}, + { type: 'marketGear', path: 'gear.flat.head_special_piDay', _id: uuid() }, + { type: 'marketGear', path: 'gear.flat.shield_special_piDay', _id: uuid() }, ]; if (count % progressCount === 0) console.warn(`${count} ${user._id}`); - return await User.update({_id: user._id}, {$inc: inc, $set: set, $push: {pinnedItems: {$each: push}}}).exec(); + return await User.update({ _id: user._id }, { $inc: inc, $set: set, $push: { pinnedItems: { $each: push } } }).exec(); } module.exports = async function processUsers () { - let query = { - migration: {$ne: MIGRATION_NAME}, - 'auth.timestamps.loggedin': {$gt: new Date('2019-02-15')}, + const query = { + migration: { $ne: MIGRATION_NAME }, + 'auth.timestamps.loggedin': { $gt: new Date('2019-02-15') }, }; const fields = { @@ -53,7 +54,7 @@ module.exports = async function processUsers () { const users = await User // eslint-disable-line no-await-in-loop .find(query) .limit(250) - .sort({_id: 1}) + .sort({ _id: 1 }) .select(fields) .lean() .exec(); diff --git a/migrations/users/remove-social-users-extra-data.js b/migrations/users/remove-social-users-extra-data.js index b57a5c3ec3..2e63da3d30 100644 --- a/migrations/users/remove-social-users-extra-data.js +++ b/migrations/users/remove-social-users-extra-data.js @@ -8,12 +8,13 @@ const authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; // ... own data is do const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE const monk = require('monk'); + const dbUsers = monk(connectionString).get('users', { castIds: false }); function processUsers (lastId) { // specify a query to limit the affected users (empty for all users): - let query = { - migration: {$ne: migrationName}, + const query = { + migration: { $ne: migrationName }, $or: [ { 'auth.facebook.id': { $exists: true } }, { 'auth.google.id': { $exists: true } }, @@ -27,17 +28,17 @@ function processUsers (lastId) { } dbUsers.find(query, { - sort: {_id: 1}, + sort: { _id: 1 }, limit: 250, }) .then(updateUsers) - .catch((err) => { + .catch(err => { console.log(err); - return exiting(1, `ERROR! ${ err}`); + return exiting(1, `ERROR! ${err}`); }); } -let progressCount = 1000; +const progressCount = 1000; let count = 0; function updateUsers (users) { @@ -47,8 +48,8 @@ function updateUsers (users) { return; } - let userPromises = users.map(updateUser); - let lastUser = users[users.length - 1]; + const userPromises = users.map(updateUser); + const lastUser = users[users.length - 1]; return Promise.all(userPromises) .then(() => { @@ -82,12 +83,12 @@ function updateUser (user) { _id: user._id, }, update); - if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); - if (user._id === authorUuid) console.warn(`${authorName } processed`); + if (count % progressCount === 0) console.warn(`${count} ${user._id}`); + if (user._id === authorUuid) console.warn(`${authorName} processed`); } function displayData () { - console.warn(`\n${ count } users processed\n`); + console.warn(`\n${count} users processed\n`); return exiting(0); } @@ -99,7 +100,7 @@ function exiting (code, msg) { if (msg) { if (code) { console.error(msg); - } else { + } else { console.log(msg); } } diff --git a/migrations/users/take-this.js b/migrations/users/take-this.js index 6d990b79f3..96a067d0be 100644 --- a/migrations/users/take-this.js +++ b/migrations/users/take-this.js @@ -1,9 +1,10 @@ /* eslint-disable no-console */ -const MIGRATION_NAME = '20181203_take_this'; import { v4 as uuid } from 'uuid'; import { model as User } from '../../website/server/models/user'; +const MIGRATION_NAME = '20181203_take_this'; + const progressCount = 1000; let count = 0; @@ -19,36 +20,35 @@ async function updateUser (user) { push = false; } else if (typeof user.items.gear.owned.body_special_takeThis !== 'undefined') { set['items.gear.owned.back_special_takeThis'] = false; - push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.back_special_takeThis', _id: uuid()}}; + push = { pinnedItems: { type: 'marketGear', path: 'gear.flat.back_special_takeThis', _id: uuid() } }; } else if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') { set['items.gear.owned.body_special_takeThis'] = false; - push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.body_special_takeThis', _id: uuid()}}; + push = { pinnedItems: { type: 'marketGear', path: 'gear.flat.body_special_takeThis', _id: uuid() } }; } else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') { set['items.gear.owned.head_special_takeThis'] = false; - push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_takeThis', _id: uuid()}}; + push = { pinnedItems: { type: 'marketGear', path: 'gear.flat.head_special_takeThis', _id: uuid() } }; } else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') { set['items.gear.owned.armor_special_takeThis'] = false; - push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_takeThis', _id: uuid()}}; + push = { pinnedItems: { type: 'marketGear', path: 'gear.flat.armor_special_takeThis', _id: uuid() } }; } else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') { set['items.gear.owned.weapon_special_takeThis'] = false; - push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.weapon_special_takeThis', _id: uuid()}}; + push = { pinnedItems: { type: 'marketGear', path: 'gear.flat.weapon_special_takeThis', _id: uuid() } }; } else { set['items.gear.owned.shield_special_takeThis'] = false; - push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.shield_special_takeThis', _id: uuid()}}; + push = { pinnedItems: { type: 'marketGear', path: 'gear.flat.shield_special_takeThis', _id: uuid() } }; } if (count % progressCount === 0) console.warn(`${count} ${user._id}`); if (push) { - return await User.update({_id: user._id}, {$set: set, $push: push}).exec(); - } else { - return await User.update({_id: user._id}, {$set: set}).exec(); + return await User.update({ _id: user._id }, { $set: set, $push: push }).exec(); } + return await User.update({ _id: user._id }, { $set: set }).exec(); } module.exports = async function processUsers () { - let query = { - migration: {$ne: MIGRATION_NAME}, + const query = { + migration: { $ne: MIGRATION_NAME }, challenges: '00708425-d477-41a5-bf27-6270466e7976', }; @@ -61,7 +61,7 @@ module.exports = async function processUsers () { const users = await User // eslint-disable-line no-await-in-loop .find(query) .limit(250) - .sort({_id: 1}) + .sort({ _id: 1 }) .select(fields) .lean() .exec(); diff --git a/migrations/users/users-to-test.js b/migrations/users/users-to-test.js index cf9f4cc92f..edb23e03db 100644 --- a/migrations/users/users-to-test.js +++ b/migrations/users/users-to-test.js @@ -8,22 +8,24 @@ let authorUuid = ''; // ... own data is done * This migraition will copy user data from prod to test */ -let monk = require('monk'); -let testConnectionSting = ''; // FOR TEST DATABASE -let usersTest = monk(testConnectionSting).get('users', { castIds: false }); -let groupsTest = monk(testConnectionSting).get('groups', { castIds: false }); -let challengesTest = monk(testConnectionSting).get('challenges', { castIds: false }); -let tasksTest = monk(testConnectionSting).get('tasks', { castIds: false }); - -let monk2 = require('monk'); -let liveConnectString = ''; // FOR TEST DATABASE -let userLive = monk2(liveConnectString).get('users', { castIds: false }); -let groupsLive = monk2(liveConnectString).get('groups', { castIds: false }); -let challengesLive = monk2(liveConnectString).get('challenges', { castIds: false }); -let tasksLive = monk2(liveConnectString).get('tasks', { castIds: false }); - import uniq from 'lodash/uniq'; +const monk = require('monk'); + +const testConnectionSting = ''; // FOR TEST DATABASE +const usersTest = monk(testConnectionSting).get('users', { castIds: false }); +const groupsTest = monk(testConnectionSting).get('groups', { castIds: false }); +const challengesTest = monk(testConnectionSting).get('challenges', { castIds: false }); +const tasksTest = monk(testConnectionSting).get('tasks', { castIds: false }); + +const monk2 = require('monk'); + +const liveConnectString = ''; // FOR TEST DATABASE +const userLive = monk2(liveConnectString).get('users', { castIds: false }); +const groupsLive = monk2(liveConnectString).get('groups', { castIds: false }); +const challengesLive = monk2(liveConnectString).get('challenges', { castIds: false }); +const tasksLive = monk2(liveConnectString).get('tasks', { castIds: false }); + // Variabls for updating /* let userIds = [ @@ -36,11 +38,11 @@ let challengeIds = []; let tasksIds = []; async function processUsers () { - let userPromises = []; + const userPromises = []; // {_id: {$in: userIds}} - return userLive.find({guilds: 'b0764d64-8276-45a1-afa5-5ca9a5c64ca0'}) - .each((user) => { + return userLive.find({ guilds: 'b0764d64-8276-45a1-afa5-5ca9a5c64ca0' }) + .each(user => { if (user.guilds.length > 0) groupIds = groupIds.concat(user.guilds); if (user.party._id) groupIds.push(user.party._id); if (user.challenges.length > 0) challengeIds = challengeIds.concat(user.challenges); @@ -49,56 +51,48 @@ async function processUsers () { if (user.tasksOrder.dailys.length > 0) tasksIds = tasksIds.concat(user.tasksOrder.dailys); if (user.tasksOrder.habits.length > 0) tasksIds = tasksIds.concat(user.tasksOrder.habits); - let userPromise = usersTest.update({_id: user._id}, user, {upsert: true}); + const userPromise = usersTest.update({ _id: user._id }, user, { upsert: true }); userPromises.push(userPromise); - }).then(() => { - return Promise.all(userPromises); - }) + }).then(() => Promise.all(userPromises)) .then(() => { console.log('Done User'); }); } function processGroups () { - let promises = []; - let groupsToQuery = uniq(groupIds); - return groupsLive.find({_id: {$in: groupsToQuery}}) - .each((group) => { - let promise = groupsTest.update({_id: group._id}, group, {upsert: true}); + const promises = []; + const groupsToQuery = uniq(groupIds); + return groupsLive.find({ _id: { $in: groupsToQuery } }) + .each(group => { + const promise = groupsTest.update({ _id: group._id }, group, { upsert: true }); promises.push(promise); - }).then(() => { - return Promise.all(promises); - }) + }).then(() => Promise.all(promises)) .then(() => { console.log('Done Group'); }); } function processChallenges () { - let promises = []; - let challengesToQuery = uniq(challengeIds); - return challengesLive.find({_id: {$in: challengesToQuery}}) - .each((challenge) => { - let promise = challengesTest.update({_id: challenge._id}, challenge, {upsert: true}); + const promises = []; + const challengesToQuery = uniq(challengeIds); + return challengesLive.find({ _id: { $in: challengesToQuery } }) + .each(challenge => { + const promise = challengesTest.update({ _id: challenge._id }, challenge, { upsert: true }); promises.push(promise); - }).then(() => { - return Promise.all(promises); - }) + }).then(() => Promise.all(promises)) .then(() => { console.log('Done Challenge'); }); } function processTasks () { - let promises = []; - let tasksToQuery = uniq(tasksIds); - return tasksLive.find({_id: {$in: tasksToQuery}}) - .each((task) => { - let promise = tasksTest.update({_id: task._id}, task, {upsert: true}); + const promises = []; + const tasksToQuery = uniq(tasksIds); + return tasksLive.find({ _id: { $in: tasksToQuery } }) + .each(task => { + const promise = tasksTest.update({ _id: task._id }, task, { upsert: true }); promises.push(promise); - }).then(() => { - return Promise.all(promises); - }) + }).then(() => Promise.all(promises)) .then(() => { console.log('Done Tasks'); }); diff --git a/migrations/utils/connect.js b/migrations/utils/connect.js index 1b20d523ca..dfdcc84c08 100644 --- a/migrations/utils/connect.js +++ b/migrations/utils/connect.js @@ -1,6 +1,5 @@ -'use strict'; -const MongoClient = require('mongodb').MongoClient; +const { MongoClient } = require('mongodb'); const logger = require('./logger'); let dbConnection; diff --git a/migrations/utils/logger.js b/migrations/utils/logger.js index cf6d643e76..d93d78612b 100644 --- a/migrations/utils/logger.js +++ b/migrations/utils/logger.js @@ -1,10 +1,9 @@ -'use strict'; const chalk = require('chalk'); function loggerGenerator (type, color) { return function logger () { - let args = Array.from(arguments).map(arg => chalk[color](arg)); + const args = Array.from(arguments).map(arg => chalk[color](arg)); console[type].apply(null, args); }; } diff --git a/migrations/utils/timer.js b/migrations/utils/timer.js index cacdfc1b8b..b378ce060e 100644 --- a/migrations/utils/timer.js +++ b/migrations/utils/timer.js @@ -1,30 +1,31 @@ -'use strict'; -let logger = require('./logger'); +const logger = require('./logger'); class Timer { constructor (options) { options = options || {}; - let warningThreshold = options.minutesWarningThreshold || 10; + const warningThreshold = options.minutesWarningThreshold || 10; this.count = 0; this._minutesWarningThreshold = warningThreshold * 60; if (!options.disableAutoStart) this.start(); } + start () { this._internalTimer = setInterval(() => { this.count++; - let shouldWarn = this._minutesWarningThreshold < this.count; - let logStyle = shouldWarn ? 'error' : 'warn'; - let dangerMessage = shouldWarn ? 'DANGER: ' : ''; + const shouldWarn = this._minutesWarningThreshold < this.count; + const logStyle = shouldWarn ? 'error' : 'warn'; + const dangerMessage = shouldWarn ? 'DANGER: ' : ''; if (this.count % 30 === 0) { logger[logStyle](`${dangerMessage}Process has been running for`, this.count / 60, 'minutes'); } }, 1000); } + stop () { if (!this._internalTimer) { throw new Error('Timer has not started'); diff --git a/migrations/utils/unique.js b/migrations/utils/unique.js index 0d222145c3..df9052e698 100644 --- a/migrations/utils/unique.js +++ b/migrations/utils/unique.js @@ -1,4 +1,3 @@ -'use strict'; function unique (array) { return Array.from(new Set(array)); diff --git a/package-lock.json b/package-lock.json index c28c02ce5b..679679b5e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1096,27 +1096,16 @@ } }, "acorn": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", + "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", "dev": true }, "acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", - "dev": true, - "requires": { - "acorn": "^3.0.4" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - } - } + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz", + "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==", + "dev": true }, "agent-base": { "version": "4.3.0", @@ -1137,12 +1126,6 @@ "uri-js": "^4.2.2" } }, - "ajv-keywords": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", - "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", - "dev": true - }, "amazon-payments": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/amazon-payments/-/amazon-payments-0.2.7.tgz", @@ -1618,6 +1601,16 @@ "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", "dev": true }, + "array-includes": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.7.0" + } + }, "array-initial": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", @@ -1715,6 +1708,12 @@ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, "async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", @@ -2701,19 +2700,10 @@ } } }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "^0.2.0" - } - }, "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, "camelcase": { @@ -2865,9 +2855,9 @@ } }, "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, "check-error": { @@ -2964,12 +2954,6 @@ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==" }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -3269,6 +3253,12 @@ } } }, + "confusing-browser-globals": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz", + "integrity": "sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==", + "dev": true + }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -3285,6 +3275,12 @@ "integrity": "sha1-oJX+B7IEZZVfL6/Si11yvM2UnUQ=", "optional": true }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -3963,9 +3959,9 @@ "integrity": "sha512-hvSnros73+qyZXhHFjx2CMLwoj3Fe7eR9EJsFsqmcI1bB2OBWL/+0YzaEaKssCHnj/6crawNnUyw74Gm2EKe+Q==" }, "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "requires": { "esutils": "^2.0.2" @@ -4190,6 +4186,12 @@ "shimmer": "^1.2.0" } }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "enabled": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", @@ -4332,134 +4334,406 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", - "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.5.1.tgz", + "integrity": "sha512-32h99BoLYStT1iq1v2P9uwpyznQ4M2jRiFB6acitKz52Gqn+vPaMDUTB1bYi1WN4Nquj2w+t+bimYUG83DC55A==", "dev": true, "requires": { - "ajv": "^5.3.0", - "babel-code-frame": "^6.22.0", + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", "chalk": "^2.1.0", - "concat-stream": "^1.6.0", - "cross-spawn": "^5.1.0", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^3.7.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^3.5.4", - "esquery": "^1.0.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.2", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.1", + "esquery": "^1.0.1", "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", + "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.0.1", - "ignore": "^3.3.3", + "glob-parent": "^5.0.0", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", - "inquirer": "^3.0.6", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.9.1", + "inquirer": "^6.4.1", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.3.0", - "lodash": "^4.17.4", - "minimatch": "^3.0.2", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", "progress": "^2.0.0", - "regexpp": "^1.0.1", - "require-uncached": "^1.0.3", - "semver": "^5.3.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "~2.0.1", - "table": "4.0.2", - "text-table": "~0.2.0" + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" }, "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } }, "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^4.1.0" } + }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true } } }, + "eslint-config-airbnb-base": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.0.0.tgz", + "integrity": "sha512-2IDHobw97upExLmsebhtfoD3NAKhV4H0CJWP3Uprd/uk+cHuWYOczPVxQ8PxLFUAw7o3Th1RAU8u1DoUpr+cMA==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.7", + "object.assign": "^4.1.0", + "object.entries": "^1.1.0" + } + }, "eslint-config-habitrpg": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-habitrpg/-/eslint-config-habitrpg-4.0.0.tgz", - "integrity": "sha512-vZc/KjnNVL2BkDBQaQBF9JV16cnZyKa6djCCqH6iKhp8Uuye8Bym3eeLNEcnGkOtZfzJE61hDqiPZXQk7BiXJQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/eslint-config-habitrpg/-/eslint-config-habitrpg-6.0.3.tgz", + "integrity": "sha512-TYDXHE40Yu65uMHDqqepX0apOQvOGKZhvjz0Rki9CqL4HWO0QfHxv9Vi3mvVzY6+vrNvnUKs1s37SLTUMakiAg==", "dev": true, "requires": { - "eslint-plugin-lodash": "^2.3.5", - "eslint-plugin-mocha": "^4.7.0" + "eslint": "^6.5.1", + "eslint-config-airbnb-base": "^14.0.0", + "eslint-plugin-import": "^2.18.2", + "eslint-plugin-mocha": "^6.1.1", + "eslint-plugin-vue": "^5.2.3" }, "dependencies": { "eslint-plugin-mocha": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-4.12.1.tgz", - "integrity": "sha512-hxWtYHvLA0p/PKymRfDYh9Mxt5dYkg2Goy1vZDarTEEYfELP9ksga7kKG1NUKSQy27C8Qjc7YrSWTLUhOEOksA==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-6.1.1.tgz", + "integrity": "sha512-p/otruG425jRYDa28HjbBYYXoFNzq3Qp++gn5dbE44Kz4NvmIsSUKSV1T+RLYUcZOcdJKKAftXbaqkHFqReKoA==", "dev": true, - "optional": true, "requires": { - "ramda": "^0.25.0" + "ramda": "^0.26.1" } } } }, - "eslint-plugin-lodash": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-lodash/-/eslint-plugin-lodash-2.7.0.tgz", - "integrity": "sha512-sIEzx85Sy+Higf4W+oLCIyh7ym0OEcmJCzY8ukptlGfkcyVagzYBjhUt1JfkcpT4qZC68+7TzceJSqLu+qwYMg==", + "eslint-import-resolver-node": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", + "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", "dev": true, - "optional": true, "requires": { - "lodash": "~4.17.0" + "debug": "^2.6.9", + "resolve": "^1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-module-utils": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz", + "integrity": "sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==", + "dev": true, + "requires": { + "debug": "^2.6.8", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.18.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz", + "integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.0", + "read-pkg-up": "^2.0.0", + "resolve": "^1.11.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } } }, "eslint-plugin-mocha": { @@ -4479,16 +4753,34 @@ } } }, + "eslint-plugin-vue": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-5.2.3.tgz", + "integrity": "sha512-mGwMqbbJf0+VvpGR5Lllq0PMxvTdrZ/ZPjmhkacrCHbubJeJOt+T6E3HUzAifa2Mxi7RSdJfC9HFpOeSYVMMIw==", + "dev": true, + "requires": { + "vue-eslint-parser": "^5.0.0" + } + }, "eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", "dev": true, "requires": { "esrecurse": "^4.1.0", "estraverse": "^4.1.1" } }, + "eslint-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", + "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.0.0" + } + }, "eslint-visitor-keys": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", @@ -4496,13 +4788,14 @@ "dev": true }, "espree": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", - "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.1.tgz", + "integrity": "sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ==", "dev": true, "requires": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" + "acorn": "^7.0.0", + "acorn-jsx": "^5.0.2", + "eslint-visitor-keys": "^1.1.0" } }, "esprima": { @@ -4814,13 +5107,13 @@ } }, "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", "tmp": "^0.0.33" } }, @@ -5070,13 +5363,12 @@ } }, "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", "dev": true, "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" + "flat-cache": "^2.0.1" } }, "file-type": { @@ -5279,15 +5571,14 @@ "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==" }, "flat-cache": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", - "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", "dev": true, "requires": { - "circular-json": "^0.3.1", - "graceful-fs": "^4.1.2", - "rimraf": "~2.6.2", - "write": "^0.2.1" + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" }, "dependencies": { "rimraf": { @@ -5301,6 +5592,12 @@ } } }, + "flatted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "dev": true + }, "flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", @@ -7008,6 +7305,24 @@ "svgo": "^1.0.5" } }, + "import-fresh": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", + "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, "import-lazy": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-3.1.0.tgz", @@ -7158,24 +7473,23 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, "inquirer": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", - "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", "cli-cursor": "^2.1.0", "cli-width": "^2.0.0", - "external-editor": "^2.0.4", + "external-editor": "^3.0.3", "figures": "^2.0.0", - "lodash": "^4.3.0", + "lodash": "^4.17.12", "mute-stream": "0.0.7", "run-async": "^2.2.0", - "rx-lite": "^4.0.8", - "rx-lite-aggregates": "^4.0.8", + "rxjs": "^6.4.0", "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", + "strip-ansi": "^5.1.0", "through": "^2.3.6" }, "dependencies": { @@ -7208,15 +7522,34 @@ "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } } }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } } } } @@ -7560,12 +7893,6 @@ "is-unc-path": "^1.0.0" } }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, "is-retry-allowed": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", @@ -9095,8 +9422,7 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "optional": true + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "nise": { "version": "1.5.2", @@ -9546,6 +9872,18 @@ "isobject": "^3.0.0" } }, + "object.entries": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", + "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, "object.getownpropertydescriptors": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", @@ -9585,7 +9923,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", - "optional": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.12.0", @@ -9911,6 +10248,15 @@ } } }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parse-data-uri": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/parse-data-uri/-/parse-data-uri-0.2.0.tgz", @@ -10249,12 +10595,6 @@ "irregular-plurals": "^2.0.0" } }, - "pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", - "dev": true - }, "pngjs": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", @@ -10454,11 +10794,10 @@ } }, "ramda": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.25.0.tgz", - "integrity": "sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ==", - "dev": true, - "optional": true + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", + "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==", + "dev": true }, "range-parser": { "version": "1.2.1", @@ -10603,9 +10942,9 @@ "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" }, "regexpp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", - "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, "regexpu-core": { @@ -10800,24 +11139,6 @@ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true - } - } - }, "require_optional": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", @@ -10943,19 +11264,13 @@ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==" }, - "rx-lite": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", - "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", - "dev": true - }, - "rx-lite-aggregates": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", - "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "rxjs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", + "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", "dev": true, "requires": { - "rx-lite": "*" + "tslib": "^1.9.0" } }, "safe-buffer": { @@ -11256,14 +11571,25 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" }, "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", "dev": true, "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", "is-fullwidth-code-point": "^2.0.0" }, "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -11949,41 +12275,21 @@ } }, "table": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", - "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", "dev": true, "requires": { - "ajv": "^5.2.3", - "ajv-keywords": "^2.1.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" }, "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "is-fullwidth-code-point": { @@ -11992,29 +12298,24 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { + "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "strip-ansi": "^5.1.0" } }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^4.1.0" } } } @@ -12303,6 +12604,12 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "dev": true + }, "tunnel-agent": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", @@ -12870,6 +13177,12 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, "v8flags": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz", @@ -13059,6 +13372,58 @@ } } }, + "vue-eslint-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz", + "integrity": "sha512-JlHVZwBBTNVvzmifwjpZYn0oPWH2SgWv5dojlZBsrhablDu95VFD+hriB1rQGwbD+bms6g+rAFhQHk6+NyiS6g==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "eslint-scope": "^4.0.0", + "eslint-visitor-keys": "^1.0.0", + "espree": "^4.1.0", + "esquery": "^1.0.1", + "lodash": "^4.17.11" + }, + "dependencies": { + "acorn": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", + "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "espree": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", + "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", + "dev": true, + "requires": { + "acorn": "^6.0.2", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + } + } + }, "w3counter": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/w3counter/-/w3counter-3.0.1.tgz", @@ -13228,9 +13593,9 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", "dev": true, "requires": { "mkdirp": "^0.5.1" diff --git a/package.json b/package.json index bd64a44378..bdb0af63a1 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "npm": "^6" }, "scripts": { - "lint": "eslint --ext .js . && cd website/client && npm run lint", + "lint": "eslint --ext .js ./website/common --fix", "test": "npm run lint && gulp test && gulp apidoc", "test:build": "gulp test:prepare:build", "test:api-v3": "gulp test:api-v3", @@ -101,8 +101,8 @@ "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "chalk": "^2.4.1", - "eslint": "^4.19.1", - "eslint-config-habitrpg": "^4.0.0", + "eslint": "^6.5.1", + "eslint-config-habitrpg": "^6.0.3", "eslint-plugin-mocha": "^5.0.0", "expect.js": "^0.3.1", "istanbul": "^1.1.0-alpha.1", diff --git a/scripts/gdpr-delete-users.js b/scripts/gdpr-delete-users.js index c60c16d92f..91dd5af506 100644 --- a/scripts/gdpr-delete-users.js +++ b/scripts/gdpr-delete-users.js @@ -1,13 +1,13 @@ /* eslint-disable no-console */ import axios from 'axios'; -import { model as User } from '../website/server/models/user'; import nconf from 'nconf'; +import { model as User } from '../website/server/models/user'; const AMPLITUDE_KEY = nconf.get('AMPLITUDE_KEY'); const AMPLITUDE_SECRET = nconf.get('AMPLITUDE_SECRET'); const BASE_URL = nconf.get('BASE_URL'); -async function _deleteAmplitudeData (userId, email) { +async function deleteAmplitudeData (userId, email) { const response = await axios.post( 'https://amplitude.com/api/2/deletions/users', { @@ -19,22 +19,24 @@ async function _deleteAmplitudeData (userId, email) { username: AMPLITUDE_KEY, password: AMPLITUDE_SECRET, }, - } - ).catch((err) => { + }, + ).catch(err => { console.log(err.response.data); }); if (response) console.log(`${response.status} ${response.statusText}`); } -async function _deleteHabiticaData (user, email) { +async function deleteHabiticaData (user, email) { await User.update( - {_id: user._id}, - {$set: { - 'auth.local.email': email, - 'auth.local.hashed_password': '$2a$10$QDnNh1j1yMPnTXDEOV38xOePEWFd4X8DSYwAM8XTmqmacG5X0DKjW', - 'auth.local.passwordHashMethod': 'bcrypt', - }} + { _id: user._id }, + { + $set: { + 'auth.local.email': email, + 'auth.local.hashed_password': '$2a$10$QDnNh1j1yMPnTXDEOV38xOePEWFd4X8DSYwAM8XTmqmacG5X0DKjW', + 'auth.local.passwordHashMethod': 'bcrypt', + }, + }, ); const response = await axios.delete( `${BASE_URL}/api/v3/user`, @@ -46,8 +48,8 @@ async function _deleteHabiticaData (user, email) { 'x-api-user': user._id, 'x-api-key': user.apiToken, }, - } - ).catch((err) => { + }, + ).catch(err => { console.log(err.response.data); }); @@ -57,14 +59,15 @@ async function _deleteHabiticaData (user, email) { } } -async function _processEmailAddress (email) { +async function processEmailAddress (email) { const emailRegex = new RegExp(`^${email}$`, 'i'); const users = await User.find({ $or: [ - {'auth.local.email': emailRegex}, - {'auth.facebook.emails.value': emailRegex}, - {'auth.google.emails.value': emailRegex}, - ]}, + { 'auth.local.email': emailRegex }, + { 'auth.facebook.emails.value': emailRegex }, + { 'auth.google.emails.value': emailRegex }, + ], + }, { _id: 1, apiToken: 1, @@ -75,14 +78,14 @@ async function _processEmailAddress (email) { console.log(`No users found with email address ${email}`); } else { for (const user of users) { - await _deleteAmplitudeData(user._id, email); // eslint-disable-line no-await-in-loop - await _deleteHabiticaData(user, email); // eslint-disable-line no-await-in-loop + await deleteAmplitudeData(user._id, email); // eslint-disable-line no-await-in-loop + await deleteHabiticaData(user, email); // eslint-disable-line no-await-in-loop } } } function deleteUserData (emails) { - const emailPromises = emails.map(_processEmailAddress); + const emailPromises = emails.map(processEmailAddress); return Promise.all(emailPromises); } diff --git a/scripts/paypalBillingSetup.js b/scripts/paypalBillingSetup.js index e99ffe31b8..2ef55af823 100644 --- a/scripts/paypalBillingSetup.js +++ b/scripts/paypalBillingSetup.js @@ -12,11 +12,12 @@ const nconf = require('nconf'); const _ = require('lodash'); const paypal = require('paypal-rest-sdk'); const blocks = require('../website/common').content.subscriptionBlocks; + const live = nconf.get('PAYPAL_MODE') === 'live'; nconf.argv().env().file('user', path.join(path.resolve(__dirname, '../config.json'))); -let OP = 'create'; // list get update create create-webprofile +const OP = 'create'; // list get update create create-webprofile paypal.configure({ mode: nconf.get('PAYPAL_MODE'), // sandbox or live @@ -25,8 +26,8 @@ paypal.configure({ }); // https://developer.paypal.com/docs/api/#billing-plans-and-agreements -let billingPlanTitle = 'Habitica Subscription'; -let billingPlanAttributes = { +const billingPlanTitle = 'Habitica Subscription'; +const billingPlanAttributes = { description: billingPlanTitle, type: 'INFINITE', merchant_preferences: { @@ -41,7 +42,7 @@ let billingPlanAttributes = { }], }; -_.each(blocks, (block) => { +_.each(blocks, block => { block.definition = _.cloneDeep(billingPlanAttributes); _.merge(block.definition.payment_definitions[0], { name: `${billingPlanTitle} ($${block.price} every ${block.months} months, recurring)`, @@ -57,17 +58,17 @@ _.each(blocks, (block) => { switch (OP) { case 'list': - paypal.billingPlan.list({status: 'ACTIVE'}, (err, plans) => { - console.log({err, plans}); + paypal.billingPlan.list({ status: 'ACTIVE' }, (err, plans) => { + console.log({ err, plans }); }); break; case 'get': paypal.billingPlan.get(nconf.get('PAYPAL_BILLING_PLANS_basic_12mo'), (err, plan) => { - console.log({err, plan}); + console.log({ err, plan }); }); break; case 'update': - let updatePayload = { + const updatePayload = { op: 'replace', path: '/merchant_preferences', value: { @@ -75,7 +76,7 @@ switch (OP) { }, }; paypal.billingPlan.update(nconf.get('PAYPAL_BILLING_PLANS_basic_12mo'), updatePayload, (err, res) => { - console.log({err, plan: res}); + console.log({ err, plan: res }); }); break; case 'create': @@ -83,10 +84,10 @@ switch (OP) { if (err) return console.log(err); if (plan.state === 'ACTIVE') { - return console.log({err, plan}); + return console.log({ err, plan }); } - let billingPlanUpdateAttributes = [{ + const billingPlanUpdateAttributes = [{ op: 'replace', path: '/', value: { @@ -96,12 +97,12 @@ switch (OP) { // Activate the plan by changing status to Active paypal.billingPlan.update(plan.id, billingPlanUpdateAttributes, (err2, response) => { - console.log({err: err2, response, id: plan.id}); + console.log({ err: err2, response, id: plan.id }); }); }); break; case 'create-webprofile': - let webexpinfo = { + const webexpinfo = { name: 'HabiticaProfile', input_fields: { no_shipping: 1, diff --git a/website/common/index.js b/website/common/index.js index 04189fa8ab..c4fb1693bd 100644 --- a/website/common/index.js +++ b/website/common/index.js @@ -1,4 +1,3 @@ -'use strict'; let pathToCommon; diff --git a/website/common/script/.eslintrc b/website/common/script/.eslintrc index 4acd626597..05ff6f5ec3 100644 --- a/website/common/script/.eslintrc +++ b/website/common/script/.eslintrc @@ -1,6 +1,5 @@ { "extends": [ - "habitrpg/browser", - "habitrpg/esnext" + "habitrpg/lib/node", ] } diff --git a/website/common/script/constants.js b/website/common/script/constants.js index 8985bea89e..758d12c719 100644 --- a/website/common/script/constants.js +++ b/website/common/script/constants.js @@ -16,8 +16,8 @@ export const CHAT_FLAG_FROM_SHADOW_MUTE = 10; // a shadow-muted user's post star // @TODO use those constants to replace hard-coded numbers export const SUPPORTED_SOCIAL_NETWORKS = [ - {key: 'facebook', name: 'Facebook'}, - {key: 'google', name: 'Google'}, + { key: 'facebook', name: 'Facebook' }, + { key: 'google', name: 'Google' }, ]; export const GUILDS_PER_PAGE = 30; // number of guilds to return per page when using pagination diff --git a/website/common/script/content/achievements.js b/website/common/script/content/achievements.js index 2f29fe2327..1e8fef8972 100644 --- a/website/common/script/content/achievements.js +++ b/website/common/script/content/achievements.js @@ -1,8 +1,8 @@ import each from 'lodash/each'; -let achievementsData = {}; +const achievementsData = {}; -let worldQuestAchievs = { +const worldQuestAchievs = { dilatoryQuest: { icon: 'achievement-dilatory', titleKey: 'achievementDilatory', @@ -31,7 +31,7 @@ let worldQuestAchievs = { }; Object.assign(achievementsData, worldQuestAchievs); -let seasonalSpellAchievs = { +const seasonalSpellAchievs = { snowball: { icon: 'achievement-snowball', titleKey: 'annoyingFriends', @@ -55,7 +55,7 @@ let seasonalSpellAchievs = { }; Object.assign(achievementsData, seasonalSpellAchievs); -let masterAchievs = { +const masterAchievs = { beastMaster: { icon: 'achievement-rat', titleKey: 'beastMasterName', @@ -77,7 +77,7 @@ let masterAchievs = { }; Object.assign(achievementsData, masterAchievs); -let basicAchievs = { +const basicAchievs = { partyUp: { icon: 'achievement-partyUp', titleKey: 'partyUpName', @@ -160,7 +160,7 @@ let basicAchievs = { }; Object.assign(achievementsData, basicAchievs); -let specialAchievs = { +const specialAchievs = { contributor: { icon: 'achievement-boot', titleKey: 'contribName', @@ -201,7 +201,7 @@ let specialAchievs = { }; Object.assign(achievementsData, specialAchievs); -let holidayAchievs = { +const holidayAchievs = { habiticaDays: { icon: 'achievement-habiticaDay', singularTitleKey: 'habiticaDay', @@ -226,7 +226,7 @@ let holidayAchievs = { }; Object.assign(achievementsData, holidayAchievs); -let ultimateGearAchievs = ['healer', 'rogue', 'warrior', 'mage'].reduce((achievs, type) => { +const ultimateGearAchievs = ['healer', 'rogue', 'warrior', 'mage'].reduce((achievs, type) => { achievs[`${type}UltimateGear`] = { icon: `achievement-ultimate-${type}`, titleKey: 'ultimGearName', @@ -236,7 +236,7 @@ let ultimateGearAchievs = ['healer', 'rogue', 'warrior', 'mage'].reduce((achievs }, {}); Object.assign(achievementsData, ultimateGearAchievs); -let cardAchievs = ['greeting', 'thankyou', 'nye', 'valentine', 'birthday', 'congrats', 'getwell', 'goodluck'].reduce((achievs, type) => { +const cardAchievs = ['greeting', 'thankyou', 'nye', 'valentine', 'birthday', 'congrats', 'getwell', 'goodluck'].reduce((achievs, type) => { achievs[`${type}Cards`] = { icon: `achievement-${type}`, titleKey: `${type}CardAchievementTitle`, diff --git a/website/common/script/content/appearance/backgrounds.js b/website/common/script/content/appearance/backgrounds.js index 15a06aa79e..7686f95ee9 100644 --- a/website/common/script/content/appearance/backgrounds.js +++ b/website/common/script/content/appearance/backgrounds.js @@ -2,7 +2,7 @@ import forOwn from 'lodash/forOwn'; import t from '../translation'; /* eslint-disable camelcase */ -let backgrounds = { +const backgrounds = { backgrounds062014: { beach: { text: t('backgroundBeachText'), @@ -948,10 +948,10 @@ let backgrounds = { }; /* eslint-enable quote-props */ -let flat = {}; +const flat = {}; -forOwn(backgrounds, function prefillBackgroundSet (backgroundsInSet, set) { - forOwn(backgroundsInSet, function prefillBackground (background, bgKey) { +forOwn(backgrounds, (backgroundsInSet, set) => { + forOwn(backgroundsInSet, (background, bgKey) => { background.key = bgKey; background.set = set; background.price = 7; diff --git a/website/common/script/content/appearance/hair/base.js b/website/common/script/content/appearance/hair/base.js index 8075f57df4..f399c7d0ea 100644 --- a/website/common/script/content/appearance/hair/base.js +++ b/website/common/script/content/appearance/hair/base.js @@ -4,23 +4,23 @@ import sets from '../sets.js'; export default prefill({ 0: {}, 1: {}, - 2: {price: 2, set: sets.baseHair1}, + 2: { price: 2, set: sets.baseHair1 }, 3: {}, - 4: {price: 2, set: sets.baseHair1}, - 5: {price: 2, set: sets.baseHair1}, - 6: {price: 2, set: sets.baseHair1}, - 7: {price: 2, set: sets.baseHair1}, - 8: {price: 2, set: sets.baseHair1}, - 9: {price: 2, set: sets.baseHair2}, - 10: {price: 2, set: sets.baseHair2}, - 11: {price: 2, set: sets.baseHair2}, - 12: {price: 2, set: sets.baseHair2}, - 13: {price: 2, set: sets.baseHair2}, - 14: {price: 2, set: sets.baseHair2}, - 15: {price: 2, set: sets.baseHair3}, - 16: {price: 2, set: sets.baseHair3}, - 17: {price: 2, set: sets.baseHair3}, - 18: {price: 2, set: sets.baseHair3}, - 19: {price: 2, set: sets.baseHair3}, - 20: {price: 2, set: sets.baseHair3}, + 4: { price: 2, set: sets.baseHair1 }, + 5: { price: 2, set: sets.baseHair1 }, + 6: { price: 2, set: sets.baseHair1 }, + 7: { price: 2, set: sets.baseHair1 }, + 8: { price: 2, set: sets.baseHair1 }, + 9: { price: 2, set: sets.baseHair2 }, + 10: { price: 2, set: sets.baseHair2 }, + 11: { price: 2, set: sets.baseHair2 }, + 12: { price: 2, set: sets.baseHair2 }, + 13: { price: 2, set: sets.baseHair2 }, + 14: { price: 2, set: sets.baseHair2 }, + 15: { price: 2, set: sets.baseHair3 }, + 16: { price: 2, set: sets.baseHair3 }, + 17: { price: 2, set: sets.baseHair3 }, + 18: { price: 2, set: sets.baseHair3 }, + 19: { price: 2, set: sets.baseHair3 }, + 20: { price: 2, set: sets.baseHair3 }, }); diff --git a/website/common/script/content/appearance/hair/beard.js b/website/common/script/content/appearance/hair/beard.js index 120b3358b5..544227e7fc 100644 --- a/website/common/script/content/appearance/hair/beard.js +++ b/website/common/script/content/appearance/hair/beard.js @@ -3,7 +3,7 @@ import prefill from '../prefill.js'; export default prefill({ 0: {}, - 1: {price: 2, set: sets.facialHair}, - 2: {price: 2, set: sets.facialHair}, - 3: {price: 2, set: sets.facialHair}, + 1: { price: 2, set: sets.facialHair }, + 2: { price: 2, set: sets.facialHair }, + 3: { price: 2, set: sets.facialHair }, }); diff --git a/website/common/script/content/appearance/hair/color.js b/website/common/script/content/appearance/hair/color.js index 467da5b165..69398d2fcd 100644 --- a/website/common/script/content/appearance/hair/color.js +++ b/website/common/script/content/appearance/hair/color.js @@ -8,43 +8,43 @@ export default prefill({ red: {}, black: {}, - candycane: {price: 2, set: sets.winterHairColors}, - frost: {price: 2, set: sets.winterHairColors}, - winternight: {price: 2, set: sets.winterHairColors}, - holly: {price: 2, set: sets.winterHairColors}, + candycane: { price: 2, set: sets.winterHairColors }, + frost: { price: 2, set: sets.winterHairColors }, + winternight: { price: 2, set: sets.winterHairColors }, + holly: { price: 2, set: sets.winterHairColors }, - pblue: {price: 2, set: sets.pastelHairColors}, - pgreen: {price: 2, set: sets.pastelHairColors}, - porange: {price: 2, set: sets.pastelHairColors}, - ppink: {price: 2, set: sets.pastelHairColors}, - ppurple: {price: 2, set: sets.pastelHairColors}, - pyellow: {price: 2, set: sets.pastelHairColors}, + pblue: { price: 2, set: sets.pastelHairColors }, + pgreen: { price: 2, set: sets.pastelHairColors }, + porange: { price: 2, set: sets.pastelHairColors }, + ppink: { price: 2, set: sets.pastelHairColors }, + ppurple: { price: 2, set: sets.pastelHairColors }, + pyellow: { price: 2, set: sets.pastelHairColors }, - rainbow: {price: 2, set: sets.rainbowHairColors}, - yellow: {price: 2, set: sets.rainbowHairColors}, - green: {price: 2, set: sets.rainbowHairColors}, - purple: {price: 2, set: sets.rainbowHairColors}, - blue: {price: 2, set: sets.rainbowHairColors}, - TRUred: {price: 2, set: sets.rainbowHairColors}, + rainbow: { price: 2, set: sets.rainbowHairColors }, + yellow: { price: 2, set: sets.rainbowHairColors }, + green: { price: 2, set: sets.rainbowHairColors }, + purple: { price: 2, set: sets.rainbowHairColors }, + blue: { price: 2, set: sets.rainbowHairColors }, + TRUred: { price: 2, set: sets.rainbowHairColors }, - pblue2: {price: 2, set: sets.shimmerHairColors}, - pgreen2: {price: 2, set: sets.shimmerHairColors}, - porange2: {price: 2, set: sets.shimmerHairColors}, - ppink2: {price: 2, set: sets.shimmerHairColors}, - ppurple2: {price: 2, set: sets.shimmerHairColors}, - pyellow2: {price: 2, set: sets.shimmerHairColors}, + pblue2: { price: 2, set: sets.shimmerHairColors }, + pgreen2: { price: 2, set: sets.shimmerHairColors }, + porange2: { price: 2, set: sets.shimmerHairColors }, + ppink2: { price: 2, set: sets.shimmerHairColors }, + ppurple2: { price: 2, set: sets.shimmerHairColors }, + pyellow2: { price: 2, set: sets.shimmerHairColors }, - candycorn: {price: 2, set: sets.hauntedHairColors}, - ghostwhite: {price: 2, set: sets.hauntedHairColors}, - halloween: {price: 2, set: sets.hauntedHairColors}, - midnight: {price: 2, set: sets.hauntedHairColors}, - pumpkin: {price: 2, set: sets.hauntedHairColors}, - zombie: {price: 2, set: sets.hauntedHairColors}, + candycorn: { price: 2, set: sets.hauntedHairColors }, + ghostwhite: { price: 2, set: sets.hauntedHairColors }, + halloween: { price: 2, set: sets.hauntedHairColors }, + midnight: { price: 2, set: sets.hauntedHairColors }, + pumpkin: { price: 2, set: sets.hauntedHairColors }, + zombie: { price: 2, set: sets.hauntedHairColors }, - aurora: {price: 2, set: sets.winteryHairColors}, - festive: {price: 2, set: sets.winteryHairColors}, - hollygreen: {price: 2, set: sets.winteryHairColors}, - peppermint: {price: 2, set: sets.winteryHairColors}, - snowy: {price: 2, set: sets.winteryHairColors}, - winterstar: {price: 2, set: sets.winteryHairColors}, + aurora: { price: 2, set: sets.winteryHairColors }, + festive: { price: 2, set: sets.winteryHairColors }, + hollygreen: { price: 2, set: sets.winteryHairColors }, + peppermint: { price: 2, set: sets.winteryHairColors }, + snowy: { price: 2, set: sets.winteryHairColors }, + winterstar: { price: 2, set: sets.winteryHairColors }, }); diff --git a/website/common/script/content/appearance/hair/mustache.js b/website/common/script/content/appearance/hair/mustache.js index 5f13d0f962..6e382e5e8b 100644 --- a/website/common/script/content/appearance/hair/mustache.js +++ b/website/common/script/content/appearance/hair/mustache.js @@ -3,6 +3,6 @@ import prefill from '../prefill.js'; export default prefill({ 0: {}, - 1: {price: 2, set: sets.facialHair}, - 2: {price: 2, set: sets.facialHair}, + 1: { price: 2, set: sets.facialHair }, + 2: { price: 2, set: sets.facialHair }, }); diff --git a/website/common/script/content/appearance/hair/top-hair.js b/website/common/script/content/appearance/hair/top-hair.js index 504a020669..6b9dcf0b36 100644 --- a/website/common/script/content/appearance/hair/top-hair.js +++ b/website/common/script/content/appearance/hair/top-hair.js @@ -3,10 +3,10 @@ import sets from '../sets.js'; export default prefill({ 0: {}, - 1: {price: 2, set: sets.topHair}, - 2: {price: 2, set: sets.topHair}, - 3: {price: 2, set: sets.topHair}, - 4: {price: 2, set: sets.topHair}, - 5: {price: 2, set: sets.topHair}, - 6: {price: 2, set: sets.topHair}, + 1: { price: 2, set: sets.topHair }, + 2: { price: 2, set: sets.topHair }, + 3: { price: 2, set: sets.topHair }, + 4: { price: 2, set: sets.topHair }, + 5: { price: 2, set: sets.topHair }, + 6: { price: 2, set: sets.topHair }, }); diff --git a/website/common/script/content/appearance/index.js b/website/common/script/content/appearance/index.js index 7564cbf2fd..d6fd8f9614 100644 --- a/website/common/script/content/appearance/index.js +++ b/website/common/script/content/appearance/index.js @@ -1,17 +1,17 @@ +import forOwn from 'lodash/forOwn'; +import clone from 'lodash/clone'; import hair from './hair'; import shirts from './shirt.js'; import skins from './skin.js'; import sizes from './size.js'; import backgrounds from './backgrounds.js'; import chairs from './chair.js'; -import forOwn from 'lodash/forOwn'; -import clone from 'lodash/clone'; -let reorderedBgs = {}; +const reorderedBgs = {}; -forOwn(backgrounds, function restructureBackgroundSet (value, key) { - forOwn(value, function restructureBackground (bgObject, bgKey) { - let bg = clone(bgObject); +forOwn(backgrounds, (value, key) => { + forOwn(value, (bgObject, bgKey) => { + const bg = clone(bgObject); bg.set = { text: key, key, @@ -22,7 +22,7 @@ forOwn(backgrounds, function restructureBackgroundSet (value, key) { }); -let appearances = { +const appearances = { hair, shirt: shirts, size: sizes, diff --git a/website/common/script/content/appearance/prefill.js b/website/common/script/content/appearance/prefill.js index badfaaf295..8823a36fd3 100644 --- a/website/common/script/content/appearance/prefill.js +++ b/website/common/script/content/appearance/prefill.js @@ -1,7 +1,7 @@ import forOwn from 'lodash/forOwn'; export default function prefillAppearances (obj) { - forOwn(obj, function prefillAppearance (value, key) { + forOwn(obj, (value, key) => { value.key = key; if (!value.price) { value.price = 0; diff --git a/website/common/script/content/appearance/sets.js b/website/common/script/content/appearance/sets.js index 31ed3261f3..d26769d271 100644 --- a/website/common/script/content/appearance/sets.js +++ b/website/common/script/content/appearance/sets.js @@ -2,22 +2,36 @@ import t from '../translation'; import prefill from './prefill.js'; export default prefill({ - baseHair1: {setPrice: 5, text: t('hairSet1')}, - baseHair2: {setPrice: 5, text: t('hairSet2')}, - baseHair3: {setPrice: 5, text: t('hairSet3')}, - facialHair: {setPrice: 5, text: t('bodyFacialHair')}, - specialShirts: {setPrice: 5, text: t('specialShirts')}, - winterHairColors: {setPrice: 5, availableUntil: '2016-01-01'}, - pastelHairColors: {setPrice: 5, availableUntil: '2016-01-01'}, - rainbowHairColors: {setPrice: 5, text: t('rainbowColors')}, - shimmerHairColors: {setPrice: 5, availableFrom: '2019-04-09', availableUntil: '2019-05-02', text: t('shimmerColors')}, - hauntedHairColors: {setPrice: 5, availableFrom: '2018-10-11', availableUntil: '2018-11-02', text: t('hauntedColors')}, - winteryHairColors: {setPrice: 5, availableFrom: '2019-01-08', availableUntil: '2019-02-02', text: t('winteryColors')}, - rainbowSkins: {setPrice: 5, text: t('rainbowSkins')}, - animalSkins: {setPrice: 5, text: t('animalSkins')}, - pastelSkins: {setPrice: 5, availableFrom: '2019-04-09', availableUntil: '2019-05-02', text: t('pastelSkins')}, - spookySkins: {setPrice: 5, availableUntil: '2016-01-01', text: t('spookySkins')}, - supernaturalSkins: {setPrice: 5, availableFrom: '2018-10-11', availableUntil: '2018-11-02', text: t('supernaturalSkins')}, - splashySkins: {setPrice: 5, availableFrom: '2019-07-02', availableUntil: '2019-08-02', text: t('splashySkins')}, - winterySkins: {setPrice: 5, availableFrom: '2019-01-08', availableUntil: '2019-02-02', text: t('winterySkins')}, + baseHair1: { setPrice: 5, text: t('hairSet1') }, + baseHair2: { setPrice: 5, text: t('hairSet2') }, + baseHair3: { setPrice: 5, text: t('hairSet3') }, + facialHair: { setPrice: 5, text: t('bodyFacialHair') }, + specialShirts: { setPrice: 5, text: t('specialShirts') }, + winterHairColors: { setPrice: 5, availableUntil: '2016-01-01' }, + pastelHairColors: { setPrice: 5, availableUntil: '2016-01-01' }, + rainbowHairColors: { setPrice: 5, text: t('rainbowColors') }, + shimmerHairColors: { + setPrice: 5, availableFrom: '2019-04-09', availableUntil: '2019-05-02', text: t('shimmerColors'), + }, + hauntedHairColors: { + setPrice: 5, availableFrom: '2018-10-11', availableUntil: '2018-11-02', text: t('hauntedColors'), + }, + winteryHairColors: { + setPrice: 5, availableFrom: '2019-01-08', availableUntil: '2019-02-02', text: t('winteryColors'), + }, + rainbowSkins: { setPrice: 5, text: t('rainbowSkins') }, + animalSkins: { setPrice: 5, text: t('animalSkins') }, + pastelSkins: { + setPrice: 5, availableFrom: '2019-04-09', availableUntil: '2019-05-02', text: t('pastelSkins'), + }, + spookySkins: { setPrice: 5, availableUntil: '2016-01-01', text: t('spookySkins') }, + supernaturalSkins: { + setPrice: 5, availableFrom: '2018-10-11', availableUntil: '2018-11-02', text: t('supernaturalSkins'), + }, + splashySkins: { + setPrice: 5, availableFrom: '2019-07-02', availableUntil: '2019-08-02', text: t('splashySkins'), + }, + winterySkins: { + setPrice: 5, availableFrom: '2019-01-08', availableUntil: '2019-02-02', text: t('winterySkins'), + }, }); diff --git a/website/common/script/content/appearance/shirt.js b/website/common/script/content/appearance/shirt.js index eac4ce0762..088113fa55 100644 --- a/website/common/script/content/appearance/shirt.js +++ b/website/common/script/content/appearance/shirt.js @@ -9,15 +9,15 @@ export default prefill({ white: {}, yellow: {}, - convict: {price: 2, set: sets.specialShirts}, - cross: {price: 2, set: sets.specialShirts}, - fire: {price: 2, set: sets.specialShirts}, - horizon: {price: 2, set: sets.specialShirts}, - ocean: {price: 2, set: sets.specialShirts}, - purple: {price: 2, set: sets.specialShirts}, - rainbow: {price: 2, set: sets.specialShirts}, - redblue: {price: 2, set: sets.specialShirts}, - thunder: {price: 2, set: sets.specialShirts}, - tropical: {price: 2, set: sets.specialShirts}, - zombie: {price: 2, set: sets.specialShirts}, + convict: { price: 2, set: sets.specialShirts }, + cross: { price: 2, set: sets.specialShirts }, + fire: { price: 2, set: sets.specialShirts }, + horizon: { price: 2, set: sets.specialShirts }, + ocean: { price: 2, set: sets.specialShirts }, + purple: { price: 2, set: sets.specialShirts }, + rainbow: { price: 2, set: sets.specialShirts }, + redblue: { price: 2, set: sets.specialShirts }, + thunder: { price: 2, set: sets.specialShirts }, + tropical: { price: 2, set: sets.specialShirts }, + zombie: { price: 2, set: sets.specialShirts }, }); diff --git a/website/common/script/content/appearance/skin.js b/website/common/script/content/appearance/skin.js index 4e7a8521af..8a9ca9dfe2 100644 --- a/website/common/script/content/appearance/skin.js +++ b/website/common/script/content/appearance/skin.js @@ -12,66 +12,66 @@ export default prefill({ 'c3e1dc': {}, '6bd049': {}, - 'eb052b': {price: 2, set: sets.rainbowSkins}, - 'f69922': {price: 2, set: sets.rainbowSkins}, - 'f5d70f': {price: 2, set: sets.rainbowSkins}, - '0ff591': {price: 2, set: sets.rainbowSkins}, - '2b43f6': {price: 2, set: sets.rainbowSkins}, - 'd7a9f7': {price: 2, set: sets.rainbowSkins}, - '800ed0': {price: 2, set: sets.rainbowSkins}, - 'rainbow': {price: 2, set: sets.rainbowSkins}, + 'eb052b': { price: 2, set: sets.rainbowSkins }, + 'f69922': { price: 2, set: sets.rainbowSkins }, + 'f5d70f': { price: 2, set: sets.rainbowSkins }, + '0ff591': { price: 2, set: sets.rainbowSkins }, + '2b43f6': { price: 2, set: sets.rainbowSkins }, + 'd7a9f7': { price: 2, set: sets.rainbowSkins }, + '800ed0': { price: 2, set: sets.rainbowSkins }, + 'rainbow': { price: 2, set: sets.rainbowSkins }, - 'bear': {price: 2, set: sets.animalSkins}, - 'cactus': {price: 2, set: sets.animalSkins}, - 'fox': {price: 2, set: sets.animalSkins}, - 'lion': {price: 2, set: sets.animalSkins}, - 'panda': {price: 2, set: sets.animalSkins}, - 'pig': {price: 2, set: sets.animalSkins}, - 'tiger': {price: 2, set: sets.animalSkins}, - 'wolf': {price: 2, set: sets.animalSkins}, + 'bear': { price: 2, set: sets.animalSkins }, + 'cactus': { price: 2, set: sets.animalSkins }, + 'fox': { price: 2, set: sets.animalSkins }, + 'lion': { price: 2, set: sets.animalSkins }, + 'panda': { price: 2, set: sets.animalSkins }, + 'pig': { price: 2, set: sets.animalSkins }, + 'tiger': { price: 2, set: sets.animalSkins }, + 'wolf': { price: 2, set: sets.animalSkins }, - 'pastelPink': {price: 2, set: sets.pastelSkins}, - 'pastelOrange': {price: 2, set: sets.pastelSkins}, - 'pastelYellow': {price: 2, set: sets.pastelSkins}, - 'pastelGreen': {price: 2, set: sets.pastelSkins}, - 'pastelBlue': {price: 2, set: sets.pastelSkins}, - 'pastelPurple': {price: 2, set: sets.pastelSkins}, - 'pastelRainbowChevron': {price: 2, set: sets.pastelSkins}, - 'pastelRainbowDiagonal': {price: 2, set: sets.pastelSkins}, + 'pastelPink': { price: 2, set: sets.pastelSkins }, + 'pastelOrange': { price: 2, set: sets.pastelSkins }, + 'pastelYellow': { price: 2, set: sets.pastelSkins }, + 'pastelGreen': { price: 2, set: sets.pastelSkins }, + 'pastelBlue': { price: 2, set: sets.pastelSkins }, + 'pastelPurple': { price: 2, set: sets.pastelSkins }, + 'pastelRainbowChevron': { price: 2, set: sets.pastelSkins }, + 'pastelRainbowDiagonal': { price: 2, set: sets.pastelSkins }, - 'monster': {price: 2, set: sets.spookySkins}, - 'pumpkin': {price: 2, set: sets.spookySkins}, - 'skeleton': {price: 2, set: sets.spookySkins}, - 'zombie': {price: 2, set: sets.spookySkins}, - 'ghost': {price: 2, set: sets.spookySkins}, - 'shadow': {price: 2, set: sets.spookySkins}, + 'monster': { price: 2, set: sets.spookySkins }, + 'pumpkin': { price: 2, set: sets.spookySkins }, + 'skeleton': { price: 2, set: sets.spookySkins }, + 'zombie': { price: 2, set: sets.spookySkins }, + 'ghost': { price: 2, set: sets.spookySkins }, + 'shadow': { price: 2, set: sets.spookySkins }, - 'candycorn': {price: 2, set: sets.supernaturalSkins}, - 'ogre': {price: 2, set: sets.supernaturalSkins}, - 'pumpkin2': {price: 2, set: sets.supernaturalSkins}, - 'reptile': {price: 2, set: sets.supernaturalSkins}, - 'shadow2': {price: 2, set: sets.supernaturalSkins}, - 'skeleton2': {price: 2, set: sets.supernaturalSkins}, - 'transparent': {price: 2, set: sets.supernaturalSkins}, - 'zombie2': {price: 2, set: sets.supernaturalSkins}, + 'candycorn': { price: 2, set: sets.supernaturalSkins }, + 'ogre': { price: 2, set: sets.supernaturalSkins }, + 'pumpkin2': { price: 2, set: sets.supernaturalSkins }, + 'reptile': { price: 2, set: sets.supernaturalSkins }, + 'shadow2': { price: 2, set: sets.supernaturalSkins }, + 'skeleton2': { price: 2, set: sets.supernaturalSkins }, + 'transparent': { price: 2, set: sets.supernaturalSkins }, + 'zombie2': { price: 2, set: sets.supernaturalSkins }, - 'clownfish': {price: 2, set: sets.splashySkins}, - 'deepocean': {price: 2, set: sets.splashySkins}, - 'merblue': {price: 2, set: sets.splashySkins}, - 'mergold': {price: 2, set: sets.splashySkins}, - 'mergreen': {price: 2, set: sets.splashySkins}, - 'merruby': {price: 2, set: sets.splashySkins}, - 'shark': {price: 2, set: sets.splashySkins}, - 'tropicalwater': {price: 2, set: sets.splashySkins}, + 'clownfish': { price: 2, set: sets.splashySkins }, + 'deepocean': { price: 2, set: sets.splashySkins }, + 'merblue': { price: 2, set: sets.splashySkins }, + 'mergold': { price: 2, set: sets.splashySkins }, + 'mergreen': { price: 2, set: sets.splashySkins }, + 'merruby': { price: 2, set: sets.splashySkins }, + 'shark': { price: 2, set: sets.splashySkins }, + 'tropicalwater': { price: 2, set: sets.splashySkins }, - 'aurora': {price: 2, set: sets.winterySkins}, - 'dapper': {price: 2, set: sets.winterySkins}, - 'festive': {price: 2, set: sets.winterySkins}, - 'holly': {price: 2, set: sets.winterySkins}, - 'polar': {price: 2, set: sets.winterySkins}, - 'sugar': {price: 2, set: sets.winterySkins}, - 'snowy': {price: 2, set: sets.winterySkins}, - 'winterstar': {price: 2, set: sets.winterySkins}, + 'aurora': { price: 2, set: sets.winterySkins }, + 'dapper': { price: 2, set: sets.winterySkins }, + 'festive': { price: 2, set: sets.winterySkins }, + 'holly': { price: 2, set: sets.winterySkins }, + 'polar': { price: 2, set: sets.winterySkins }, + 'sugar': { price: 2, set: sets.winterySkins }, + 'snowy': { price: 2, set: sets.winterySkins }, + 'winterstar': { price: 2, set: sets.winterySkins }, /* eslint-enable quote-props */ }); diff --git a/website/common/script/content/constants.js b/website/common/script/content/constants.js index 5cc052e1b7..5ea01a931d 100644 --- a/website/common/script/content/constants.js +++ b/website/common/script/content/constants.js @@ -203,19 +203,19 @@ export const GEAR_TYPES = [ ]; export const ITEM_LIST = { - weapon: { localeKey: 'weapon', isEquipment: true }, - armor: { localeKey: 'armor', isEquipment: true }, - head: { localeKey: 'headgear', isEquipment: true }, - shield: { localeKey: 'offhand', isEquipment: true }, - back: { localeKey: 'back', isEquipment: true }, - body: { localeKey: 'body', isEquipment: true }, - headAccessory: { localeKey: 'headAccessory', isEquipment: true }, - eyewear: { localeKey: 'eyewear', isEquipment: true }, + weapon: { localeKey: 'weapon', isEquipment: true }, + armor: { localeKey: 'armor', isEquipment: true }, + head: { localeKey: 'headgear', isEquipment: true }, + shield: { localeKey: 'offhand', isEquipment: true }, + back: { localeKey: 'back', isEquipment: true }, + body: { localeKey: 'body', isEquipment: true }, + headAccessory: { localeKey: 'headAccessory', isEquipment: true }, + eyewear: { localeKey: 'eyewear', isEquipment: true }, hatchingPotions: { localeKey: 'hatchingPotion', isEquipment: false }, premiumHatchingPotions: { localeKey: 'hatchingPotion', isEquipment: false }, - eggs: { localeKey: 'eggSingular', isEquipment: false }, - quests: { localeKey: 'quest', isEquipment: false }, - food: { localeKey: 'foodTextThe', isEquipment: false }, + eggs: { localeKey: 'eggSingular', isEquipment: false }, + quests: { localeKey: 'quest', isEquipment: false }, + food: { localeKey: 'foodTextThe', isEquipment: false }, Saddle: { localeKey: 'foodSaddleText', isEquipment: false }, bundles: { localeKey: 'discountBundle', isEquipment: false }, }; @@ -264,6 +264,10 @@ export const QUEST_SERIES_ACHIEVEMENTS = { }; export const ANIMAL_COLOR_ACHIEVEMENTS = [ - {color: 'Base', petAchievement: 'backToBasics', petNotificationType: 'ACHIEVEMENT_BACK_TO_BASICS', mountAchievement: 'allYourBase', mountNotificationType: 'ACHIEVEMENT_ALL_YOUR_BASE'}, - {color: 'Desert', petAchievement: 'dustDevil', petNotificationType: 'ACHIEVEMENT_DUST_DEVIL', mountAchievement: 'aridAuthority', mountNotificationType: 'ACHIEVEMENT_ARID_AUTHORITY'}, + { + color: 'Base', petAchievement: 'backToBasics', petNotificationType: 'ACHIEVEMENT_BACK_TO_BASICS', mountAchievement: 'allYourBase', mountNotificationType: 'ACHIEVEMENT_ALL_YOUR_BASE', + }, + { + color: 'Desert', petAchievement: 'dustDevil', petNotificationType: 'ACHIEVEMENT_DUST_DEVIL', mountAchievement: 'aridAuthority', mountNotificationType: 'ACHIEVEMENT_ARID_AUTHORITY', + }, ]; diff --git a/website/common/script/content/eggs.js b/website/common/script/content/eggs.js index 9b0820b8e1..77ddd4a209 100644 --- a/website/common/script/content/eggs.js +++ b/website/common/script/content/eggs.js @@ -19,13 +19,11 @@ function applyEggDefaults (set, config) { } function hasQuestAchievementFunction (key) { - return (user) => { - return user.achievements.quests && - user.achievements.quests[key] > 0; - }; + return user => user.achievements.quests + && user.achievements.quests[key] > 0; } -let drops = { +const drops = { Wolf: { text: t('dropEggWolfText'), mountText: t('dropEggWolfMountText'), @@ -73,7 +71,7 @@ let drops = { }, }; -let quests = { +const quests = { Gryphon: { text: t('questEggGryphonText'), mountText: t('questEggGryphonMountText'), @@ -150,11 +148,11 @@ let quests = { mountText: t('questEggTRexMountText'), adjective: t('questEggTRexAdjective'), canBuy (user) { - let questAchievements = user.achievements.quests; + const questAchievements = user.achievements.quests; return questAchievements && ( - questAchievements.trex > 0 || - questAchievements.trex_undead > 0 + questAchievements.trex > 0 + || questAchievements.trex_undead > 0 ); }, }, @@ -406,7 +404,7 @@ applyEggDefaults(quests, { }, }); -let all = assign({}, drops, quests); +const all = assign({}, drops, quests); export { drops, diff --git a/website/common/script/content/faq.js b/website/common/script/content/faq.js index c1cb26bd2c..d63954e2d3 100644 --- a/website/common/script/content/faq.js +++ b/website/common/script/content/faq.js @@ -2,7 +2,7 @@ import t from './translation'; const NUMBER_OF_QUESTIONS = 12; -let faq = { +const faq = { questions: [], stillNeedHelp: { ios: t('iosFaqStillNeedHelp'), @@ -11,7 +11,7 @@ let faq = { }; for (let i = 0; i <= NUMBER_OF_QUESTIONS; i++) { - let question = { + const question = { question: t(`faqQuestion${i}`), ios: t(`iosFaqAnswer${i}`), android: t(`androidFaqAnswer${i}`), diff --git a/website/common/script/content/gear/armor.js b/website/common/script/content/gear/armor.js index 53a0a1c7bc..a090c56364 100644 --- a/website/common/script/content/gear/armor.js +++ b/website/common/script/content/gear/armor.js @@ -1,15 +1,15 @@ -import {armor as baseArmor} from './sets/base'; +import { armor as baseArmor } from './sets/base'; -import {armor as warriorArmor} from './sets/warrior'; -import {armor as rogueArmor} from './sets/rogue'; -import {armor as healerArmor} from './sets/healer'; -import {armor as wizardArmor} from './sets/wizard'; +import { armor as warriorArmor } from './sets/warrior'; +import { armor as rogueArmor } from './sets/rogue'; +import { armor as healerArmor } from './sets/healer'; +import { armor as wizardArmor } from './sets/wizard'; -import {armor as specialArmor} from './sets/special'; -import {armor as mysteryArmor} from './sets/mystery'; -import {armor as armoireArmor} from './sets/armoire'; +import { armor as specialArmor } from './sets/special'; +import { armor as mysteryArmor } from './sets/mystery'; +import { armor as armoireArmor } from './sets/armoire'; -let armor = { +const armor = { base: baseArmor, warrior: warriorArmor, diff --git a/website/common/script/content/gear/back.js b/website/common/script/content/gear/back.js index 7e0e6cf1bd..0cfe855b70 100644 --- a/website/common/script/content/gear/back.js +++ b/website/common/script/content/gear/back.js @@ -1,13 +1,12 @@ -import {back as baseBack} from './sets/base'; +import { back as baseBack } from './sets/base'; -import {back as mysteryBack} from './sets/mystery'; -import {back as specialBack} from './sets/special'; +import { back as mysteryBack } from './sets/mystery'; +import { back as specialBack } from './sets/special'; -let back = { +const back = { base: baseBack, mystery: mysteryBack, special: specialBack, }; export default back; - diff --git a/website/common/script/content/gear/body.js b/website/common/script/content/gear/body.js index b0ce7eff0a..19349eb2a2 100644 --- a/website/common/script/content/gear/body.js +++ b/website/common/script/content/gear/body.js @@ -1,10 +1,10 @@ -import {body as baseBody} from './sets/base'; +import { body as baseBody } from './sets/base'; -import {body as mysteryBody} from './sets/mystery'; -import {body as specialBody} from './sets/special'; -import {body as armoireBody} from './sets/armoire'; +import { body as mysteryBody } from './sets/mystery'; +import { body as specialBody } from './sets/special'; +import { body as armoireBody } from './sets/armoire'; -let body = { +const body = { base: baseBody, mystery: mysteryBody, special: specialBody, diff --git a/website/common/script/content/gear/eyewear.js b/website/common/script/content/gear/eyewear.js index 073b12ca41..2fdf54e079 100644 --- a/website/common/script/content/gear/eyewear.js +++ b/website/common/script/content/gear/eyewear.js @@ -1,10 +1,10 @@ -import {eyewear as baseEyewear} from './sets/base'; +import { eyewear as baseEyewear } from './sets/base'; -import {eyewear as armoireEyewear} from './sets/armoire'; -import {eyewear as mysteryEyewear} from './sets/mystery'; -import {eyewear as specialEyewear} from './sets/special'; +import { eyewear as armoireEyewear } from './sets/armoire'; +import { eyewear as mysteryEyewear } from './sets/mystery'; +import { eyewear as specialEyewear } from './sets/special'; -let eyewear = { +const eyewear = { base: baseEyewear, special: specialEyewear, mystery: mysteryEyewear, diff --git a/website/common/script/content/gear/gear-helper.js b/website/common/script/content/gear/gear-helper.js index 5319e1eed6..8acfad9297 100644 --- a/website/common/script/content/gear/gear-helper.js +++ b/website/common/script/content/gear/gear-helper.js @@ -1,7 +1,5 @@ import isBoolean from 'lodash/isBoolean'; export function ownsItem (item) { - return (user) => { - return item && isBoolean(user.items.gear.owned[item]); - }; + return user => item && isBoolean(user.items.gear.owned[item]); } diff --git a/website/common/script/content/gear/head-accessory.js b/website/common/script/content/gear/head-accessory.js index c820a95e9d..146c0e77b8 100644 --- a/website/common/script/content/gear/head-accessory.js +++ b/website/common/script/content/gear/head-accessory.js @@ -1,10 +1,10 @@ -import {headAccessory as baseHeadAccessory} from './sets/base'; +import { headAccessory as baseHeadAccessory } from './sets/base'; -import {headAccessory as specialHeadAccessory} from './sets/special'; -import {headAccessory as mysteryHeadAccessory} from './sets/mystery'; -import {headAccessory as armoireHeadAccessory} from './sets/armoire'; +import { headAccessory as specialHeadAccessory } from './sets/special'; +import { headAccessory as mysteryHeadAccessory } from './sets/mystery'; +import { headAccessory as armoireHeadAccessory } from './sets/armoire'; -let headAccessory = { +const headAccessory = { base: baseHeadAccessory, special: specialHeadAccessory, mystery: mysteryHeadAccessory, @@ -12,4 +12,3 @@ let headAccessory = { }; export default headAccessory; - diff --git a/website/common/script/content/gear/head.js b/website/common/script/content/gear/head.js index a5b550f8cc..6eee326efb 100644 --- a/website/common/script/content/gear/head.js +++ b/website/common/script/content/gear/head.js @@ -1,15 +1,15 @@ -import {head as baseHead} from './sets/base'; +import { head as baseHead } from './sets/base'; -import {head as healerHead} from './sets/healer'; -import {head as rogueHead} from './sets/rogue'; -import {head as warriorHead} from './sets/warrior'; -import {head as wizardHead} from './sets/wizard'; +import { head as healerHead } from './sets/healer'; +import { head as rogueHead } from './sets/rogue'; +import { head as warriorHead } from './sets/warrior'; +import { head as wizardHead } from './sets/wizard'; -import {head as armoireHead} from './sets/armoire'; -import {head as mysteryHead} from './sets/mystery'; -import {head as specialHead} from './sets/special'; +import { head as armoireHead } from './sets/armoire'; +import { head as mysteryHead } from './sets/mystery'; +import { head as specialHead } from './sets/special'; -let head = { +const head = { base: baseHead, warrior: warriorHead, diff --git a/website/common/script/content/gear/index.js b/website/common/script/content/gear/index.js index dc4c0e9f6a..2dc039b1c2 100644 --- a/website/common/script/content/gear/index.js +++ b/website/common/script/content/gear/index.js @@ -17,7 +17,7 @@ import body from './body'; import headAccessory from './head-accessory'; import eyewear from './eyewear'; -let gear = { +const gear = { weapon, armor, head, @@ -32,15 +32,15 @@ let gear = { The gear is exported as a tree (defined above), and a flat list (eg, {weapon_healer_1: .., shield_special_0: ...}) since they are needed in different forms at different points in the app */ -let flat = {}; +const flat = {}; -each(GEAR_TYPES, (type) => { - let allGearTypes = CLASSES.concat(['base', 'special', 'mystery', 'armoire']); +each(GEAR_TYPES, type => { + const allGearTypes = CLASSES.concat(['base', 'special', 'mystery', 'armoire']); - each(allGearTypes, (klass) => { + each(allGearTypes, klass => { each(gear[type][klass], (item, index) => { - let key = `${type}_${klass}_${index}`; - let set = `${klass}-${index}`; + const key = `${type}_${klass}_${index}`; + const set = `${klass}-${index}`; defaults(item, { type, @@ -52,21 +52,17 @@ each(GEAR_TYPES, (type) => { int: 0, per: 0, con: 0, - canBuy: () => { - return false; - }, + canBuy: () => false, }); if (item.event) { - let canOwnFuncTrue = () => { - return true; - }; - let _canOwn = item.canOwn || canOwnFuncTrue; + const canOwnFuncTrue = () => true; + const _canOwn = item.canOwn || canOwnFuncTrue; - item.canOwn = (user) => { - let userHasOwnedItem = ownsItem(key)(user); - let eventIsCurrent = moment().isAfter(item.event.start) && moment().isBefore(item.event.end); - let compatibleWithUserClass = item.specialClass ? user.stats.class === item.specialClass : true; + item.canOwn = user => { + const userHasOwnedItem = ownsItem(key)(user); + const eventIsCurrent = moment().isAfter(item.event.start) && moment().isBefore(item.event.end); + const compatibleWithUserClass = item.specialClass ? user.stats.class === item.specialClass : true; return _canOwn(user) && (userHasOwnedItem || eventIsCurrent) && compatibleWithUserClass; }; diff --git a/website/common/script/content/gear/sets/armoire.js b/website/common/script/content/gear/sets/armoire.js index 31c735f731..a6c761c711 100644 --- a/website/common/script/content/gear/sets/armoire.js +++ b/website/common/script/content/gear/sets/armoire.js @@ -1,7 +1,7 @@ import { ownsItem } from '../gear-helper'; import t from '../../translation'; -let armor = { +const armor = { lunarArmor: { text: t('armorArmoireLunarArmorText'), notes: t('armorArmoireLunarArmorNotes', { str: 7, int: 7 }), @@ -500,7 +500,7 @@ let armor = { }, }; -let body = { +const body = { cozyScarf: { text: t('bodyArmoireCozyScarfText'), notes: t('bodyArmoireCozyScarfNotes', { attrs: 5 }), @@ -512,7 +512,7 @@ let body = { }, }; -let eyewear = { +const eyewear = { plagueDoctorMask: { text: t('eyewearArmoirePlagueDoctorMaskText'), notes: t('eyewearArmoirePlagueDoctorMaskNotes', { attrs: 5 }), @@ -531,7 +531,7 @@ let eyewear = { }, }; -let head = { +const head = { lunarCrown: { text: t('headArmoireLunarCrownText'), notes: t('headArmoireLunarCrownNotes', { con: 7, per: 7 }), @@ -1022,7 +1022,7 @@ let head = { }, }; -let shield = { +const shield = { gladiatorShield: { text: t('shieldArmoireGladiatorShieldText'), notes: t('shieldArmoireGladiatorShieldNotes', { con: 5, str: 5 }), @@ -1301,7 +1301,7 @@ let shield = { }, }; -let headAccessory = { +const headAccessory = { comicalArrow: { text: t('headAccessoryArmoireComicalArrowText'), notes: t('headAccessoryArmoireComicalArrowNotes', { str: 10 }), @@ -1319,7 +1319,7 @@ let headAccessory = { }, }; -let weapon = { +const weapon = { basicCrossbow: { text: t('weaponArmoireBasicCrossbowText'), notes: t('weaponArmoireBasicCrossbowNotes', { str: 5, per: 5, con: 5 }), @@ -1755,4 +1755,4 @@ export { headAccessory, shield, weapon, -}; \ No newline at end of file +}; diff --git a/website/common/script/content/gear/sets/base.js b/website/common/script/content/gear/sets/base.js index a041667df1..f87562012a 100644 --- a/website/common/script/content/gear/sets/base.js +++ b/website/common/script/content/gear/sets/base.js @@ -1,6 +1,6 @@ import t from '../../translation'; -let armor = { +const armor = { 0: { text: t('armorBase0Text'), notes: t('armorBase0Notes'), @@ -8,7 +8,7 @@ let armor = { }, }; -let back = { +const back = { 0: { text: t('backBase0Text'), notes: t('backBase0Notes'), @@ -16,7 +16,7 @@ let back = { }, }; -let body = { +const body = { 0: { text: t('bodyBase0Text'), notes: t('bodyBase0Notes'), @@ -24,7 +24,7 @@ let body = { }, }; -let eyewear = { +const eyewear = { 0: { text: t('eyewearBase0Text'), notes: t('eyewearBase0Notes'), @@ -33,7 +33,7 @@ let eyewear = { }, }; -let head = { +const head = { 0: { text: t('headBase0Text'), notes: t('headBase0Notes'), @@ -41,7 +41,7 @@ let head = { }, }; -let headAccessory = { +const headAccessory = { 0: { text: t('headAccessoryBase0Text'), notes: t('headAccessoryBase0Notes'), @@ -50,7 +50,7 @@ let headAccessory = { }, }; -let shield = { +const shield = { 0: { text: t('shieldBase0Text'), notes: t('shieldBase0Notes'), @@ -58,7 +58,7 @@ let shield = { }, }; -let weapon = { +const weapon = { 0: { text: t('weaponBase0Text'), notes: t('weaponBase0Notes'), @@ -75,4 +75,4 @@ export { headAccessory, shield, weapon, -}; \ No newline at end of file +}; diff --git a/website/common/script/content/gear/sets/healer.js b/website/common/script/content/gear/sets/healer.js index 7eeda40422..04af4c2855 100644 --- a/website/common/script/content/gear/sets/healer.js +++ b/website/common/script/content/gear/sets/healer.js @@ -1,6 +1,6 @@ import t from '../../translation'; -let armor = { +const armor = { 1: { text: t('armorHealer1Text'), notes: t('armorHealer1Notes', { con: 6 }), @@ -34,7 +34,7 @@ let armor = { }, }; -let head = { +const head = { 1: { text: t('headHealer1Text'), notes: t('headHealer1Notes', { int: 2 }), @@ -68,7 +68,7 @@ let head = { }, }; -let shield = { +const shield = { 1: { text: t('shieldHealer1Text'), notes: t('shieldHealer1Notes', { con: 2 }), @@ -102,7 +102,7 @@ let shield = { }, }; -let weapon = { +const weapon = { 0: { text: t('weaponHealer0Text'), @@ -153,4 +153,4 @@ export { head, shield, weapon, -}; \ No newline at end of file +}; diff --git a/website/common/script/content/gear/sets/mystery.js b/website/common/script/content/gear/sets/mystery.js index 38ee8696fe..5833564e2f 100644 --- a/website/common/script/content/gear/sets/mystery.js +++ b/website/common/script/content/gear/sets/mystery.js @@ -1,6 +1,6 @@ import t from '../../translation'; -let armor = { +const armor = { 201402: { text: t('armorMystery201402Text'), notes: t('armorMystery201402Notes'), @@ -279,7 +279,7 @@ let armor = { }, }; -let back = { +const back = { 201402: { text: t('backMystery201402Text'), notes: t('backMystery201402Notes'), @@ -390,7 +390,7 @@ let back = { }, }; -let body = { +const body = { 201705: { text: t('bodyMystery201705Text'), notes: t('bodyMystery201705Notes'), @@ -417,7 +417,7 @@ let body = { }, }; -let eyewear = { +const eyewear = { 201503: { text: t('eyewearMystery201503Text'), notes: t('eyewearMystery201503Notes'), @@ -474,7 +474,7 @@ let eyewear = { }, }; -let head = { +const head = { 201402: { text: t('headMystery201402Text'), notes: t('headMystery201402Notes'), @@ -771,7 +771,7 @@ let head = { }, }; -let headAccessory = { +const headAccessory = { 201403: { text: t('headAccessoryMystery201403Text'), notes: t('headAccessoryMystery201403Notes'), @@ -846,7 +846,7 @@ let headAccessory = { }, }; -let shield = { +const shield = { 201601: { text: t('shieldMystery201601Text'), notes: t('shieldMystery201601Notes'), @@ -897,7 +897,7 @@ let shield = { }, }; -let weapon = { +const weapon = { 201411: { text: t('weaponMystery201411Text'), notes: t('weaponMystery201411Notes'), @@ -951,4 +951,4 @@ export { headAccessory, shield, weapon, -}; \ No newline at end of file +}; diff --git a/website/common/script/content/gear/sets/rogue.js b/website/common/script/content/gear/sets/rogue.js index efb070467e..9c8c45a607 100644 --- a/website/common/script/content/gear/sets/rogue.js +++ b/website/common/script/content/gear/sets/rogue.js @@ -1,6 +1,6 @@ import t from '../../translation'; -let armor = { +const armor = { 1: { text: t('armorRogue1Text'), notes: t('armorRogue1Notes', { per: 6 }), @@ -34,7 +34,7 @@ let armor = { }, }; -let head = { +const head = { 1: { text: t('headRogue1Text'), notes: t('headRogue1Notes', { per: 2 }), @@ -68,7 +68,7 @@ let head = { }, }; -let weapon = { +const weapon = { 0: { text: t('weaponRogue0Text'), notes: t('weaponRogue0Notes'), @@ -114,7 +114,7 @@ let weapon = { }, }; -let shield = { +const shield = { 0: { text: t('weaponRogue0Text'), notes: t('weaponRogue0Notes'), @@ -165,4 +165,4 @@ export { head, shield, weapon, -}; \ No newline at end of file +}; diff --git a/website/common/script/content/gear/sets/special/index.js b/website/common/script/content/gear/sets/special/index.js index 9651f39766..e505116e53 100644 --- a/website/common/script/content/gear/sets/special/index.js +++ b/website/common/script/content/gear/sets/special/index.js @@ -10,7 +10,7 @@ import t from '../../../translation'; const CURRENT_SEASON = 'fall'; -let armor = { +const armor = { 0: backerGear.armorSpecial0, 1: contributorGear.armorSpecial1, 2: backerGear.armorSpecial2, @@ -114,9 +114,7 @@ let armor = { notes: t('armorSpecialYetiNotes', { con: 9 }), con: 9, value: 90, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, ski: { event: EVENTS.winter, @@ -126,9 +124,7 @@ let armor = { notes: t('armorSpecialSkiNotes', { per: 15 }), per: 15, value: 90, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, candycane: { event: EVENTS.winter, @@ -138,9 +134,7 @@ let armor = { notes: t('armorSpecialCandycaneNotes', { int: 9 }), int: 9, value: 90, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, snowflake: { event: EVENTS.winter, @@ -150,14 +144,13 @@ let armor = { notes: t('armorSpecialSnowflakeNotes', { con: 15 }), con: 15, value: 90, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, birthday: { event: EVENTS.birthday, text: t('armorSpecialBirthdayText'), - notes: t('armorSpecialBirthdayNotes'), value: 0, + notes: t('armorSpecialBirthdayNotes'), + value: 0, }, springRogue: { event: EVENTS.spring, @@ -167,9 +160,7 @@ let armor = { notes: t('armorSpecialSpringRogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, springWarrior: { event: EVENTS.spring, @@ -179,9 +170,7 @@ let armor = { notes: t('armorSpecialSpringWarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, springMage: { event: EVENTS.spring, @@ -191,9 +180,7 @@ let armor = { notes: t('armorSpecialSpringMageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, springHealer: { event: EVENTS.spring, @@ -203,9 +190,7 @@ let armor = { notes: t('armorSpecialSpringHealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summerRogue: { event: EVENTS.summer, @@ -215,9 +200,7 @@ let armor = { notes: t('armorSpecialSummerRogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summerWarrior: { event: EVENTS.summer, @@ -227,9 +210,7 @@ let armor = { notes: t('armorSpecialSummerWarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summerMage: { event: EVENTS.summer, @@ -239,9 +220,7 @@ let armor = { notes: t('armorSpecialSummerMageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summerHealer: { event: EVENTS.summer, @@ -251,9 +230,7 @@ let armor = { notes: t('armorSpecialSummerHealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fallRogue: { event: EVENTS.fall, @@ -263,9 +240,7 @@ let armor = { notes: t('armorSpecialFallRogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fallWarrior: { event: EVENTS.fall, @@ -275,9 +250,7 @@ let armor = { notes: t('armorSpecialFallWarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fallMage: { event: EVENTS.fall, @@ -287,9 +260,7 @@ let armor = { notes: t('armorSpecialFallMageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fallHealer: { event: EVENTS.fall, @@ -299,9 +270,7 @@ let armor = { notes: t('armorSpecialFallHealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, winter2015Rogue: { event: EVENTS.winter2015, @@ -311,9 +280,7 @@ let armor = { notes: t('armorSpecialWinter2015RogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2015Warrior: { event: EVENTS.winter2015, @@ -323,9 +290,7 @@ let armor = { notes: t('armorSpecialWinter2015WarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2015Mage: { event: EVENTS.winter2015, @@ -335,9 +300,7 @@ let armor = { notes: t('armorSpecialWinter2015MageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2015Healer: { event: EVENTS.winter2015, @@ -347,9 +310,7 @@ let armor = { notes: t('armorSpecialWinter2015HealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, birthday2015: { text: t('armorSpecialBirthday2015Text'), @@ -365,9 +326,7 @@ let armor = { notes: t('armorSpecialSpring2015RogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2015Warrior: { event: EVENTS.spring2015, @@ -377,9 +336,7 @@ let armor = { notes: t('armorSpecialSpring2015WarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2015Mage: { event: EVENTS.spring2015, @@ -389,9 +346,7 @@ let armor = { notes: t('armorSpecialSpring2015MageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2015Healer: { event: EVENTS.spring2015, @@ -401,9 +356,7 @@ let armor = { notes: t('armorSpecialSpring2015HealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summer2015Rogue: { event: EVENTS.summer2015, @@ -413,9 +366,7 @@ let armor = { notes: t('armorSpecialSummer2015RogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2015Warrior: { event: EVENTS.summer2015, @@ -425,9 +376,7 @@ let armor = { notes: t('armorSpecialSummer2015WarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2015Mage: { event: EVENTS.summer2015, @@ -437,9 +386,7 @@ let armor = { notes: t('armorSpecialSummer2015MageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2015Healer: { event: EVENTS.summer2015, @@ -449,9 +396,7 @@ let armor = { notes: t('armorSpecialSummer2015HealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fall2015Rogue: { event: EVENTS.fall2015, @@ -461,9 +406,7 @@ let armor = { notes: t('armorSpecialFall2015RogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2015Warrior: { event: EVENTS.fall2015, @@ -473,9 +416,7 @@ let armor = { notes: t('armorSpecialFall2015WarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2015Mage: { event: EVENTS.fall2015, @@ -485,9 +426,7 @@ let armor = { notes: t('armorSpecialFall2015MageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2015Healer: { event: EVENTS.fall2015, @@ -497,9 +436,7 @@ let armor = { notes: t('armorSpecialFall2015HealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, gaymerx: { event: EVENTS.gaymerx, @@ -515,9 +452,7 @@ let armor = { notes: t('armorSpecialWinter2016RogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2016Warrior: { event: EVENTS.winter2016, @@ -527,9 +462,7 @@ let armor = { notes: t('armorSpecialWinter2016WarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2016Mage: { event: EVENTS.winter2016, @@ -539,9 +472,7 @@ let armor = { notes: t('armorSpecialWinter2016MageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2016Healer: { event: EVENTS.winter2016, @@ -551,9 +482,7 @@ let armor = { notes: t('armorSpecialWinter2016HealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, birthday2016: { text: t('armorSpecialBirthday2016Text'), @@ -569,9 +498,7 @@ let armor = { notes: t('armorSpecialSpring2016RogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2016Warrior: { event: EVENTS.spring2016, @@ -581,9 +508,7 @@ let armor = { notes: t('armorSpecialSpring2016WarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2016Mage: { event: EVENTS.spring2016, @@ -593,9 +518,7 @@ let armor = { notes: t('armorSpecialSpring2016MageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2016Healer: { event: EVENTS.spring2016, @@ -605,9 +528,7 @@ let armor = { notes: t('armorSpecialSpring2016HealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summer2016Rogue: { event: EVENTS.summer2016, @@ -617,9 +538,7 @@ let armor = { notes: t('armorSpecialSummer2016RogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2016Warrior: { event: EVENTS.summer2016, @@ -629,9 +548,7 @@ let armor = { notes: t('armorSpecialSummer2016WarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2016Mage: { event: EVENTS.summer2016, @@ -641,9 +558,7 @@ let armor = { notes: t('armorSpecialSummer2016MageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2016Healer: { event: EVENTS.summer2016, @@ -653,9 +568,7 @@ let armor = { notes: t('armorSpecialSummer2016HealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fall2016Rogue: { event: EVENTS.fall2016, @@ -665,9 +578,7 @@ let armor = { notes: t('armorSpecialFall2016RogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2016Warrior: { event: EVENTS.fall2016, @@ -677,9 +588,7 @@ let armor = { notes: t('armorSpecialFall2016WarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2016Mage: { event: EVENTS.fall2016, @@ -689,9 +598,7 @@ let armor = { notes: t('armorSpecialFall2016MageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2016Healer: { event: EVENTS.fall2016, @@ -701,9 +608,7 @@ let armor = { notes: t('armorSpecialFall2016HealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, winter2017Rogue: { event: EVENTS.winter2017, @@ -713,9 +618,7 @@ let armor = { notes: t('armorSpecialWinter2017RogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2017Warrior: { event: EVENTS.winter2017, @@ -725,9 +628,7 @@ let armor = { notes: t('armorSpecialWinter2017WarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2017Mage: { event: EVENTS.winter2017, @@ -737,9 +638,7 @@ let armor = { notes: t('armorSpecialWinter2017MageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2017Healer: { event: EVENTS.winter2017, @@ -749,9 +648,7 @@ let armor = { notes: t('armorSpecialWinter2017HealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, birthday2017: { text: t('armorSpecialBirthday2017Text'), @@ -767,9 +664,7 @@ let armor = { notes: t('armorSpecialSpring2017RogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2017Warrior: { event: EVENTS.spring2017, @@ -779,9 +674,7 @@ let armor = { notes: t('armorSpecialSpring2017WarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2017Mage: { event: EVENTS.spring2017, @@ -791,9 +684,7 @@ let armor = { notes: t('armorSpecialSpring2017MageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2017Healer: { event: EVENTS.spring2017, @@ -803,9 +694,7 @@ let armor = { notes: t('armorSpecialSpring2017HealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summer2017Rogue: { event: EVENTS.summer2017, @@ -815,9 +704,7 @@ let armor = { notes: t('armorSpecialSummer2017RogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2017Warrior: { event: EVENTS.summer2017, @@ -827,9 +714,7 @@ let armor = { notes: t('armorSpecialSummer2017WarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2017Mage: { event: EVENTS.summer2017, @@ -839,9 +724,7 @@ let armor = { notes: t('armorSpecialSummer2017MageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2017Healer: { event: EVENTS.summer2017, @@ -851,9 +734,7 @@ let armor = { notes: t('armorSpecialSummer2017HealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fall2017Rogue: { event: EVENTS.fall2017, @@ -863,9 +744,7 @@ let armor = { notes: t('armorSpecialFall2017RogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2017Warrior: { event: EVENTS.fall2017, @@ -875,9 +754,7 @@ let armor = { notes: t('armorSpecialFall2017WarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2017Mage: { event: EVENTS.fall2017, @@ -887,9 +764,7 @@ let armor = { notes: t('armorSpecialFall2017MageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2017Healer: { event: EVENTS.fall2017, @@ -899,9 +774,7 @@ let armor = { notes: t('armorSpecialFall2017HealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, winter2018Rogue: { event: EVENTS.winter2018, @@ -911,9 +784,7 @@ let armor = { notes: t('armorSpecialWinter2018RogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2018Warrior: { event: EVENTS.winter2018, @@ -923,9 +794,7 @@ let armor = { notes: t('armorSpecialWinter2018WarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2018Mage: { event: EVENTS.winter2018, @@ -935,9 +804,7 @@ let armor = { notes: t('armorSpecialWinter2018MageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2018Healer: { event: EVENTS.winter2018, @@ -947,9 +814,7 @@ let armor = { notes: t('armorSpecialWinter2018HealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, birthday2018: { text: t('armorSpecialBirthday2018Text'), @@ -965,9 +830,7 @@ let armor = { notes: t('armorSpecialSpring2018RogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2018Warrior: { event: EVENTS.spring2018, @@ -977,9 +840,7 @@ let armor = { notes: t('armorSpecialSpring2018WarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2018Mage: { event: EVENTS.spring2018, @@ -989,9 +850,7 @@ let armor = { notes: t('armorSpecialSpring2018MageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2018Healer: { event: EVENTS.spring2018, @@ -1001,9 +860,7 @@ let armor = { notes: t('armorSpecialSpring2018HealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summer2018Rogue: { event: EVENTS.summer2018, @@ -1013,9 +870,7 @@ let armor = { notes: t('armorSpecialSummer2018RogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2018Warrior: { event: EVENTS.summer2018, @@ -1025,9 +880,7 @@ let armor = { notes: t('armorSpecialSummer2018WarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2018Mage: { event: EVENTS.summer2018, @@ -1037,9 +890,7 @@ let armor = { notes: t('armorSpecialSummer2018MageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2018Healer: { event: EVENTS.summer2018, @@ -1049,9 +900,7 @@ let armor = { notes: t('armorSpecialSummer2018HealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fall2018Rogue: { event: EVENTS.fall2018, @@ -1061,9 +910,7 @@ let armor = { notes: t('armorSpecialFall2018RogueNotes', { per: 15 }), value: 90, per: 15, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2018Warrior: { event: EVENTS.fall2018, @@ -1073,9 +920,7 @@ let armor = { notes: t('armorSpecialFall2018WarriorNotes', { con: 9 }), value: 90, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2018Mage: { event: EVENTS.fall2018, @@ -1085,9 +930,7 @@ let armor = { notes: t('armorSpecialFall2018MageNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2018Healer: { event: EVENTS.fall2018, @@ -1097,9 +940,7 @@ let armor = { notes: t('armorSpecialFall2018HealerNotes', { con: 15 }), value: 90, con: 15, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, turkeyArmorGilded: { text: t('armorSpecialTurkeyArmorGildedText'), @@ -1266,9 +1107,9 @@ let armor = { }, }; -let back = { - wondercon_red: wonderconGear.backSpecialWonderconRed, // eslint-disable-line camelcase - wondercon_black: wonderconGear.backSpecialWonderconBlack, // eslint-disable-line camelcase +const back = { + wondercon_red: wonderconGear.backSpecialWonderconRed, // eslint-disable-line camelcase + wondercon_black: wonderconGear.backSpecialWonderconBlack, // eslint-disable-line camelcase takeThis: takeThisGear.backSpecialTakeThis, snowdriftVeil: { text: t('backSpecialSnowdriftVeilText'), @@ -1294,9 +1135,7 @@ let back = { notes: t('backBearTailNotes'), value: 20, canOwn: ownsItem('back_special_bearTail'), - canBuy: () => { - return true; - }, + canBuy: () => true, }, cactusTail: { gearSet: 'animal', @@ -1304,9 +1143,7 @@ let back = { notes: t('backCactusTailNotes'), value: 20, canOwn: ownsItem('back_special_cactusTail'), - canBuy: () => { - return true; - }, + canBuy: () => true, }, foxTail: { gearSet: 'animal', @@ -1314,9 +1151,7 @@ let back = { notes: t('backFoxTailNotes'), value: 20, canOwn: ownsItem('back_special_foxTail'), - canBuy: () => { - return true; - }, + canBuy: () => true, }, lionTail: { gearSet: 'animal', @@ -1324,9 +1159,7 @@ let back = { notes: t('backLionTailNotes'), value: 20, canOwn: ownsItem('back_special_lionTail'), - canBuy: () => { - return true; - }, + canBuy: () => true, }, pandaTail: { gearSet: 'animal', @@ -1334,9 +1167,7 @@ let back = { notes: t('backPandaTailNotes'), value: 20, canOwn: ownsItem('back_special_pandaTail'), - canBuy: () => { - return true; - }, + canBuy: () => true, }, pigTail: { gearSet: 'animal', @@ -1344,9 +1175,7 @@ let back = { notes: t('backPigTailNotes'), value: 20, canOwn: ownsItem('back_special_pigTail'), - canBuy: () => { - return true; - }, + canBuy: () => true, }, tigerTail: { gearSet: 'animal', @@ -1354,9 +1183,7 @@ let back = { notes: t('backTigerTailNotes'), value: 20, canOwn: ownsItem('back_special_tigerTail'), - canBuy: () => { - return true; - }, + canBuy: () => true, }, wolfTail: { gearSet: 'animal', @@ -1364,9 +1191,7 @@ let back = { notes: t('backWolfTailNotes'), value: 20, canOwn: ownsItem('back_special_wolfTail'), - canBuy: () => { - return true; - }, + canBuy: () => true, }, turkeyTailGilded: { text: t('backSpecialTurkeyTailGildedText'), @@ -1376,10 +1201,10 @@ let back = { }, }; -let body = { - wondercon_red: wonderconGear.bodySpecialWonderconRed, // eslint-disable-line camelcase - wondercon_gold: wonderconGear.bodySpecialWonderconGold, // eslint-disable-line camelcase - wondercon_black: wonderconGear.bodySpecialWonderconBlack, // eslint-disable-line camelcase +const body = { + wondercon_red: wonderconGear.bodySpecialWonderconRed, // eslint-disable-line camelcase + wondercon_gold: wonderconGear.bodySpecialWonderconGold, // eslint-disable-line camelcase + wondercon_black: wonderconGear.bodySpecialWonderconBlack, // eslint-disable-line camelcase takeThis: takeThisGear.bodySpecialTakeThis, summerHealer: { event: EVENTS.summer, @@ -1388,9 +1213,7 @@ let body = { text: t('bodySpecialSummerHealerText'), notes: t('bodySpecialSummerHealerNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summerMage: { event: EVENTS.summer, @@ -1399,9 +1222,7 @@ let body = { text: t('bodySpecialSummerMageText'), notes: t('bodySpecialSummerMageNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2015Healer: { event: EVENTS.summer2015, @@ -1410,9 +1231,7 @@ let body = { text: t('bodySpecialSummer2015HealerText'), notes: t('bodySpecialSummer2015HealerNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2015Mage: { event: EVENTS.summer2015, @@ -1421,9 +1240,7 @@ let body = { text: t('bodySpecialSummer2015MageText'), notes: t('bodySpecialSummer2015MageNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2015Rogue: { event: EVENTS.summer2015, @@ -1432,9 +1249,7 @@ let body = { text: t('bodySpecialSummer2015RogueText'), notes: t('bodySpecialSummer2015RogueNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2015Warrior: { event: EVENTS.summer2015, @@ -1443,9 +1258,7 @@ let body = { text: t('bodySpecialSummer2015WarriorText'), notes: t('bodySpecialSummer2015WarriorNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, aetherAmulet: { text: t('bodySpecialAetherAmuletText'), @@ -1463,9 +1276,9 @@ let body = { }, }; -let eyewear = { - wondercon_red: wonderconGear.eyewearSpecialWonderconRed, // eslint-disable-line camelcase - wondercon_black: wonderconGear.eyewearSpecialWonderconBlack, // eslint-disable-line camelcase +const eyewear = { + wondercon_red: wonderconGear.eyewearSpecialWonderconRed, // eslint-disable-line camelcase + wondercon_black: wonderconGear.eyewearSpecialWonderconBlack, // eslint-disable-line camelcase summerRogue: { event: EVENTS.summer, specialClass: 'rogue', @@ -1473,9 +1286,7 @@ let eyewear = { text: t('eyewearSpecialSummerRogueText'), notes: t('eyewearSpecialSummerRogueNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summerWarrior: { event: EVENTS.summer, @@ -1484,9 +1295,7 @@ let eyewear = { text: t('eyewearSpecialSummerWarriorText'), notes: t('eyewearSpecialSummerWarriorNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, blackTopFrame: { gearSet: 'glasses', @@ -1617,7 +1426,7 @@ let eyewear = { }, }; -let head = { +const head = { 0: backerGear.headSpecial0, 1: contributorGear.headSpecial1, 2: backerGear.headSpecial2, @@ -1728,9 +1537,7 @@ let head = { notes: t('headSpecialYetiNotes', { str: 9 }), str: 9, value: 60, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, ski: { event: EVENTS.winter, @@ -1740,9 +1547,7 @@ let head = { notes: t('headSpecialSkiNotes', { per: 9 }), per: 9, value: 60, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, candycane: { event: EVENTS.winter, @@ -1752,9 +1557,7 @@ let head = { notes: t('headSpecialCandycaneNotes', { per: 7 }), per: 7, value: 60, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, snowflake: { event: EVENTS.winter, @@ -1764,9 +1567,7 @@ let head = { notes: t('headSpecialSnowflakeNotes', { int: 7 }), int: 7, value: 60, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, springRogue: { event: EVENTS.spring, @@ -1776,9 +1577,7 @@ let head = { notes: t('headSpecialSpringRogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, springWarrior: { event: EVENTS.spring, @@ -1788,9 +1587,7 @@ let head = { notes: t('headSpecialSpringWarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, springMage: { event: EVENTS.spring, @@ -1800,9 +1597,7 @@ let head = { notes: t('headSpecialSpringMageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, springHealer: { event: EVENTS.spring, @@ -1812,9 +1607,7 @@ let head = { notes: t('headSpecialSpringHealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summerRogue: { event: EVENTS.summer, @@ -1824,9 +1617,7 @@ let head = { notes: t('headSpecialSummerRogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summerWarrior: { event: EVENTS.summer, @@ -1836,9 +1627,7 @@ let head = { notes: t('headSpecialSummerWarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summerMage: { event: EVENTS.summer, @@ -1848,9 +1637,7 @@ let head = { notes: t('headSpecialSummerMageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summerHealer: { event: EVENTS.summer, @@ -1860,9 +1647,7 @@ let head = { notes: t('headSpecialSummerHealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fallRogue: { event: EVENTS.fall, @@ -1872,9 +1657,7 @@ let head = { notes: t('headSpecialFallRogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fallWarrior: { event: EVENTS.fall, @@ -1884,9 +1667,7 @@ let head = { notes: t('headSpecialFallWarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fallMage: { event: EVENTS.fall, @@ -1896,9 +1677,7 @@ let head = { notes: t('headSpecialFallMageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fallHealer: { event: EVENTS.fall, @@ -1908,9 +1687,7 @@ let head = { notes: t('headSpecialFallHealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, winter2015Rogue: { event: EVENTS.winter2015, @@ -1920,9 +1697,7 @@ let head = { notes: t('headSpecialWinter2015RogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2015Warrior: { event: EVENTS.winter2015, @@ -1932,9 +1707,7 @@ let head = { notes: t('headSpecialWinter2015WarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2015Mage: { event: EVENTS.winter2015, @@ -1944,9 +1717,7 @@ let head = { notes: t('headSpecialWinter2015MageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2015Healer: { event: EVENTS.winter2015, @@ -1956,9 +1727,7 @@ let head = { notes: t('headSpecialWinter2015HealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, nye2014: { text: t('headSpecialNye2014Text'), @@ -1974,9 +1743,7 @@ let head = { notes: t('headSpecialSpring2015RogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2015Warrior: { event: EVENTS.spring2015, @@ -1986,9 +1753,7 @@ let head = { notes: t('headSpecialSpring2015WarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2015Mage: { event: EVENTS.spring2015, @@ -1998,9 +1763,7 @@ let head = { notes: t('headSpecialSpring2015MageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2015Healer: { event: EVENTS.spring2015, @@ -2010,9 +1773,7 @@ let head = { notes: t('headSpecialSpring2015HealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summer2015Rogue: { event: EVENTS.summer2015, @@ -2022,9 +1783,7 @@ let head = { notes: t('headSpecialSummer2015RogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2015Warrior: { event: EVENTS.summer2015, @@ -2034,9 +1793,7 @@ let head = { notes: t('headSpecialSummer2015WarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2015Mage: { event: EVENTS.summer2015, @@ -2046,9 +1803,7 @@ let head = { notes: t('headSpecialSummer2015MageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2015Healer: { event: EVENTS.summer2015, @@ -2058,9 +1813,7 @@ let head = { notes: t('headSpecialSummer2015HealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fall2015Rogue: { event: EVENTS.fall2015, @@ -2070,9 +1823,7 @@ let head = { notes: t('headSpecialFall2015RogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2015Warrior: { event: EVENTS.fall2015, @@ -2082,9 +1833,7 @@ let head = { notes: t('headSpecialFall2015WarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2015Mage: { event: EVENTS.fall2015, @@ -2094,9 +1843,7 @@ let head = { notes: t('headSpecialFall2015MageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2015Healer: { event: EVENTS.fall2015, @@ -2106,9 +1853,7 @@ let head = { notes: t('headSpecialFall2015HealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, gaymerx: { event: EVENTS.gaymerx, @@ -2124,9 +1869,7 @@ let head = { notes: t('headSpecialWinter2016RogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2016Warrior: { event: EVENTS.winter2016, @@ -2136,9 +1879,7 @@ let head = { notes: t('headSpecialWinter2016WarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2016Mage: { event: EVENTS.winter2016, @@ -2148,9 +1889,7 @@ let head = { notes: t('headSpecialWinter2016MageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2016Healer: { event: EVENTS.winter2016, @@ -2160,9 +1899,7 @@ let head = { notes: t('headSpecialWinter2016HealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, nye2015: { text: t('headSpecialNye2015Text'), @@ -2178,9 +1915,7 @@ let head = { notes: t('headSpecialSpring2016RogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2016Warrior: { event: EVENTS.spring2016, @@ -2190,9 +1925,7 @@ let head = { notes: t('headSpecialSpring2016WarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2016Mage: { event: EVENTS.spring2016, @@ -2202,9 +1935,7 @@ let head = { notes: t('headSpecialSpring2016MageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2016Healer: { event: EVENTS.spring2016, @@ -2214,9 +1945,7 @@ let head = { notes: t('headSpecialSpring2016HealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summer2016Rogue: { event: EVENTS.summer2016, @@ -2226,9 +1955,7 @@ let head = { notes: t('headSpecialSummer2016RogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2016Warrior: { event: EVENTS.summer2016, @@ -2238,9 +1965,7 @@ let head = { notes: t('headSpecialSummer2016WarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2016Mage: { event: EVENTS.summer2016, @@ -2250,9 +1975,7 @@ let head = { notes: t('headSpecialSummer2016MageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2016Healer: { event: EVENTS.summer2016, @@ -2262,9 +1985,7 @@ let head = { notes: t('headSpecialSummer2016HealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fall2016Rogue: { event: EVENTS.fall2016, @@ -2274,9 +1995,7 @@ let head = { notes: t('headSpecialFall2016RogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2016Warrior: { event: EVENTS.fall2016, @@ -2286,9 +2005,7 @@ let head = { notes: t('headSpecialFall2016WarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2016Mage: { event: EVENTS.fall2016, @@ -2298,9 +2015,7 @@ let head = { notes: t('headSpecialFall2016MageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2016Healer: { event: EVENTS.fall2016, @@ -2310,9 +2025,7 @@ let head = { notes: t('headSpecialFall2016HealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, winter2017Rogue: { event: EVENTS.winter2017, @@ -2322,9 +2035,7 @@ let head = { notes: t('headSpecialWinter2017RogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2017Warrior: { event: EVENTS.winter2017, @@ -2334,9 +2045,7 @@ let head = { notes: t('headSpecialWinter2017WarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2017Mage: { event: EVENTS.winter2017, @@ -2346,9 +2055,7 @@ let head = { notes: t('headSpecialWinter2017MageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2017Healer: { event: EVENTS.winter2017, @@ -2358,9 +2065,7 @@ let head = { notes: t('headSpecialWinter2017HealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, nye2016: { text: t('headSpecialNye2016Text'), @@ -2376,9 +2081,7 @@ let head = { notes: t('headSpecialSpring2017RogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2017Warrior: { event: EVENTS.spring2017, @@ -2388,9 +2091,7 @@ let head = { notes: t('headSpecialSpring2017WarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2017Mage: { event: EVENTS.spring2017, @@ -2400,9 +2101,7 @@ let head = { notes: t('headSpecialSpring2017MageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2017Healer: { event: EVENTS.spring2017, @@ -2412,9 +2111,7 @@ let head = { notes: t('headSpecialSpring2017HealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summer2017Rogue: { event: EVENTS.summer2017, @@ -2424,9 +2121,7 @@ let head = { notes: t('headSpecialSummer2017RogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2017Warrior: { event: EVENTS.summer2017, @@ -2436,9 +2131,7 @@ let head = { notes: t('headSpecialSummer2017WarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2017Mage: { event: EVENTS.summer2017, @@ -2448,9 +2141,7 @@ let head = { notes: t('headSpecialSummer2017MageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2017Healer: { event: EVENTS.summer2017, @@ -2460,9 +2151,7 @@ let head = { notes: t('headSpecialSummer2017HealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, namingDay2017: { text: t('headSpecialNamingDay2017Text'), @@ -2478,9 +2167,7 @@ let head = { notes: t('headSpecialFall2017RogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2017Warrior: { event: EVENTS.fall2017, @@ -2490,9 +2177,7 @@ let head = { notes: t('headSpecialFall2017WarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2017Mage: { event: EVENTS.fall2017, @@ -2502,9 +2187,7 @@ let head = { notes: t('headSpecialFall2017MageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2017Healer: { event: EVENTS.fall2017, @@ -2514,9 +2197,7 @@ let head = { notes: t('headSpecialFall2017HealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, nye2017: { text: t('headSpecialNye2017Text'), @@ -2532,9 +2213,7 @@ let head = { notes: t('headSpecialWinter2018RogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2018Warrior: { event: EVENTS.winter2018, @@ -2544,9 +2223,7 @@ let head = { notes: t('headSpecialWinter2018WarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2018Mage: { event: EVENTS.winter2018, @@ -2556,9 +2233,7 @@ let head = { notes: t('headSpecialWinter2018MageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2018Healer: { event: EVENTS.winter2018, @@ -2568,9 +2243,7 @@ let head = { notes: t('headSpecialWinter2018HealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, spring2018Rogue: { event: EVENTS.spring2018, @@ -2580,9 +2253,7 @@ let head = { notes: t('headSpecialSpring2018RogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2018Warrior: { event: EVENTS.spring2018, @@ -2592,9 +2263,7 @@ let head = { notes: t('headSpecialSpring2018WarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2018Mage: { event: EVENTS.spring2018, @@ -2604,9 +2273,7 @@ let head = { notes: t('headSpecialSpring2018MageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2018Healer: { event: EVENTS.spring2018, @@ -2616,9 +2283,7 @@ let head = { notes: t('headSpecialSpring2018HealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summer2018Rogue: { event: EVENTS.summer2018, @@ -2628,9 +2293,7 @@ let head = { notes: t('headSpecialSummer2018RogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2018Warrior: { event: EVENTS.summer2018, @@ -2640,9 +2303,7 @@ let head = { notes: t('headSpecialSummer2018WarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2018Mage: { event: EVENTS.summer2018, @@ -2652,9 +2313,7 @@ let head = { notes: t('headSpecialSummer2018MageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2018Healer: { event: EVENTS.summer2018, @@ -2664,9 +2323,7 @@ let head = { notes: t('headSpecialSummer2018HealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fall2018Rogue: { event: EVENTS.fall2018, @@ -2676,9 +2333,7 @@ let head = { notes: t('headSpecialFall2018RogueNotes', { per: 9 }), value: 60, per: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2018Warrior: { event: EVENTS.fall2018, @@ -2688,9 +2343,7 @@ let head = { notes: t('headSpecialFall2018WarriorNotes', { str: 9 }), value: 60, str: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2018Mage: { event: EVENTS.fall2018, @@ -2700,9 +2353,7 @@ let head = { notes: t('headSpecialFall2018MageNotes', { per: 7 }), value: 60, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2018Healer: { event: EVENTS.fall2018, @@ -2712,9 +2363,7 @@ let head = { notes: t('headSpecialFall2018HealerNotes', { int: 7 }), value: 60, int: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, turkeyHelmGilded: { text: t('headSpecialTurkeyHelmGildedText'), @@ -2887,7 +2536,7 @@ let head = { }, }; -let headAccessory = { +const headAccessory = { springRogue: { event: EVENTS.spring, specialClass: 'rogue', @@ -2895,9 +2544,7 @@ let headAccessory = { text: t('headAccessorySpecialSpringRogueText'), notes: t('headAccessorySpecialSpringRogueNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, springWarrior: { event: EVENTS.spring, @@ -2906,9 +2553,7 @@ let headAccessory = { text: t('headAccessorySpecialSpringWarriorText'), notes: t('headAccessorySpecialSpringWarriorNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, springMage: { event: EVENTS.spring, @@ -2917,9 +2562,7 @@ let headAccessory = { text: t('headAccessorySpecialSpringMageText'), notes: t('headAccessorySpecialSpringMageNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, springHealer: { event: EVENTS.spring, @@ -2928,9 +2571,7 @@ let headAccessory = { text: t('headAccessorySpecialSpringHealerText'), notes: t('headAccessorySpecialSpringHealerNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2015Rogue: { event: EVENTS.spring2015, @@ -2939,9 +2580,7 @@ let headAccessory = { text: t('headAccessorySpecialSpring2015RogueText'), notes: t('headAccessorySpecialSpring2015RogueNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2015Warrior: { event: EVENTS.spring2015, @@ -2950,9 +2589,7 @@ let headAccessory = { text: t('headAccessorySpecialSpring2015WarriorText'), notes: t('headAccessorySpecialSpring2015WarriorNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2015Mage: { event: EVENTS.spring2015, @@ -2961,9 +2598,7 @@ let headAccessory = { text: t('headAccessorySpecialSpring2015MageText'), notes: t('headAccessorySpecialSpring2015MageNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2015Healer: { event: EVENTS.spring2015, @@ -2972,9 +2607,7 @@ let headAccessory = { text: t('headAccessorySpecialSpring2015HealerText'), notes: t('headAccessorySpecialSpring2015HealerNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, bearEars: { gearSet: 'animal', @@ -2982,9 +2615,7 @@ let headAccessory = { notes: t('headAccessoryBearEarsNotes'), value: 20, canOwn: ownsItem('headAccessory_special_bearEars'), - canBuy: () => { - return true; - }, + canBuy: () => true, }, cactusEars: { gearSet: 'animal', @@ -2992,9 +2623,7 @@ let headAccessory = { notes: t('headAccessoryCactusEarsNotes'), value: 20, canOwn: ownsItem('headAccessory_special_cactusEars'), - canBuy: () => { - return true; - }, + canBuy: () => true, }, foxEars: { gearSet: 'animal', @@ -3002,9 +2631,7 @@ let headAccessory = { notes: t('headAccessoryFoxEarsNotes'), value: 20, canOwn: ownsItem('headAccessory_special_foxEars'), - canBuy: () => { - return true; - }, + canBuy: () => true, }, lionEars: { gearSet: 'animal', @@ -3012,9 +2639,7 @@ let headAccessory = { notes: t('headAccessoryLionEarsNotes'), value: 20, canOwn: ownsItem('headAccessory_special_lionEars'), - canBuy: () => { - return true; - }, + canBuy: () => true, }, pandaEars: { gearSet: 'animal', @@ -3022,9 +2647,7 @@ let headAccessory = { notes: t('headAccessoryPandaEarsNotes'), value: 20, canOwn: ownsItem('headAccessory_special_pandaEars'), - canBuy: () => { - return true; - }, + canBuy: () => true, }, pigEars: { gearSet: 'animal', @@ -3032,9 +2655,7 @@ let headAccessory = { notes: t('headAccessoryPigEarsNotes'), value: 20, canOwn: ownsItem('headAccessory_special_pigEars'), - canBuy: () => { - return true; - }, + canBuy: () => true, }, tigerEars: { gearSet: 'animal', @@ -3042,9 +2663,7 @@ let headAccessory = { notes: t('headAccessoryTigerEarsNotes'), value: 20, canOwn: ownsItem('headAccessory_special_tigerEars'), - canBuy: () => { - return true; - }, + canBuy: () => true, }, wolfEars: { gearSet: 'animal', @@ -3052,9 +2671,7 @@ let headAccessory = { notes: t('headAccessoryWolfEarsNotes'), value: 20, canOwn: ownsItem('headAccessory_special_wolfEars'), - canBuy: () => { - return true; - }, + canBuy: () => true, }, spring2016Rogue: { event: EVENTS.spring2016, @@ -3063,9 +2680,7 @@ let headAccessory = { text: t('headAccessorySpecialSpring2016RogueText'), notes: t('headAccessorySpecialSpring2016RogueNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2016Warrior: { event: EVENTS.spring2016, @@ -3074,9 +2689,7 @@ let headAccessory = { text: t('headAccessorySpecialSpring2016WarriorText'), notes: t('headAccessorySpecialSpring2016WarriorNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2016Mage: { event: EVENTS.spring2016, @@ -3085,9 +2698,7 @@ let headAccessory = { text: t('headAccessorySpecialSpring2016MageText'), notes: t('headAccessorySpecialSpring2016MageNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2016Healer: { event: EVENTS.spring2016, @@ -3096,9 +2707,7 @@ let headAccessory = { text: t('headAccessorySpecialSpring2016HealerText'), notes: t('headAccessorySpecialSpring2016HealerNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2017Rogue: { event: EVENTS.spring2017, @@ -3107,9 +2716,7 @@ let headAccessory = { text: t('headAccessorySpecialSpring2017RogueText'), notes: t('headAccessorySpecialSpring2017RogueNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2017Warrior: { event: EVENTS.spring2017, @@ -3118,9 +2725,7 @@ let headAccessory = { text: t('headAccessorySpecialSpring2017WarriorText'), notes: t('headAccessorySpecialSpring2017WarriorNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2017Mage: { event: EVENTS.spring2017, @@ -3129,9 +2734,7 @@ let headAccessory = { text: t('headAccessorySpecialSpring2017MageText'), notes: t('headAccessorySpecialSpring2017MageNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2017Healer: { event: EVENTS.spring2017, @@ -3140,9 +2743,7 @@ let headAccessory = { text: t('headAccessorySpecialSpring2017HealerText'), notes: t('headAccessorySpecialSpring2017HealerNotes'), value: 20, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, blackHeadband: { gearSet: 'headband', @@ -3195,7 +2796,7 @@ let headAccessory = { }, }; -let shield = { +const shield = { 0: backerGear.shieldSpecial0, 1: contributorGear.shieldSpecial1, takeThis: takeThisGear.shieldSpecialTakeThis, @@ -3264,9 +2865,7 @@ let shield = { notes: t('shieldSpecialYetiNotes', { con: 7 }), con: 7, value: 70, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, ski: { event: EVENTS.winter, @@ -3276,9 +2875,7 @@ let shield = { notes: t('weaponSpecialSkiNotes', { str: 8 }), str: 8, value: 90, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, snowflake: { event: EVENTS.winter, @@ -3288,9 +2885,7 @@ let shield = { notes: t('shieldSpecialSnowflakeNotes', { con: 9 }), con: 9, value: 70, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, springRogue: { event: EVENTS.spring, @@ -3300,9 +2895,7 @@ let shield = { notes: t('shieldSpecialSpringRogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, springWarrior: { event: EVENTS.spring, @@ -3312,9 +2905,7 @@ let shield = { notes: t('shieldSpecialSpringWarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, springHealer: { event: EVENTS.spring, @@ -3324,9 +2915,7 @@ let shield = { notes: t('shieldSpecialSpringHealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summerRogue: { event: EVENTS.summer, @@ -3336,9 +2925,7 @@ let shield = { notes: t('shieldSpecialSummerRogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summerWarrior: { event: EVENTS.summer, @@ -3348,9 +2935,7 @@ let shield = { notes: t('shieldSpecialSummerWarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summerHealer: { event: EVENTS.summer, @@ -3360,9 +2945,7 @@ let shield = { notes: t('shieldSpecialSummerHealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fallRogue: { event: EVENTS.fall, @@ -3372,9 +2955,7 @@ let shield = { notes: t('shieldSpecialFallRogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fallWarrior: { event: EVENTS.fall, @@ -3384,9 +2965,7 @@ let shield = { notes: t('shieldSpecialFallWarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fallHealer: { event: EVENTS.fall, @@ -3396,9 +2975,7 @@ let shield = { notes: t('shieldSpecialFallHealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, winter2015Rogue: { event: EVENTS.winter2015, @@ -3408,9 +2985,7 @@ let shield = { notes: t('shieldSpecialWinter2015RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2015Warrior: { event: EVENTS.winter2015, @@ -3420,9 +2995,7 @@ let shield = { notes: t('shieldSpecialWinter2015WarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2015Healer: { event: EVENTS.winter2015, @@ -3432,9 +3005,7 @@ let shield = { notes: t('shieldSpecialWinter2015HealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, spring2015Rogue: { event: EVENTS.spring2015, @@ -3444,9 +3015,7 @@ let shield = { notes: t('shieldSpecialSpring2015RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2015Warrior: { event: EVENTS.spring2015, @@ -3456,9 +3025,7 @@ let shield = { notes: t('shieldSpecialSpring2015WarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2015Healer: { event: EVENTS.spring2015, @@ -3468,9 +3035,7 @@ let shield = { notes: t('shieldSpecialSpring2015HealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summer2015Rogue: { event: EVENTS.summer2015, @@ -3480,9 +3045,7 @@ let shield = { notes: t('shieldSpecialSummer2015RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2015Warrior: { event: EVENTS.summer2015, @@ -3492,9 +3055,7 @@ let shield = { notes: t('shieldSpecialSummer2015WarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2015Healer: { event: EVENTS.summer2015, @@ -3504,9 +3065,7 @@ let shield = { notes: t('shieldSpecialSummer2015HealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fall2015Rogue: { event: EVENTS.fall2015, @@ -3516,9 +3075,7 @@ let shield = { notes: t('shieldSpecialFall2015RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2015Warrior: { event: EVENTS.fall2015, @@ -3528,9 +3085,7 @@ let shield = { notes: t('shieldSpecialFall2015WarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2015Healer: { event: EVENTS.fall2015, @@ -3540,9 +3095,7 @@ let shield = { notes: t('shieldSpecialFall2015HealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, winter2016Rogue: { event: EVENTS.winter2016, @@ -3552,9 +3105,7 @@ let shield = { notes: t('shieldSpecialWinter2016RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2016Warrior: { event: EVENTS.winter2016, @@ -3564,9 +3115,7 @@ let shield = { notes: t('shieldSpecialWinter2016WarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2016Healer: { event: EVENTS.winter2016, @@ -3576,9 +3125,7 @@ let shield = { notes: t('shieldSpecialWinter2016HealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, spring2016Rogue: { event: EVENTS.spring2016, @@ -3588,9 +3135,7 @@ let shield = { notes: t('shieldSpecialSpring2016RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2016Warrior: { event: EVENTS.spring2016, @@ -3600,9 +3145,7 @@ let shield = { notes: t('shieldSpecialSpring2016WarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2016Healer: { event: EVENTS.spring2016, @@ -3612,9 +3155,7 @@ let shield = { notes: t('shieldSpecialSpring2016HealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summer2016Rogue: { event: EVENTS.summer2016, @@ -3624,9 +3165,7 @@ let shield = { notes: t('shieldSpecialSummer2016RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2016Warrior: { event: EVENTS.summer2016, @@ -3636,9 +3175,7 @@ let shield = { notes: t('shieldSpecialSummer2016WarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2016Healer: { event: EVENTS.summer2016, @@ -3648,9 +3185,7 @@ let shield = { notes: t('shieldSpecialSummer2016HealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fall2016Rogue: { event: EVENTS.fall2016, @@ -3660,9 +3195,7 @@ let shield = { notes: t('shieldSpecialFall2016RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2016Warrior: { event: EVENTS.fall2016, @@ -3672,9 +3205,7 @@ let shield = { notes: t('shieldSpecialFall2016WarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2016Healer: { event: EVENTS.fall2016, @@ -3684,9 +3215,7 @@ let shield = { notes: t('shieldSpecialFall2016HealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, winter2017Rogue: { event: EVENTS.winter2017, @@ -3696,9 +3225,7 @@ let shield = { notes: t('shieldSpecialWinter2017RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2017Warrior: { event: EVENTS.winter2017, @@ -3708,9 +3235,7 @@ let shield = { notes: t('shieldSpecialWinter2017WarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2017Healer: { event: EVENTS.winter2017, @@ -3720,9 +3245,7 @@ let shield = { notes: t('shieldSpecialWinter2017HealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, spring2017Rogue: { event: EVENTS.spring2017, @@ -3732,9 +3255,7 @@ let shield = { notes: t('shieldSpecialSpring2017RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2017Warrior: { event: EVENTS.spring2017, @@ -3744,9 +3265,7 @@ let shield = { notes: t('shieldSpecialSpring2017WarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2017Healer: { event: EVENTS.spring2017, @@ -3756,9 +3275,7 @@ let shield = { notes: t('shieldSpecialSpring2017HealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summer2017Rogue: { event: EVENTS.summer2017, @@ -3768,9 +3285,7 @@ let shield = { notes: t('shieldSpecialSummer2017RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2017Warrior: { event: EVENTS.summer2017, @@ -3780,9 +3295,7 @@ let shield = { notes: t('shieldSpecialSummer2017WarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2017Healer: { event: EVENTS.summer2017, @@ -3792,9 +3305,7 @@ let shield = { notes: t('shieldSpecialSummer2017HealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fall2017Rogue: { event: EVENTS.fall2017, @@ -3804,9 +3315,7 @@ let shield = { notes: t('shieldSpecialFall2017RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2017Warrior: { event: EVENTS.fall2017, @@ -3816,9 +3325,7 @@ let shield = { notes: t('shieldSpecialFall2017WarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2017Healer: { event: EVENTS.fall2017, @@ -3828,9 +3335,7 @@ let shield = { notes: t('shieldSpecialFall2017HealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, winter2018Rogue: { event: EVENTS.winter2018, @@ -3840,9 +3345,7 @@ let shield = { notes: t('shieldSpecialWinter2018RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2018Warrior: { event: EVENTS.winter2018, @@ -3852,9 +3355,7 @@ let shield = { notes: t('shieldSpecialWinter2018WarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2018Healer: { event: EVENTS.winter2018, @@ -3864,9 +3365,7 @@ let shield = { notes: t('shieldSpecialWinter2018HealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, spring2018Rogue: { event: EVENTS.spring2018, @@ -3876,9 +3375,7 @@ let shield = { notes: t('weaponSpecialSpring2018RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2018Warrior: { event: EVENTS.spring2018, @@ -3888,9 +3385,7 @@ let shield = { notes: t('shieldSpecialSpring2018WarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2018Healer: { event: EVENTS.spring2018, @@ -3900,9 +3395,7 @@ let shield = { notes: t('shieldSpecialSpring2018HealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summer2018Rogue: { event: EVENTS.summer2018, @@ -3912,9 +3405,7 @@ let shield = { notes: t('weaponSpecialSummer2018RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2018Warrior: { event: EVENTS.summer2018, @@ -3924,9 +3415,7 @@ let shield = { notes: t('shieldSpecialSummer2018WarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2018Healer: { event: EVENTS.summer2018, @@ -3936,9 +3425,7 @@ let shield = { notes: t('shieldSpecialSummer2018HealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fall2018Rogue: { event: EVENTS.fall2018, @@ -3948,9 +3435,7 @@ let shield = { notes: t('shieldSpecialFall2018RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2018Warrior: { event: EVENTS.fall2018, @@ -3960,9 +3445,7 @@ let shield = { notes: t('shieldSpecialFall2018WarriorNotes', { con: 7 }), value: 70, con: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2018Healer: { event: EVENTS.fall2018, @@ -3972,9 +3455,7 @@ let shield = { notes: t('shieldSpecialFall2018HealerNotes', { con: 9 }), value: 70, con: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, winter2019Rogue: { event: EVENTS.winter2019, @@ -4108,7 +3589,7 @@ let shield = { }, }; -let weapon = { +const weapon = { 0: backerGear.weaponSpecial0, 1: contributorGear.weaponSpecial1, 2: backerGear.weaponSpecial2, @@ -4217,9 +3698,7 @@ let weapon = { notes: t('weaponSpecialYetiNotes', { str: 15 }), str: 15, value: 90, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, ski: { event: EVENTS.winter, @@ -4229,9 +3708,7 @@ let weapon = { notes: t('weaponSpecialSkiNotes', { str: 8 }), str: 8, value: 90, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, candycane: { event: EVENTS.winter, @@ -4243,9 +3720,7 @@ let weapon = { int: 15, per: 7, value: 160, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, snowflake: { event: EVENTS.winter, @@ -4255,9 +3730,7 @@ let weapon = { notes: t('weaponSpecialSnowflakeNotes', { int: 9 }), int: 9, value: 90, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, springRogue: { event: EVENTS.spring, @@ -4267,9 +3740,7 @@ let weapon = { notes: t('weaponSpecialSpringRogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, springWarrior: { event: EVENTS.spring, @@ -4279,9 +3750,7 @@ let weapon = { notes: t('weaponSpecialSpringWarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, springMage: { event: EVENTS.spring, @@ -4293,9 +3762,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, springHealer: { event: EVENTS.spring, @@ -4305,9 +3772,7 @@ let weapon = { notes: t('weaponSpecialSpringHealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summerRogue: { event: EVENTS.summer, @@ -4317,9 +3782,7 @@ let weapon = { notes: t('weaponSpecialSummerRogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summerWarrior: { event: EVENTS.summer, @@ -4329,9 +3792,7 @@ let weapon = { notes: t('weaponSpecialSummerWarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summerMage: { event: EVENTS.summer, @@ -4343,9 +3804,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summerHealer: { event: EVENTS.summer, @@ -4355,9 +3814,7 @@ let weapon = { notes: t('weaponSpecialSummerHealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fallRogue: { event: EVENTS.fall, @@ -4367,9 +3824,7 @@ let weapon = { notes: t('weaponSpecialFallRogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fallWarrior: { event: EVENTS.fall, @@ -4379,9 +3834,7 @@ let weapon = { notes: t('weaponSpecialFallWarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fallMage: { event: EVENTS.fall, @@ -4393,9 +3846,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fallHealer: { event: EVENTS.fall, @@ -4405,9 +3856,7 @@ let weapon = { notes: t('weaponSpecialFallHealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, winter2015Rogue: { event: EVENTS.winter2015, @@ -4417,9 +3866,7 @@ let weapon = { notes: t('weaponSpecialWinter2015RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2015Warrior: { event: EVENTS.winter2015, @@ -4429,9 +3876,7 @@ let weapon = { notes: t('weaponSpecialWinter2015WarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2015Mage: { event: EVENTS.winter2015, @@ -4443,9 +3888,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2015Healer: { event: EVENTS.winter2015, @@ -4455,9 +3898,7 @@ let weapon = { notes: t('weaponSpecialWinter2015HealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, spring2015Rogue: { event: EVENTS.spring2015, @@ -4467,9 +3908,7 @@ let weapon = { notes: t('weaponSpecialSpring2015RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2015Warrior: { event: EVENTS.spring2015, @@ -4479,9 +3918,7 @@ let weapon = { notes: t('weaponSpecialSpring2015WarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2015Mage: { event: EVENTS.spring2015, @@ -4493,9 +3930,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2015Healer: { event: EVENTS.spring2015, @@ -4505,9 +3940,7 @@ let weapon = { notes: t('weaponSpecialSpring2015HealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summer2015Rogue: { event: EVENTS.summer2015, @@ -4517,9 +3950,7 @@ let weapon = { notes: t('weaponSpecialSummer2015RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2015Warrior: { event: EVENTS.summer2015, @@ -4529,9 +3960,7 @@ let weapon = { notes: t('weaponSpecialSummer2015WarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2015Mage: { event: EVENTS.summer2015, @@ -4543,9 +3972,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2015Healer: { event: EVENTS.summer2015, @@ -4555,9 +3982,7 @@ let weapon = { notes: t('weaponSpecialSummer2015HealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fall2015Rogue: { event: EVENTS.fall2015, @@ -4567,9 +3992,7 @@ let weapon = { notes: t('weaponSpecialFall2015RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2015Warrior: { event: EVENTS.fall2015, @@ -4579,9 +4002,7 @@ let weapon = { notes: t('weaponSpecialFall2015WarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2015Mage: { event: EVENTS.fall2015, @@ -4593,9 +4014,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2015Healer: { event: EVENTS.fall2015, @@ -4605,9 +4024,7 @@ let weapon = { notes: t('weaponSpecialFall2015HealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, winter2016Rogue: { event: EVENTS.winter2016, @@ -4617,9 +4034,7 @@ let weapon = { notes: t('weaponSpecialWinter2016RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2016Warrior: { event: EVENTS.winter2016, @@ -4629,9 +4044,7 @@ let weapon = { notes: t('weaponSpecialWinter2016WarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2016Mage: { event: EVENTS.winter2016, @@ -4643,9 +4056,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2016Healer: { event: EVENTS.winter2016, @@ -4655,9 +4066,7 @@ let weapon = { notes: t('weaponSpecialWinter2016HealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, spring2016Rogue: { event: EVENTS.spring2016, @@ -4667,9 +4076,7 @@ let weapon = { notes: t('weaponSpecialSpring2016RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2016Warrior: { event: EVENTS.spring2016, @@ -4679,9 +4086,7 @@ let weapon = { notes: t('weaponSpecialSpring2016WarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2016Mage: { event: EVENTS.spring2016, @@ -4693,9 +4098,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2016Healer: { event: EVENTS.spring2016, @@ -4705,9 +4108,7 @@ let weapon = { notes: t('weaponSpecialSpring2016HealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summer2016Rogue: { event: EVENTS.summer2016, @@ -4717,9 +4118,7 @@ let weapon = { notes: t('weaponSpecialSummer2016RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2016Warrior: { event: EVENTS.summer2016, @@ -4729,9 +4128,7 @@ let weapon = { notes: t('weaponSpecialSummer2016WarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2016Mage: { event: EVENTS.summer2016, @@ -4743,9 +4140,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2016Healer: { event: EVENTS.summer2016, @@ -4755,9 +4150,7 @@ let weapon = { notes: t('weaponSpecialSummer2016HealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fall2016Rogue: { event: EVENTS.fall2016, @@ -4767,9 +4160,7 @@ let weapon = { notes: t('weaponSpecialFall2016RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2016Warrior: { event: EVENTS.fall2016, @@ -4779,9 +4170,7 @@ let weapon = { notes: t('weaponSpecialFall2016WarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2016Mage: { event: EVENTS.fall2016, @@ -4793,9 +4182,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2016Healer: { event: EVENTS.fall2016, @@ -4805,9 +4192,7 @@ let weapon = { notes: t('weaponSpecialFall2016HealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, winter2017Rogue: { event: EVENTS.winter2017, @@ -4817,9 +4202,7 @@ let weapon = { notes: t('weaponSpecialWinter2017RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2017Warrior: { event: EVENTS.winter2017, @@ -4829,9 +4212,7 @@ let weapon = { notes: t('weaponSpecialWinter2017WarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2017Mage: { event: EVENTS.winter2017, @@ -4843,9 +4224,7 @@ let weapon = { value: 170, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2017Healer: { event: EVENTS.winter2017, @@ -4855,9 +4234,7 @@ let weapon = { notes: t('weaponSpecialWinter2017HealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, spring2017Rogue: { event: EVENTS.spring2017, @@ -4867,9 +4244,7 @@ let weapon = { notes: t('weaponSpecialSpring2017RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2017Warrior: { event: EVENTS.spring2017, @@ -4879,9 +4254,7 @@ let weapon = { notes: t('weaponSpecialSpring2017WarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2017Mage: { event: EVENTS.spring2017, @@ -4893,9 +4266,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2017Healer: { event: EVENTS.spring2017, @@ -4905,9 +4276,7 @@ let weapon = { notes: t('weaponSpecialSpring2017HealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summer2017Rogue: { event: EVENTS.summer2017, @@ -4917,9 +4286,7 @@ let weapon = { notes: t('weaponSpecialSummer2017RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2017Warrior: { event: EVENTS.summer2017, @@ -4929,9 +4296,7 @@ let weapon = { notes: t('weaponSpecialSummer2017WarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2017Mage: { event: EVENTS.summer2017, @@ -4943,9 +4308,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2017Healer: { event: EVENTS.summer2017, @@ -4955,9 +4318,7 @@ let weapon = { notes: t('weaponSpecialSummer2017HealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fall2017Rogue: { event: EVENTS.fall2017, @@ -4967,9 +4328,7 @@ let weapon = { notes: t('weaponSpecialFall2017RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2017Warrior: { event: EVENTS.fall2017, @@ -4979,9 +4338,7 @@ let weapon = { notes: t('weaponSpecialFall2017WarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2017Mage: { event: EVENTS.fall2017, @@ -4993,9 +4350,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2017Healer: { event: EVENTS.fall2017, @@ -5005,9 +4360,7 @@ let weapon = { notes: t('weaponSpecialFall2017HealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, winter2018Rogue: { event: EVENTS.winter2018, @@ -5017,9 +4370,7 @@ let weapon = { notes: t('weaponSpecialWinter2018RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2018Warrior: { event: EVENTS.winter2018, @@ -5029,9 +4380,7 @@ let weapon = { notes: t('weaponSpecialWinter2018WarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2018Mage: { event: EVENTS.winter2018, @@ -5043,9 +4392,7 @@ let weapon = { value: 170, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, winter2018Healer: { event: EVENTS.winter2018, @@ -5055,9 +4402,7 @@ let weapon = { notes: t('weaponSpecialWinter2018HealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'winter'; - }, + canBuy: () => CURRENT_SEASON === 'winter', }, spring2018Rogue: { event: EVENTS.spring2018, @@ -5067,9 +4412,7 @@ let weapon = { notes: t('weaponSpecialSpring2018RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2018Warrior: { event: EVENTS.spring2018, @@ -5079,9 +4422,7 @@ let weapon = { notes: t('weaponSpecialSpring2018WarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2018Mage: { event: EVENTS.spring2018, @@ -5093,9 +4434,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, spring2018Healer: { event: EVENTS.spring2018, @@ -5105,9 +4444,7 @@ let weapon = { notes: t('weaponSpecialSpring2018HealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'spring'; - }, + canBuy: () => CURRENT_SEASON === 'spring', }, summer2018Rogue: { event: EVENTS.summer2018, @@ -5117,9 +4454,7 @@ let weapon = { notes: t('weaponSpecialSummer2018RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2018Warrior: { event: EVENTS.summer2018, @@ -5129,9 +4464,7 @@ let weapon = { notes: t('weaponSpecialSummer2018WarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2018Mage: { event: EVENTS.summer2018, @@ -5143,9 +4476,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, summer2018Healer: { event: EVENTS.summer2018, @@ -5155,9 +4486,7 @@ let weapon = { notes: t('weaponSpecialSummer2018HealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'summer'; - }, + canBuy: () => CURRENT_SEASON === 'summer', }, fall2018Rogue: { event: EVENTS.fall2018, @@ -5167,9 +4496,7 @@ let weapon = { notes: t('weaponSpecialFall2018RogueNotes', { str: 8 }), value: 80, str: 8, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2018Warrior: { event: EVENTS.fall2018, @@ -5179,9 +4506,7 @@ let weapon = { notes: t('weaponSpecialFall2018WarriorNotes', { str: 15 }), value: 90, str: 15, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2018Mage: { event: EVENTS.fall2018, @@ -5193,9 +4518,7 @@ let weapon = { value: 160, int: 15, per: 7, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, fall2018Healer: { event: EVENTS.fall2018, @@ -5205,9 +4528,7 @@ let weapon = { notes: t('weaponSpecialFall2018HealerNotes', { int: 9 }), value: 90, int: 9, - canBuy: () => { - return CURRENT_SEASON === 'fall'; - }, + canBuy: () => CURRENT_SEASON === 'fall', }, winter2019Rogue: { event: EVENTS.winter2019, @@ -5377,4 +4698,4 @@ export { headAccessory, shield, weapon, -}; \ No newline at end of file +}; diff --git a/website/common/script/content/gear/sets/special/special-backer.js b/website/common/script/content/gear/sets/special/special-backer.js index d4b7a4e6f3..599009fce6 100644 --- a/website/common/script/content/gear/sets/special/special-backer.js +++ b/website/common/script/content/gear/sets/special/special-backer.js @@ -1,16 +1,14 @@ import { ownsItem } from '../../gear-helper'; import t from '../../../translation'; -let isBackerOfLevel = (tierRequirement, ownedItem) => { - return (user) => { - let backer = user.backer; - let tier = Number(backer && backer.tier); +const isBackerOfLevel = (tierRequirement, ownedItem) => user => { + const { backer } = user; + const tier = Number(backer && backer.tier); - return tier >= tierRequirement || ownsItem(ownedItem)(user); - }; + return tier >= tierRequirement || ownsItem(ownedItem)(user); }; -let armorSpecial0 = { +const armorSpecial0 = { text: t('armorSpecial0Text'), notes: t('armorSpecial0Notes', { con: 20 }), con: 20, @@ -18,7 +16,7 @@ let armorSpecial0 = { canOwn: isBackerOfLevel(45, 'armor_special_0'), }; -let armorSpecial2 = { +const armorSpecial2 = { text: t('armorSpecial2Text'), notes: t('armorSpecial2Notes', { attrs: 25 }), int: 25, @@ -27,7 +25,7 @@ let armorSpecial2 = { canOwn: isBackerOfLevel(300, 'armor_special_2'), }; -let headSpecial0 = { +const headSpecial0 = { text: t('headSpecial0Text'), notes: t('headSpecial0Notes', { int: 20 }), int: 20, @@ -35,7 +33,7 @@ let headSpecial0 = { canOwn: isBackerOfLevel(45, 'head_special_0'), }; -let headSpecial2 = { +const headSpecial2 = { text: t('headSpecial2Text'), notes: t('headSpecial2Notes', { attrs: 25 }), int: 25, @@ -44,7 +42,7 @@ let headSpecial2 = { canOwn: isBackerOfLevel(300, 'head_special_2'), }; -let shieldSpecial0 = { +const shieldSpecial0 = { text: t('shieldSpecial0Text'), notes: t('shieldSpecial0Notes', { per: 20 }), per: 20, @@ -52,7 +50,7 @@ let shieldSpecial0 = { canOwn: isBackerOfLevel(45, 'shield_special_0'), }; -let weaponSpecial0 = { +const weaponSpecial0 = { text: t('weaponSpecial0Text'), notes: t('weaponSpecial0Notes', { str: 20 }), str: 20, @@ -60,7 +58,7 @@ let weaponSpecial0 = { canOwn: isBackerOfLevel(70, 'weapon_special_0'), }; -let weaponSpecial2 = { +const weaponSpecial2 = { text: t('weaponSpecial2Text'), notes: t('weaponSpecial2Notes', { attrs: 25 }), str: 25, @@ -69,7 +67,7 @@ let weaponSpecial2 = { canOwn: isBackerOfLevel(300, 'weapon_special_2'), }; -let weaponSpecial3 = { +const weaponSpecial3 = { text: t('weaponSpecial3Text'), notes: t('weaponSpecial3Notes', { attrs: 17 }), str: 17, @@ -88,4 +86,4 @@ export { weaponSpecial0, weaponSpecial2, weaponSpecial3, -}; \ No newline at end of file +}; diff --git a/website/common/script/content/gear/sets/special/special-contributor.js b/website/common/script/content/gear/sets/special/special-contributor.js index 478d3ff5cb..c5de25b1c6 100644 --- a/website/common/script/content/gear/sets/special/special-contributor.js +++ b/website/common/script/content/gear/sets/special/special-contributor.js @@ -1,16 +1,14 @@ import { ownsItem } from '../../gear-helper'; import t from '../../../translation'; -let isContributorOfLevel = (tierRequirement, ownedItem) => { - return (user) => { - let contributor = user.contributor; - let tier = contributor && contributor.level; +const isContributorOfLevel = (tierRequirement, ownedItem) => user => { + const { contributor } = user; + const tier = contributor && contributor.level; - return Number(tier) >= tierRequirement || ownsItem(ownedItem)(user); - }; + return Number(tier) >= tierRequirement || ownsItem(ownedItem)(user); }; -let armorSpecial1 = { +const armorSpecial1 = { text: t('armorSpecial1Text'), notes: t('armorSpecial1Notes', { attrs: 6 }), con: 6, @@ -21,7 +19,7 @@ let armorSpecial1 = { canOwn: isContributorOfLevel(2, 'armor_special_1'), }; -let headSpecial1 = { +const headSpecial1 = { text: t('headSpecial1Text'), notes: t('headSpecial1Notes', { attrs: 6 }), con: 6, @@ -32,7 +30,7 @@ let headSpecial1 = { canOwn: isContributorOfLevel(3, 'head_special_1'), }; -let shieldSpecial1 = { +const shieldSpecial1 = { text: t('shieldSpecial1Text'), notes: t('shieldSpecial1Notes', { attrs: 6 }), con: 6, @@ -43,7 +41,7 @@ let shieldSpecial1 = { canOwn: isContributorOfLevel(5, 'shield_special_1'), }; -let weaponSpecial1 = { +const weaponSpecial1 = { text: t('weaponSpecial1Text'), notes: t('weaponSpecial1Notes', { attrs: 6 }), str: 6, @@ -54,15 +52,15 @@ let weaponSpecial1 = { canOwn: isContributorOfLevel(4, 'weapon_special_1'), }; -let weaponSpecialCritical = { +const weaponSpecialCritical = { text: t('weaponSpecialCriticalText'), notes: t('weaponSpecialCriticalNotes', { attrs: 40 }), str: 40, per: 40, value: 200, - canOwn: (user) => { - let hasCriticalFlag = user.contributor && user.contributor.critical; - let alreadyHasItem = ownsItem('weapon_special_critical')(user); + canOwn: user => { + const hasCriticalFlag = user.contributor && user.contributor.critical; + const alreadyHasItem = ownsItem('weapon_special_critical')(user); return hasCriticalFlag || alreadyHasItem; }, @@ -74,4 +72,4 @@ export { shieldSpecial1, weaponSpecial1, weaponSpecialCritical, -}; \ No newline at end of file +}; diff --git a/website/common/script/content/gear/sets/special/special-takeThis.js b/website/common/script/content/gear/sets/special/special-takeThis.js index efd21f1d53..2bab7ec50c 100644 --- a/website/common/script/content/gear/sets/special/special-takeThis.js +++ b/website/common/script/content/gear/sets/special/special-takeThis.js @@ -1,8 +1,8 @@ import t from '../../../translation'; -let armorSpecialTakeThis = { +const armorSpecialTakeThis = { text: t('armorSpecialTakeThisText'), - notes: t('armorSpecialTakeThisNotes', {attrs: 5}), + notes: t('armorSpecialTakeThisNotes', { attrs: 5 }), value: 0, con: 5, int: 5, @@ -10,9 +10,9 @@ let armorSpecialTakeThis = { str: 5, }; -let backSpecialTakeThis = { +const backSpecialTakeThis = { text: t('backSpecialTakeThisText'), - notes: t('backSpecialTakeThisNotes', {attrs: 1}), + notes: t('backSpecialTakeThisNotes', { attrs: 1 }), value: 0, con: 1, int: 1, @@ -20,9 +20,9 @@ let backSpecialTakeThis = { str: 1, }; -let bodySpecialTakeThis = { +const bodySpecialTakeThis = { text: t('bodySpecialTakeThisText'), - notes: t('bodySpecialTakeThisNotes', {attrs: 1}), + notes: t('bodySpecialTakeThisNotes', { attrs: 1 }), value: 0, con: 1, int: 1, @@ -30,9 +30,9 @@ let bodySpecialTakeThis = { str: 1, }; -let headSpecialTakeThis = { +const headSpecialTakeThis = { text: t('headSpecialTakeThisText'), - notes: t('headSpecialTakeThisNotes', {attrs: 5}), + notes: t('headSpecialTakeThisNotes', { attrs: 5 }), value: 0, con: 5, int: 5, @@ -40,9 +40,9 @@ let headSpecialTakeThis = { str: 5, }; -let shieldSpecialTakeThis = { +const shieldSpecialTakeThis = { text: t('shieldSpecialTakeThisText'), - notes: t('shieldSpecialTakeThisNotes', {attrs: 5}), + notes: t('shieldSpecialTakeThisNotes', { attrs: 5 }), value: 0, con: 5, int: 5, @@ -50,9 +50,9 @@ let shieldSpecialTakeThis = { str: 5, }; -let weaponSpecialTakeThis = { +const weaponSpecialTakeThis = { text: t('weaponSpecialTakeThisText'), - notes: t('weaponSpecialTakeThisNotes', {attrs: 5}), + notes: t('weaponSpecialTakeThisNotes', { attrs: 5 }), value: 0, con: 5, int: 5, @@ -67,4 +67,4 @@ export { headSpecialTakeThis, shieldSpecialTakeThis, weaponSpecialTakeThis, -}; \ No newline at end of file +}; diff --git a/website/common/script/content/gear/sets/special/special-wondercon.js b/website/common/script/content/gear/sets/special/special-wondercon.js index e4862d9c3b..13cf36b55d 100644 --- a/website/common/script/content/gear/sets/special/special-wondercon.js +++ b/website/common/script/content/gear/sets/special/special-wondercon.js @@ -1,48 +1,48 @@ import t from '../../../translation'; -let backSpecialWonderconRed = { +const backSpecialWonderconRed = { text: t('backSpecialWonderconRedText'), notes: t('backSpecialWonderconRedNotes'), value: 0, mystery: 'wondercon', }; -let backSpecialWonderconBlack = { +const backSpecialWonderconBlack = { text: t('backSpecialWonderconBlackText'), notes: t('backSpecialWonderconBlackNotes'), value: 0, mystery: 'wondercon', }; -let bodySpecialWonderconRed = { +const bodySpecialWonderconRed = { text: t('bodySpecialWonderconRedText'), notes: t('bodySpecialWonderconRedNotes'), value: 0, mystery: 'wondercon', }; -let bodySpecialWonderconGold = { +const bodySpecialWonderconGold = { text: t('bodySpecialWonderconGoldText'), notes: t('bodySpecialWonderconGoldNotes'), value: 0, mystery: 'wondercon', }; -let bodySpecialWonderconBlack = { +const bodySpecialWonderconBlack = { text: t('bodySpecialWonderconBlackText'), notes: t('bodySpecialWonderconBlackNotes'), value: 0, mystery: 'wondercon', }; -let eyewearSpecialWonderconRed = { +const eyewearSpecialWonderconRed = { text: t('eyewearSpecialWonderconRedText'), notes: t('eyewearSpecialWonderconRedNotes'), value: 0, mystery: 'wondercon', }; -let eyewearSpecialWonderconBlack = { +const eyewearSpecialWonderconBlack = { text: t('eyewearSpecialWonderconBlackText'), notes: t('eyewearSpecialWonderconBlackNotes'), value: 0, @@ -57,4 +57,4 @@ export { bodySpecialWonderconBlack, eyewearSpecialWonderconRed, eyewearSpecialWonderconBlack, -}; \ No newline at end of file +}; diff --git a/website/common/script/content/gear/sets/warrior.js b/website/common/script/content/gear/sets/warrior.js index 689ad716fc..2a479d6caa 100644 --- a/website/common/script/content/gear/sets/warrior.js +++ b/website/common/script/content/gear/sets/warrior.js @@ -1,6 +1,6 @@ import t from '../../translation'; -let armor = { +const armor = { 1: { text: t('armorWarrior1Text'), notes: t('armorWarrior1Notes', { con: 3 }), @@ -34,7 +34,7 @@ let armor = { }, }; -let head = { +const head = { 1: { text: t('headWarrior1Text'), notes: t('headWarrior1Notes', { str: 2 }), @@ -68,7 +68,7 @@ let head = { }, }; -let shield = { +const shield = { 1: { text: t('shieldWarrior1Text'), notes: t('shieldWarrior1Notes', { con: 2 }), @@ -102,10 +102,12 @@ let shield = { }, }; -let weapon = { +const weapon = { 0: { text: t('weaponWarrior0Text'), - notes: t('weaponWarrior0Notes'), value: 1 }, + notes: t('weaponWarrior0Notes'), + value: 1, + }, 1: { text: t('weaponWarrior1Text'), notes: t('weaponWarrior1Notes', { str: 3 }), @@ -150,4 +152,4 @@ export { head, shield, weapon, -}; \ No newline at end of file +}; diff --git a/website/common/script/content/gear/sets/wizard.js b/website/common/script/content/gear/sets/wizard.js index f3f34e9156..db1178714c 100644 --- a/website/common/script/content/gear/sets/wizard.js +++ b/website/common/script/content/gear/sets/wizard.js @@ -1,6 +1,6 @@ import t from '../../translation'; -let armor = { +const armor = { 1: { text: t('armorWizard1Text'), notes: t('armorWizard1Notes', { int: 2 }), @@ -34,7 +34,7 @@ let armor = { }, }; -let head = { +const head = { 1: { text: t('headWizard1Text'), notes: t('headWizard1Notes', { per: 2 }), @@ -68,18 +68,20 @@ let head = { }, }; -let shield = { +const shield = { // Wizard's weapons are two handed // And thus do not have shields // But the content structure still expects an object }; -let weapon = { +const weapon = { 0: { twoHanded: true, text: t('weaponWizard0Text'), - notes: t('weaponWizard0Notes'), value: 0 }, + notes: t('weaponWizard0Notes'), + value: 0, + }, 1: { twoHanded: true, text: t('weaponWizard1Text'), @@ -136,4 +138,4 @@ export { head, shield, weapon, -}; \ No newline at end of file +}; diff --git a/website/common/script/content/gear/shield.js b/website/common/script/content/gear/shield.js index b04af0f52a..46afefd3bb 100644 --- a/website/common/script/content/gear/shield.js +++ b/website/common/script/content/gear/shield.js @@ -1,19 +1,19 @@ import cloneDeep from 'lodash/cloneDeep'; -import {shield as baseShield} from './sets/base'; +import { shield as baseShield } from './sets/base'; -import {shield as healerShield} from './sets/healer'; -import {weapon as rogueWeapon} from './sets/rogue'; -import {shield as warriorShield} from './sets/warrior'; -import {shield as wizardShield} from './sets/wizard'; +import { shield as healerShield } from './sets/healer'; +import { weapon as rogueWeapon } from './sets/rogue'; +import { shield as warriorShield } from './sets/warrior'; +import { shield as wizardShield } from './sets/wizard'; -import {shield as armoireShield} from './sets/armoire'; -import {shield as mysteryShield} from './sets/mystery'; -import {shield as specialShield} from './sets/special'; +import { shield as armoireShield } from './sets/armoire'; +import { shield as mysteryShield } from './sets/mystery'; +import { shield as specialShield } from './sets/special'; -let rogueShield = cloneDeep(rogueWeapon); +const rogueShield = cloneDeep(rogueWeapon); -let shield = { +const shield = { base: baseShield, warrior: warriorShield, diff --git a/website/common/script/content/gear/weapon.js b/website/common/script/content/gear/weapon.js index ca1a0527b0..98ce8e5829 100644 --- a/website/common/script/content/gear/weapon.js +++ b/website/common/script/content/gear/weapon.js @@ -1,17 +1,17 @@ import t from '../translation'; -import {weapon as baseWeapon} from './sets/base'; +import { weapon as baseWeapon } from './sets/base'; -import {weapon as healerWeapon} from './sets/healer'; -import {weapon as rogueWeapon} from './sets/rogue'; -import {weapon as warriorWeapon} from './sets/warrior'; -import {weapon as wizardWeapon} from './sets/wizard'; +import { weapon as healerWeapon } from './sets/healer'; +import { weapon as rogueWeapon } from './sets/rogue'; +import { weapon as warriorWeapon } from './sets/warrior'; +import { weapon as wizardWeapon } from './sets/wizard'; -import {weapon as armoireWeapon} from './sets/armoire'; -import {weapon as mysteryWeapon} from './sets/mystery'; -import {weapon as specialWeapon} from './sets/special'; +import { weapon as armoireWeapon } from './sets/armoire'; +import { weapon as mysteryWeapon } from './sets/mystery'; +import { weapon as specialWeapon } from './sets/special'; -let weapon = { +const weapon = { base: baseWeapon, warrior: warriorWeapon, @@ -27,31 +27,31 @@ let weapon = { // Add Two Handed message to all weapons const rtlLanguages = [ 'ae', /* Avestan */ - 'ar', /* 'العربية', Arabic */ - 'arc', /* Aramaic */ - 'bcc', /* 'بلوچی مکرانی', Southern Balochi */ - 'bqi', /* 'بختياري', Bakthiari */ - 'ckb', /* 'Soranî / کوردی', Sorani */ - 'dv', /* Dhivehi */ - 'fa', /* 'فارسی', Persian */ - 'glk', /* 'گیلکی', Gilaki */ - 'he', /* 'עברית', Hebrew */ - 'ku', /* 'Kurdî / كوردی', Kurdish */ - 'mzn', /* 'مازِرونی', Mazanderani */ - 'nqo', /* N'Ko */ - 'pnb', /* 'پنجابی', Western Punjabi */ - 'ps', /* 'پښتو', Pashto, */ - 'sd', /* 'سنڌي', Sindhi */ - 'ug', /* 'Uyghurche / ئۇيغۇرچە', Uyghur */ - 'ur', /* 'اردو', Urdu */ - 'yi', /* 'ייִדיש', Yiddish */ + 'ar', /* 'العربية', Arabic */ + 'arc', /* Aramaic */ + 'bcc', /* 'بلوچی مکرانی', Southern Balochi */ + 'bqi', /* 'بختياري', Bakthiari */ + 'ckb', /* 'Soranî / کوردی', Sorani */ + 'dv', /* Dhivehi */ + 'fa', /* 'فارسی', Persian */ + 'glk', /* 'گیلکی', Gilaki */ + 'he', /* 'עברית', Hebrew */ + 'ku', /* 'Kurdî / كوردی', Kurdish */ + 'mzn', /* 'مازِرونی', Mazanderani */ + 'nqo', /* N'Ko */ + 'pnb', /* 'پنجابی', Western Punjabi */ + 'ps', /* 'پښتو', Pashto, */ + 'sd', /* 'سنڌي', Sindhi */ + 'ug', /* 'Uyghurche / ئۇيغۇرچە', Uyghur */ + 'ur', /* 'اردو', Urdu */ + 'yi', /* 'ייִדיש', Yiddish */ ]; -for (let key in weapon) { +for (const key in weapon) { const set = weapon[key]; - for (let weaponKey in set) { + for (const weaponKey in set) { const item = set[weaponKey]; const oldnotes = item.notes; - item.notes = (lang) => { + item.notes = lang => { const twoHandedText = item.twoHanded ? t('twoHandedItem')(lang) : ''; if (rtlLanguages.indexOf(lang) !== -1) { diff --git a/website/common/script/content/hatching-potions.js b/website/common/script/content/hatching-potions.js index b2001593d0..8a77355f3a 100644 --- a/website/common/script/content/hatching-potions.js +++ b/website/common/script/content/hatching-potions.js @@ -6,13 +6,11 @@ import t from './translation'; const CURRENT_SEASON = 'October'; function hasQuestAchievementFunction (key) { - return (user) => { - return user.achievements.quests && - user.achievements.quests[key] > 0; - }; + return user => user.achievements.quests + && user.achievements.quests[key] > 0; } -let drops = { +const drops = { Base: { value: 2, text: t('hatchingPotionBase'), @@ -55,7 +53,7 @@ let drops = { }, }; -let premium = { +const premium = { RoyalPurple: { value: 2, text: t('hatchingPotionRoyalPurple'), @@ -279,7 +277,7 @@ each(wacky, (pot, key) => { }); }); -let all = assign({}, drops, premium, wacky); +const all = assign({}, drops, premium, wacky); export { drops, diff --git a/website/common/script/content/index.js b/website/common/script/content/index.js index f0daf5e527..b020eb10d0 100644 --- a/website/common/script/content/index.js +++ b/website/common/script/content/index.js @@ -2,7 +2,7 @@ import defaults from 'lodash/defaults'; import each from 'lodash/each'; import moment from 'moment'; import t from './translation'; -import {tasksByCategory} from './tasks'; +import { tasksByCategory } from './tasks'; import { CLASSES, @@ -12,8 +12,6 @@ import { ANIMAL_COLOR_ACHIEVEMENTS, } from './constants'; -const api = {}; - import achievements from './achievements'; import * as eggs from './eggs'; @@ -27,7 +25,7 @@ import { } from './quests'; import appearances from './appearance'; -import {backgroundsTree, backgroundsFlat} from './appearance/backgrounds'; +import { backgroundsTree, backgroundsFlat } from './appearance/backgrounds'; import spells from './spells'; import subscriptionBlocks from './subscriptionBlocks'; import faq from './faq'; @@ -37,6 +35,8 @@ import loginIncentives from './loginIncentives'; import officialPinnedItems from './officialPinnedItems'; +const api = {}; + api.achievements = achievements; api.questSeriesAchievements = QUEST_SERIES_ACHIEVEMENTS; api.animalColorAchievements = ANIMAL_COLOR_ACHIEVEMENTS; @@ -810,17 +810,15 @@ api.food = { /* eslint-enable camelcase */ }; -each(api.food, (food, key) => { - return defaults(food, { - value: 1, - key, - notes: t('foodNotes'), - canBuy () { - return false; - }, - canDrop: false, - }); -}); +each(api.food, (food, key) => defaults(food, { + value: 1, + key, + notes: t('foodNotes'), + canBuy () { + return false; + }, + canDrop: false, +})); api.appearances = appearances; @@ -837,9 +835,9 @@ api.userDefaults = { down: false, attribute: 'per', tags: [ - t('defaultTag1'), // Work - t('defaultTag4'), // School - t('defaultTag6'), // Chores + t('defaultTag1'), // Work + t('defaultTag4'), // School + t('defaultTag6'), // Chores ], }, { type: 'habit', @@ -849,7 +847,7 @@ api.userDefaults = { down: true, attribute: 'str', tags: [ - t('defaultTag3'), // Health + Wellness + t('defaultTag3'), // Health + Wellness ], }, { type: 'habit', @@ -859,8 +857,8 @@ api.userDefaults = { down: true, attribute: 'str', tags: [ - t('defaultTag2'), // Exercise - t('defaultTag3'), // Health + Wellness + t('defaultTag2'), // Exercise + t('defaultTag3'), // Health + Wellness ], }, ], diff --git a/website/common/script/content/loginIncentives.js b/website/common/script/content/loginIncentives.js index 691d3d929d..c4d1363b4f 100644 --- a/website/common/script/content/loginIncentives.js +++ b/website/common/script/content/loginIncentives.js @@ -4,7 +4,7 @@ import { MAX_INCENTIVES } from '../constants'; // NOTE do not import this file alone but only access it through common.content // so that it's already compiled export default function getLoginIncentives (api) { - let loginIncentives = { + const loginIncentives = { 1: { rewardKey: ['armor_special_bardRobes'], reward: [api.gear.flat.armor_special_bardRobes], @@ -645,7 +645,7 @@ export default function getLoginIncentives (api) { // We could also, use a list, but then we would be cloning each of the rewards. // Create a new array if we want the loginIncentives to be immutable in the future let nextRewardKey; - range(MAX_INCENTIVES + 1).reverse().forEach(function addNextRewardLink (index) { + range(MAX_INCENTIVES + 1).reverse().forEach(index => { if (loginIncentives[index] && loginIncentives[index].rewardKey) { loginIncentives[index].nextRewardAt = nextRewardKey; nextRewardKey = index; @@ -659,7 +659,7 @@ export default function getLoginIncentives (api) { }); let prevRewardKey; - range(MAX_INCENTIVES + 1).forEach(function addPrevRewardLink (index) { + range(MAX_INCENTIVES + 1).forEach(index => { loginIncentives[index].prevRewardKey = prevRewardKey; if (loginIncentives[index].rewardKey) prevRewardKey = index; }); diff --git a/website/common/script/content/mystery-sets.js b/website/common/script/content/mystery-sets.js index d4223f3406..560e26ebf0 100644 --- a/website/common/script/content/mystery-sets.js +++ b/website/common/script/content/mystery-sets.js @@ -1,7 +1,7 @@ import each from 'lodash/each'; import t from './translation'; -let mysterySets = { +const mysterySets = { 201402: { start: '2014-02-22', end: '2014-02-28', diff --git a/website/common/script/content/quests.js b/website/common/script/content/quests.js index 8fb2c90afc..be1795c4da 100644 --- a/website/common/script/content/quests.js +++ b/website/common/script/content/quests.js @@ -6,9 +6,9 @@ import { USER_CAN_OWN_QUEST_CATEGORIES, } from './constants'; -let userCanOwnQuestCategories = USER_CAN_OWN_QUEST_CATEGORIES; +const userCanOwnQuestCategories = USER_CAN_OWN_QUEST_CATEGORIES; -let quests = { +const quests = { dilatory: { text: t('questDilatoryText'), notes: t('questDilatoryNotes'), @@ -2261,7 +2261,7 @@ let quests = { unlockCondition: { condition: 'party invite', incentiveThreshold: 7, - text: t('loginReward', {count: 7}), + text: t('loginReward', { count: 7 }), }, collect: { shard: { @@ -2292,7 +2292,7 @@ let quests = { unlockCondition: { condition: 'party invite', incentiveThreshold: 22, - text: t('loginReward', {count: 22}), + text: t('loginReward', { count: 22 }), }, boss: { name: t('questMoon2Boss'), @@ -2322,7 +2322,7 @@ let quests = { unlockCondition: { condition: 'party invite', incentiveThreshold: 40, - text: t('loginReward', {count: 40}), + text: t('loginReward', { count: 40 }), }, boss: { name: t('questMoon3Boss'), @@ -3527,9 +3527,7 @@ each(quests, (v, key) => { } }); -let questsByLevel = sortBy(quests, (quest) => { - return quest.lvl || 0; -}); +const questsByLevel = sortBy(quests, quest => quest.lvl || 0); export { quests, diff --git a/website/common/script/content/spells.js b/website/common/script/content/spells.js index 657294fe26..809f4a6e8d 100644 --- a/website/common/script/content/spells.js +++ b/website/common/script/content/spells.js @@ -1,5 +1,5 @@ -import t from './translation'; import each from 'lodash/each'; +import t from './translation'; import { NotAuthorized } from '../libs/errors'; import statsComputed from '../libs/statsComputed'; import crit from '../fns/crit'; @@ -35,7 +35,7 @@ function calculateBonus (value, stat, critVal = 1, statScale = 0.5) { return (value < 0 ? 1 : value + 1) + stat * statScale * critVal; } -let spells = {}; +const spells = {}; spells.wizard = { fireball: { // Burst of Flames @@ -60,8 +60,8 @@ spells.wizard = { target: 'party', notes: t('spellWizardMPHealNotes'), cast (user, target) { - each(target, (member) => { - let bonus = statsComputed(user).int; + each(target, member => { + const bonus = statsComputed(user).int; if (user._id !== member._id && member.stats.class !== 'wizard') { member.stats.mp += Math.ceil(diminishingReturns(bonus, 25, 125)); } @@ -75,8 +75,8 @@ spells.wizard = { target: 'party', notes: t('spellWizardEarthNotes'), cast (user, target) { - each(target, (member) => { - let bonus = statsComputed(user).int - user.stats.buffs.int; + each(target, member => { + const bonus = statsComputed(user).int - user.stats.buffs.int; if (!member.stats.buffs.int) member.stats.buffs.int = 0; member.stats.buffs.int += Math.ceil(diminishingReturns(bonus, 30, 200)); }); @@ -102,7 +102,7 @@ spells.warrior = { target: 'task', notes: t('spellWarriorSmashNotes'), cast (user, target) { - let bonus = statsComputed(user).str * crit.crit(user, 'con'); + const bonus = statsComputed(user).str * crit.crit(user, 'con'); target.value += diminishingReturns(bonus, 2.5, 35); if (!user.party.quest.progress.up) user.party.quest.progress.up = 0; user.party.quest.progress.up += diminishingReturns(bonus, 55, 70); @@ -115,7 +115,7 @@ spells.warrior = { target: 'self', notes: t('spellWarriorDefensiveStanceNotes'), cast (user) { - let bonus = statsComputed(user).con - user.stats.buffs.con; + const bonus = statsComputed(user).con - user.stats.buffs.con; if (!user.stats.buffs.con) user.stats.buffs.con = 0; user.stats.buffs.con += Math.ceil(diminishingReturns(bonus, 40, 200)); }, @@ -127,8 +127,8 @@ spells.warrior = { target: 'party', notes: t('spellWarriorValorousPresenceNotes'), cast (user, target) { - each(target, (member) => { - let bonus = statsComputed(user).str - user.stats.buffs.str; + each(target, member => { + const bonus = statsComputed(user).str - user.stats.buffs.str; if (!member.stats.buffs.str) member.stats.buffs.str = 0; member.stats.buffs.str += Math.ceil(diminishingReturns(bonus, 20, 200)); }); @@ -141,8 +141,8 @@ spells.warrior = { target: 'party', notes: t('spellWarriorIntimidateNotes'), cast (user, target) { - each(target, (member) => { - let bonus = statsComputed(user).con - user.stats.buffs.con; + each(target, member => { + const bonus = statsComputed(user).con - user.stats.buffs.con; if (!member.stats.buffs.con) member.stats.buffs.con = 0; member.stats.buffs.con += Math.ceil(diminishingReturns(bonus, 24, 200)); }); @@ -158,7 +158,7 @@ spells.rogue = { target: 'task', notes: t('spellRoguePickPocketNotes'), cast (user, target) { - let bonus = calculateBonus(target.value, statsComputed(user).per); + const bonus = calculateBonus(target.value, statsComputed(user).per); user.stats.gp += diminishingReturns(bonus, 25, 75); }, }, @@ -169,8 +169,8 @@ spells.rogue = { target: 'task', notes: t('spellRogueBackStabNotes'), cast (user, target, req) { - let _crit = crit.crit(user, 'str', 0.3); - let bonus = calculateBonus(target.value, statsComputed(user).str, _crit); + const _crit = crit.crit(user, 'str', 0.3); + const bonus = calculateBonus(target.value, statsComputed(user).str, _crit); user.stats.exp += diminishingReturns(bonus, 75, 50); user.stats.gp += diminishingReturns(bonus, 18, 75); updateStats(user, user.stats, req); @@ -183,8 +183,8 @@ spells.rogue = { target: 'party', notes: t('spellRogueToolsOfTradeNotes'), cast (user, target) { - each(target, (member) => { - let bonus = statsComputed(user).per - user.stats.buffs.per; + each(target, member => { + const bonus = statsComputed(user).per - user.stats.buffs.per; if (!member.stats.buffs.per) member.stats.buffs.per = 0; member.stats.buffs.per += Math.ceil(diminishingReturns(bonus, 100, 50)); }); @@ -223,7 +223,7 @@ spells.healer = { target: 'tasks', notes: t('spellHealerBrightnessNotes'), cast (user, tasks) { - each(tasks, (task) => { + each(tasks, task => { if (task.type !== 'reward') { task.value += 4 * (statsComputed(user).int / (statsComputed(user).int + 40)); } @@ -237,8 +237,8 @@ spells.healer = { target: 'party', notes: t('spellHealerProtectAuraNotes'), cast (user, target) { - each(target, (member) => { - let bonus = statsComputed(user).con - user.stats.buffs.con; + each(target, member => { + const bonus = statsComputed(user).con - user.stats.buffs.con; if (!member.stats.buffs.con) member.stats.buffs.con = 0; member.stats.buffs.con += Math.ceil(diminishingReturns(bonus, 200, 200)); }); @@ -251,7 +251,7 @@ spells.healer = { target: 'party', notes: t('spellHealerHealAllNotes'), cast (user, target) { - each(target, (member) => { + each(target, member => { member.stats.hp += (statsComputed(user).con + statsComputed(user).int + 5) * 0.04; if (member.stats.hp > 50) member.stats.hp = 50; }); @@ -397,7 +397,7 @@ spells.special = { if (!user.achievements.nye) user.achievements.nye = 0; user.achievements.nye++; } else { - each([user, target], (u) => { + each([user, target], u => { if (!u.achievements.nye) u.achievements.nye = 0; u.achievements.nye++; }); @@ -407,13 +407,15 @@ spells.special = { const senderName = user.profile.name; target.items.special.nyeReceived.push(senderName); - if (target.addNotification) target.addNotification('CARD_RECEIVED', { - card: 'nye', - from: { - id: user._id, - name: senderName, - }, - }); + if (target.addNotification) { + target.addNotification('CARD_RECEIVED', { + card: 'nye', + from: { + id: user._id, + name: senderName, + }, + }); + } target.flags.cardReceived = true; user.stats.gp -= 10; @@ -432,7 +434,7 @@ spells.special = { if (!user.achievements.valentine) user.achievements.valentine = 0; user.achievements.valentine++; } else { - each([user, target], (u) => { + each([user, target], u => { if (!u.achievements.valentine) u.achievements.valentine = 0; u.achievements.valentine++; }); @@ -442,13 +444,15 @@ spells.special = { const senderName = user.profile.name; target.items.special.valentineReceived.push(senderName); - if (target.addNotification) target.addNotification('CARD_RECEIVED', { - card: 'valentine', - from: { - id: user._id, - name: senderName, - }, - }); + if (target.addNotification) { + target.addNotification('CARD_RECEIVED', { + card: 'valentine', + from: { + id: user._id, + name: senderName, + }, + }); + } target.flags.cardReceived = true; user.stats.gp -= 10; @@ -467,7 +471,7 @@ spells.special = { if (!user.achievements.greeting) user.achievements.greeting = 0; user.achievements.greeting++; } else { - each([user, target], (u) => { + each([user, target], u => { if (!u.achievements.greeting) u.achievements.greeting = 0; u.achievements.greeting++; }); @@ -477,13 +481,15 @@ spells.special = { const senderName = user.profile.name; target.items.special.greetingReceived.push(senderName); - if (target.addNotification) target.addNotification('CARD_RECEIVED', { - card: 'greeting', - from: { - id: user._id, - name: senderName, - }, - }); + if (target.addNotification) { + target.addNotification('CARD_RECEIVED', { + card: 'greeting', + from: { + id: user._id, + name: senderName, + }, + }); + } target.flags.cardReceived = true; user.stats.gp -= 10; @@ -502,7 +508,7 @@ spells.special = { if (!user.achievements.thankyou) user.achievements.thankyou = 0; user.achievements.thankyou++; } else { - each([user, target], (u) => { + each([user, target], u => { if (!u.achievements.thankyou) u.achievements.thankyou = 0; u.achievements.thankyou++; }); @@ -512,13 +518,15 @@ spells.special = { const senderName = user.profile.name; target.items.special.thankyouReceived.push(senderName); - if (target.addNotification) target.addNotification('CARD_RECEIVED', { - card: 'thankyou', - from: { - id: user._id, - name: senderName, - }, - }); + if (target.addNotification) { + target.addNotification('CARD_RECEIVED', { + card: 'thankyou', + from: { + id: user._id, + name: senderName, + }, + }); + } target.flags.cardReceived = true; user.stats.gp -= 10; @@ -537,7 +545,7 @@ spells.special = { if (!user.achievements.birthday) user.achievements.birthday = 0; user.achievements.birthday++; } else { - each([user, target], (u) => { + each([user, target], u => { if (!u.achievements.birthday) u.achievements.birthday = 0; u.achievements.birthday++; }); @@ -547,13 +555,15 @@ spells.special = { const senderName = user.profile.name; target.items.special.birthdayReceived.push(senderName); - if (target.addNotification) target.addNotification('CARD_RECEIVED', { - card: 'birthday', - from: { - id: user._id, - name: senderName, - }, - }); + if (target.addNotification) { + target.addNotification('CARD_RECEIVED', { + card: 'birthday', + from: { + id: user._id, + name: senderName, + }, + }); + } target.flags.cardReceived = true; user.stats.gp -= 10; @@ -572,7 +582,7 @@ spells.special = { if (!user.achievements.congrats) user.achievements.congrats = 0; user.achievements.congrats++; } else { - each([user, target], (u) => { + each([user, target], u => { if (!u.achievements.congrats) u.achievements.congrats = 0; u.achievements.congrats++; }); @@ -582,13 +592,15 @@ spells.special = { const senderName = user.profile.name; target.items.special.congratsReceived.push(senderName); - if (target.addNotification) target.addNotification('CARD_RECEIVED', { - card: 'congrats', - from: { - id: user._id, - name: senderName, - }, - }); + if (target.addNotification) { + target.addNotification('CARD_RECEIVED', { + card: 'congrats', + from: { + id: user._id, + name: senderName, + }, + }); + } target.flags.cardReceived = true; user.stats.gp -= 10; @@ -607,7 +619,7 @@ spells.special = { if (!user.achievements.getwell) user.achievements.getwell = 0; user.achievements.getwell++; } else { - each([user, target], (u) => { + each([user, target], u => { if (!u.achievements.getwell) u.achievements.getwell = 0; u.achievements.getwell++; }); @@ -617,13 +629,15 @@ spells.special = { const senderName = user.profile.name; target.items.special.getwellReceived.push(senderName); - if (target.addNotification) target.addNotification('CARD_RECEIVED', { - card: 'getwell', - from: { - id: user._id, - name: senderName, - }, - }); + if (target.addNotification) { + target.addNotification('CARD_RECEIVED', { + card: 'getwell', + from: { + id: user._id, + name: senderName, + }, + }); + } target.flags.cardReceived = true; user.stats.gp -= 10; @@ -642,7 +656,7 @@ spells.special = { if (!user.achievements.goodluck) user.achievements.goodluck = 0; user.achievements.goodluck++; } else { - each([user, target], (u) => { + each([user, target], u => { if (!u.achievements.goodluck) u.achievements.goodluck = 0; u.achievements.goodluck++; }); @@ -652,13 +666,15 @@ spells.special = { const senderName = user.profile.name; target.items.special.goodluckReceived.push(senderName); - if (target.addNotification) target.addNotification('CARD_RECEIVED', { - card: 'goodluck', - from: { - id: user._id, - name: senderName, - }, - }); + if (target.addNotification) { + target.addNotification('CARD_RECEIVED', { + card: 'goodluck', + from: { + id: user._id, + name: senderName, + }, + }); + } target.flags.cardReceived = true; user.stats.gp -= 10; @@ -666,10 +682,10 @@ spells.special = { }, }; -each(spells, (spellClass) => { +each(spells, spellClass => { each(spellClass, (spell, key) => { spell.key = key; - let _cast = spell.cast; + const _cast = spell.cast; spell.cast = function castSpell (user, target, req) { _cast(user, target, req); user.stats.mp -= spell.mana; diff --git a/website/common/script/content/stable.js b/website/common/script/content/stable.js index 016611e72f..387633d220 100644 --- a/website/common/script/content/stable.js +++ b/website/common/script/content/stable.js @@ -10,16 +10,16 @@ import { } from './hatching-potions'; import t from './translation'; -let petInfo = {}; -let mountInfo = {}; +const petInfo = {}; +const mountInfo = {}; function constructSet (type, eggs, potions) { - let pets = {}; - let mounts = {}; + const pets = {}; + const mounts = {}; - each(eggs, (egg) => { - each(potions, (potion) => { - let key = `${egg.key}-${potion.key}`; + each(eggs, egg => { + each(potions, potion => { + const key = `${egg.key}-${potion.key}`; function getAnimalData (text) { return { @@ -49,11 +49,11 @@ function constructSet (type, eggs, potions) { } function constructPetOnlySet (type, eggs, potions) { - let pets = {}; + const pets = {}; - each(eggs, (egg) => { - each(potions, (potion) => { - let key = `${egg.key}-${potion.key}`; + each(eggs, egg => { + each(potions, potion => { + const key = `${egg.key}-${potion.key}`; function getAnimalData (text) { return { @@ -76,12 +76,12 @@ function constructPetOnlySet (type, eggs, potions) { return pets; } -let [dropPets, dropMounts] = constructSet('drop', dropEggs, dropPotions); -let [premiumPets, premiumMounts] = constructSet('premium', dropEggs, premiumPotions); -let [questPets, questMounts] = constructSet('quest', questEggs, dropPotions); -let wackyPets = constructPetOnlySet('wacky', dropEggs, wackyPotions); +const [dropPets, dropMounts] = constructSet('drop', dropEggs, dropPotions); +const [premiumPets, premiumMounts] = constructSet('premium', dropEggs, premiumPotions); +const [questPets, questMounts] = constructSet('quest', questEggs, dropPotions); +const wackyPets = constructPetOnlySet('wacky', dropEggs, wackyPotions); -let specialPets = { +const specialPets = { 'Wolf-Veteran': 'veteranWolf', 'Wolf-Cerberus': 'cerberusPup', 'Dragon-Hydra': 'hydra', @@ -106,7 +106,7 @@ let specialPets = { 'Gryphon-Gryphatrice': 'gryphatrice', }; -let specialMounts = { +const specialMounts = { 'BearCub-Polar': 'polarBear', 'LionCub-Ethereal': 'etherealLion', 'MantisShrimp-Base': 'mantisShrimp', diff --git a/website/common/script/content/subscriptionBlocks.js b/website/common/script/content/subscriptionBlocks.js index 69beedc6db..4de82ea711 100644 --- a/website/common/script/content/subscriptionBlocks.js +++ b/website/common/script/content/subscriptionBlocks.js @@ -1,7 +1,7 @@ /* eslint-disable camelcase */ import each from 'lodash/each'; -let subscriptionBlocks = { +const subscriptionBlocks = { basic_earned: { target: 'user', canSubscribe: true, @@ -50,8 +50,6 @@ let subscriptionBlocks = { }, }; -each(subscriptionBlocks, function createKeys (b, k) { - return b.key = k; -}); +each(subscriptionBlocks, (b, k) => b.key = k); export default subscriptionBlocks; diff --git a/website/common/script/content/time-travelers.js b/website/common/script/content/time-travelers.js index 7a6b2a9ae3..eb18d340dc 100644 --- a/website/common/script/content/time-travelers.js +++ b/website/common/script/content/time-travelers.js @@ -7,19 +7,17 @@ import reduce from 'lodash/reduce'; import mysterySets from './mystery-sets'; import gear from './gear'; -let mystery = mysterySets; +const mystery = mysterySets; -each(mystery, (v, k) => { - return v.items = filter(gear.flat, { - mystery: k, - }); -}); +each(mystery, (v, k) => v.items = filter(gear.flat, { + mystery: k, +})); -let timeTravelerStore = (user) => { +const timeTravelerStore = user => { let ownedKeys; - let owned = user.items.gear.owned; - let mysteryItems = user.purchased.plan.mysteryItems; - let unopenedGifts = typeof mysteryItems.toObject === 'function' ? mysteryItems.toObject() : mysteryItems; + const { owned } = user.items.gear; + const { mysteryItems } = user.purchased.plan; + const unopenedGifts = typeof mysteryItems.toObject === 'function' ? mysteryItems.toObject() : mysteryItems; ownedKeys = keys(typeof owned.toObject === 'function' ? owned.toObject() : owned); ownedKeys = union(ownedKeys, unopenedGifts); return reduce(mystery, (m, v, k) => { @@ -34,4 +32,4 @@ let timeTravelerStore = (user) => { export default { timeTravelerStore, mystery, -}; \ No newline at end of file +}; diff --git a/website/common/script/count.js b/website/common/script/count.js index 846b8efee5..6a15df8c8c 100644 --- a/website/common/script/count.js +++ b/website/common/script/count.js @@ -9,9 +9,8 @@ const DROP_ANIMALS = keys(content.pets); export function beastMasterProgress (pets = {}) { let count = 0; - each(DROP_ANIMALS, (animal) => { - if (pets[animal] > 0 || pets[animal] === -1) - count++; + each(DROP_ANIMALS, animal => { + if (pets[animal] > 0 || pets[animal] === -1) count++; }); return count; @@ -20,7 +19,7 @@ export function beastMasterProgress (pets = {}) { export function beastCount (pets = {}) { let count = 0; - each(DROP_ANIMALS, (animal) => { + each(DROP_ANIMALS, animal => { if (pets[animal] > 0) count++; }); @@ -30,9 +29,8 @@ export function beastCount (pets = {}) { export function dropPetsCurrentlyOwned (pets = {}) { let count = 0; - each(DROP_ANIMALS, (animal) => { - if (pets[animal] > 0) - count++; + each(DROP_ANIMALS, animal => { + if (pets[animal] > 0) count++; }); return count; @@ -41,36 +39,35 @@ export function dropPetsCurrentlyOwned (pets = {}) { export function mountMasterProgress (mounts = {}) { let count = 0; - each(DROP_ANIMALS, (animal) => { - if (mounts[animal]) - count++; + each(DROP_ANIMALS, animal => { + if (mounts[animal]) count++; }); return count; } export function remainingGearInSet (userGear = {}, set) { - let gear = filter(content.gear.flat, (item) => { - let setMatches = item.klass === set; - let hasItem = userGear[item.key]; + const gear = filter(content.gear.flat, item => { + const setMatches = item.klass === set; + const hasItem = userGear[item.key]; return setMatches && !hasItem; }); - let count = size(gear); + const count = size(gear); return count; } export function questsOfCategory (userQuests = {}, category) { - let quests = filter(content.quests, (quest) => { - let categoryMatches = quest.category === category; - let hasQuest = userQuests[quest.key]; + const quests = filter(content.quests, quest => { + const categoryMatches = quest.category === category; + const hasQuest = userQuests[quest.key]; return categoryMatches && hasQuest; }); - let count = size(quests); + const count = size(quests); return count; -} \ No newline at end of file +} diff --git a/website/common/script/cron.js b/website/common/script/cron.js index b93df4b80e..49ccbe86a5 100644 --- a/website/common/script/cron.js +++ b/website/common/script/cron.js @@ -28,11 +28,11 @@ export const DAY_MAPPING_STRING_TO_NUMBER = invert(DAY_MAPPING); */ function sanitizeOptions (o) { - let ref = Number(o.dayStart || 0); - let dayStart = !Number.isNaN(ref) && ref >= 0 && ref <= 24 ? ref : 0; + const ref = Number(o.dayStart || 0); + const dayStart = !Number.isNaN(ref) && ref >= 0 && ref <= 24 ? ref : 0; let timezoneOffset; - let timezoneOffsetDefault = Number(moment().zone()); + const timezoneOffsetDefault = Number(moment().zone()); if (isFinite(o.timezoneOffsetOverride)) { timezoneOffset = Number(o.timezoneOffsetOverride); @@ -46,7 +46,7 @@ function sanitizeOptions (o) { timezoneOffset = timezoneOffsetDefault; } - let now = o.now ? moment(o.now).zone(timezoneOffset) : moment().zone(timezoneOffset); + const now = o.now ? moment(o.now).zone(timezoneOffset) : moment().zone(timezoneOffset); // return a new object, we don't want to add "now" to user object return { dayStart, @@ -56,7 +56,7 @@ function sanitizeOptions (o) { } export function startOfWeek (options = {}) { - let o = sanitizeOptions(options); + const o = sanitizeOptions(options); return moment(o.now).startOf('week'); } @@ -69,8 +69,8 @@ export function startOfWeek (options = {}) { */ export function startOfDay (options = {}) { - let o = sanitizeOptions(options); - let dayStart = moment(o.now).startOf('day').add({ hours: o.dayStart }); + const o = sanitizeOptions(options); + const dayStart = moment(o.now).startOf('day').add({ hours: o.dayStart }); if (moment(o.now).hour() < o.dayStart) { dayStart.subtract({ days: 1 }); @@ -84,9 +84,9 @@ export function startOfDay (options = {}) { */ export function daysSince (yesterday, options = {}) { - let o = sanitizeOptions(options); - let startOfNow = startOfDay(defaults({ now: o.now }, o)); - let startOfYesterday = startOfDay(defaults({ now: yesterday }, o)); + const o = sanitizeOptions(options); + const startOfNow = startOfDay(defaults({ now: o.now }, o)); + const startOfYesterday = startOfDay(defaults({ now: yesterday }, o)); return startOfNow.diff(startOfYesterday, 'days'); } @@ -99,22 +99,22 @@ export function shouldDo (day, dailyTask, options = {}) { if (dailyTask.type !== 'daily' || dailyTask.startDate === null || dailyTask.everyX < 1 || dailyTask.everyX > 9999) { return false; } - let o = sanitizeOptions(options); - let startOfDayWithCDSTime = startOfDay(defaults({ now: day }, o)); + const o = sanitizeOptions(options); + const startOfDayWithCDSTime = startOfDay(defaults({ now: day }, o)); // The time portion of the Start Date is never visible to or modifiable by the user so we must ignore it. // Therefore, we must also ignore the time portion of the user's day start (startOfDayWithCDSTime), otherwise the date comparison will be wrong for some times. // NB: The user's day start date has already been converted to the PREVIOUS day's date if the time portion was before CDS. - let startDate = moment(dailyTask.startDate).zone(o.timezoneOffset).startOf('day'); + const startDate = moment(dailyTask.startDate).zone(o.timezoneOffset).startOf('day'); if (startDate > startOfDayWithCDSTime.startOf('day') && !options.nextDue) { return false; // Daily starts in the future } - let daysOfTheWeek = []; + const daysOfTheWeek = []; if (dailyTask.repeat) { - for (let [repeatDay, active] of Object.entries(dailyTask.repeat)) { + for (const [repeatDay, active] of Object.entries(dailyTask.repeat)) { if (!isFinite(DAY_MAPPING_STRING_TO_NUMBER[repeatDay])) continue; // eslint-disable-line no-continue if (active) daysOfTheWeek.push(parseInt(DAY_MAPPING_STRING_TO_NUMBER[repeatDay], 10)); } @@ -122,36 +122,36 @@ export function shouldDo (day, dailyTask, options = {}) { if (dailyTask.frequency === 'daily') { if (!dailyTask.everyX) return false; // error condition - let schedule = moment(startDate).recur() + const schedule = moment(startDate).recur() .every(dailyTask.everyX).days(); if (options.nextDue) { - let filteredDates = []; + const filteredDates = []; for (let i = 1; filteredDates.length < 6; i++) { - let calcDate = moment(startDate).add(dailyTask.everyX * i, 'days'); + const calcDate = moment(startDate).add(dailyTask.everyX * i, 'days'); if (calcDate > startOfDayWithCDSTime) filteredDates.push(calcDate); } return filteredDates; } return schedule.matches(startOfDayWithCDSTime); - } else if (dailyTask.frequency === 'weekly') { + } if (dailyTask.frequency === 'weekly') { let schedule = moment(startDate).recur(); - let differenceInWeeks = moment(startOfDayWithCDSTime).diff(moment(startDate), 'week'); - let matchEveryX = differenceInWeeks % dailyTask.everyX === 0; + const differenceInWeeks = moment(startOfDayWithCDSTime).diff(moment(startDate), 'week'); + const matchEveryX = differenceInWeeks % dailyTask.everyX === 0; if (daysOfTheWeek.length === 0) return false; schedule = schedule.every(daysOfTheWeek).daysOfWeek(); if (options.nextDue) { - let filteredDates = []; + const filteredDates = []; for (let i = 0; filteredDates.length < 6; i++) { for (let j = 0; j < daysOfTheWeek.length && filteredDates.length < 6; j++) { - let calcDate = moment(startDate).day(daysOfTheWeek[j]).add(dailyTask.everyX * i, 'weeks'); + const calcDate = moment(startDate).day(daysOfTheWeek[j]).add(dailyTask.everyX * i, 'weeks'); if (calcDate > startOfDayWithCDSTime) filteredDates.push(calcDate); } } - let sortedDates = filteredDates.sort((date1, date2) => { + const sortedDates = filteredDates.sort((date1, date2) => { if (date1.toDate() > date2.toDate()) return 1; if (date2.toDate() > date1.toDate()) return -1; return 0; @@ -160,15 +160,15 @@ export function shouldDo (day, dailyTask, options = {}) { } return schedule.matches(startOfDayWithCDSTime) && matchEveryX; - } else if (dailyTask.frequency === 'monthly') { + } if (dailyTask.frequency === 'monthly') { let schedule = moment(startDate).recur(); // Use startOf to ensure that we are always comparing month // to the next rather than a month from the day - let differenceInMonths = moment(startOfDayWithCDSTime).startOf('month') + const differenceInMonths = moment(startOfDayWithCDSTime).startOf('month') .diff(moment(startDate).startOf('month'), 'month', true); - let matchEveryX = differenceInMonths % dailyTask.everyX === 0; + const matchEveryX = differenceInMonths % dailyTask.everyX === 0; if (dailyTask.weeksOfMonth && dailyTask.weeksOfMonth.length > 0) { if (daysOfTheWeek.length === 0) return false; @@ -176,13 +176,13 @@ export function shouldDo (day, dailyTask, options = {}) { .every(dailyTask.weeksOfMonth).weeksOfMonthByDay(); if (options.nextDue) { - let filteredDates = []; + const filteredDates = []; for (let i = 1; filteredDates.length < 6; i++) { - let recurDate = moment(startDate).add(dailyTask.everyX * i, 'months'); - let calcDate = recurDate.clone(); + const recurDate = moment(startDate).add(dailyTask.everyX * i, 'months'); + const calcDate = recurDate.clone(); calcDate.day(daysOfTheWeek[0]); - let startDateWeek = Math.ceil(moment(startDate).date() / 7); + const startDateWeek = Math.ceil(moment(startDate).date() / 7); let calcDateWeek = Math.ceil(calcDate.date() / 7); // adjust week since weeks will rollover to other months @@ -193,19 +193,19 @@ export function shouldDo (day, dailyTask, options = {}) { calcDateWeek = Math.ceil(calcDate.date() / 7); - if (calcDate >= startOfDayWithCDSTime && - calcDateWeek === startDateWeek && calcDate.month() === recurDate.month()) filteredDates.push(calcDate); + if (calcDate >= startOfDayWithCDSTime + && calcDateWeek === startDateWeek && calcDate.month() === recurDate.month()) filteredDates.push(calcDate); } return filteredDates; } return schedule.matches(startOfDayWithCDSTime) && matchEveryX; - } else if (dailyTask.daysOfMonth && dailyTask.daysOfMonth.length > 0) { + } if (dailyTask.daysOfMonth && dailyTask.daysOfMonth.length > 0) { schedule = schedule.every(dailyTask.daysOfMonth).daysOfMonth(); if (options.nextDue) { - let filteredDates = []; + const filteredDates = []; for (let i = 1; filteredDates.length < 6; i++) { - let calcDate = moment(startDate).add(dailyTask.everyX * i, 'months'); + const calcDate = moment(startDate).add(dailyTask.everyX * i, 'months'); if (calcDate >= startOfDayWithCDSTime) filteredDates.push(calcDate); } return filteredDates; @@ -213,15 +213,15 @@ export function shouldDo (day, dailyTask, options = {}) { } return schedule.matches(startOfDayWithCDSTime) && matchEveryX; - } else if (dailyTask.frequency === 'yearly') { + } if (dailyTask.frequency === 'yearly') { let schedule = moment(startDate).recur(); schedule = schedule.every(dailyTask.everyX).years(); if (options.nextDue) { - let filteredDates = []; + const filteredDates = []; for (let i = 1; filteredDates.length < 6; i++) { - let calcDate = moment(startDate).add(dailyTask.everyX * i, 'years'); + const calcDate = moment(startDate).add(dailyTask.everyX * i, 'years'); if (calcDate > startOfDayWithCDSTime) filteredDates.push(calcDate); } return filteredDates; diff --git a/website/common/script/fns/autoAllocate.js b/website/common/script/fns/autoAllocate.js index 7fa344d78e..a7244b7613 100644 --- a/website/common/script/fns/autoAllocate.js +++ b/website/common/script/fns/autoAllocate.js @@ -16,17 +16,17 @@ import splitWhitespace from '../libs/splitWhitespace'; function getStatToAllocate (user) { let suggested; - let statsObj = user.stats.toObject ? user.stats.toObject() : user.stats; + const statsObj = user.stats.toObject ? user.stats.toObject() : user.stats; switch (user.preferences.allocationMode) { case 'flat': { - let stats = pick(statsObj, splitWhitespace('con str per int')); + const stats = pick(statsObj, splitWhitespace('con str per int')); return invert(stats)[min(values(stats))]; } case 'classbased': { let preference; - let lvlDiv7 = statsObj.lvl / 7; - let ideal = [lvlDiv7 * 3, lvlDiv7 * 2, lvlDiv7, lvlDiv7]; + const lvlDiv7 = statsObj.lvl / 7; + const ideal = [lvlDiv7 * 3, lvlDiv7 * 2, lvlDiv7, lvlDiv7]; switch (statsObj.class) { case 'wizard': { @@ -46,14 +46,14 @@ function getStatToAllocate (user) { } } - let diff = [ + const diff = [ statsObj[preference[0]] - ideal[0], statsObj[preference[1]] - ideal[1], statsObj[preference[2]] - ideal[2], statsObj[preference[3]] - ideal[3], ]; - suggested = findIndex(diff, (val) => { + suggested = findIndex(diff, val => { if (val === min(diff)) return true; }); @@ -76,7 +76,7 @@ function getStatToAllocate (user) { } export default function autoAllocate (user) { - let statToIncrease = getStatToAllocate(user); + const statToIncrease = getStatToAllocate(user); return user.stats[statToIncrease]++; } diff --git a/website/common/script/fns/crit.js b/website/common/script/fns/crit.js index a0109ebe11..230b0c9b73 100644 --- a/website/common/script/fns/crit.js +++ b/website/common/script/fns/crit.js @@ -2,12 +2,11 @@ import predictableRandom from './predictableRandom'; import statsComputed from '../libs/statsComputed'; function crit (user, stat = 'str', chance = 0.03) { - let s = statsComputed(user)[stat]; + const s = statsComputed(user)[stat]; if (predictableRandom(user) <= chance * (1 + s / 100)) { return 1.5 + 4 * s / (s + 200); - } else { - return 1; } + return 1; } export default { crit }; diff --git a/website/common/script/fns/handleTwoHanded.js b/website/common/script/fns/handleTwoHanded.js index 8e3911ccbd..d793e0a9d6 100644 --- a/website/common/script/fns/handleTwoHanded.js +++ b/website/common/script/fns/handleTwoHanded.js @@ -2,8 +2,8 @@ import content from '../content/index'; import i18n from '../i18n'; export default function handleTwoHanded (user, item, type = 'equipped', req = {}) { - let currentShield = content.gear.flat[user.items.gear[type].shield]; - let currentWeapon = content.gear.flat[user.items.gear[type].weapon]; + const currentShield = content.gear.flat[user.items.gear[type].shield]; + const currentWeapon = content.gear.flat[user.items.gear[type].weapon]; let message; diff --git a/website/common/script/fns/predictableRandom.js b/website/common/script/fns/predictableRandom.js index 9d449d1b85..9407339da3 100644 --- a/website/common/script/fns/predictableRandom.js +++ b/website/common/script/fns/predictableRandom.js @@ -15,12 +15,11 @@ export default function predictableRandom (user, seed) { seed = reduce(stats, (accumulator, val) => { if (isNumber(val)) { return accumulator + val; - } else { - return accumulator; } + return accumulator; }, 0); } - let x = Math.sin(seed++) * 10000; + const x = Math.sin(seed++) * 10000; return x - Math.floor(x); } diff --git a/website/common/script/fns/randomDrop.js b/website/common/script/fns/randomDrop.js index 11e808354e..86ef28643f 100644 --- a/website/common/script/fns/randomDrop.js +++ b/website/common/script/fns/randomDrop.js @@ -16,9 +16,8 @@ import statsComputed from '../libs/statsComputed'; // Clone a drop object maintaining its functions so that we can change it without affecting the original item function cloneDropItem (drop) { - return cloneDeepWith(drop, (val) => { - return isFunction(val) ? val : undefined; // undefined will be handled by lodash - }); + return cloneDeepWith(drop, val => (isFunction(val) ? val : undefined), // undefined will be handled by lodash + ); } function trueRandom () { @@ -31,19 +30,19 @@ export default function randomDrop (user, options, req = {}, analytics) { let dropMultiplier; let rarity; - let predictableRandom = options.predictableRandom || trueRandom; - let task = options.task; + const predictableRandom = options.predictableRandom || trueRandom; + const { task } = options; let chance = min([Math.abs(task.value - 21.27), 37.5]) / 150 + 0.02; - chance *= task.priority * // Task priority: +50% for Medium, +100% for Hard - (1 + (task.streak / 100 || 0)) * // Streak bonus: +1% per streak - (1 + statsComputed(user).per / 100) * // PERception: +1% per point - (1 + (user.contributor.level / 40 || 0)) * // Contrib levels: +2.5% per level - (1 + (user.achievements.rebirths / 20 || 0)) * // Rebirths: +5% per achievement - (1 + (user.achievements.streak / 200 || 0)) * // Streak achievements: +0.5% per achievement - (user._tmp.crit || 1) * (1 + 0.5 * (reduce(task.checklist, (m, i) => { // +50% per checklist item complete. TODO: make this into X individual drop chances instead - return m + (i.completed ? 1 : 0); // eslint-disable-line indent - }, 0) || 0)); // eslint-disable-line indent + chance *= task.priority // Task priority: +50% for Medium, +100% for Hard + * (1 + (task.streak / 100 || 0)) // Streak bonus: +1% per streak + * (1 + statsComputed(user).per / 100) // PERception: +1% per point + * (1 + (user.contributor.level / 40 || 0)) // Contrib levels: +2.5% per level + * (1 + (user.achievements.rebirths / 20 || 0)) // Rebirths: +5% per achievement + * (1 + (user.achievements.streak / 200 || 0)) // Streak achievements: +0.5% per achievement + * (user._tmp.crit || 1) * (1 + 0.5 * (reduce(task.checklist, (m, i) => // +50% per checklist item complete. TODO: make this into X individual drop chances instead + m + (i.completed ? 1 : 0), // eslint-disable-line indent + 0) || 0)); // eslint-disable-line indent chance = diminishingReturns(chance, 0.75); if (predictableRandom() < chance) { @@ -60,8 +59,8 @@ export default function randomDrop (user, options, req = {}, analytics) { dropMultiplier = 1; } - if (daysSince(user.items.lastDrop.date, user.preferences) === 0 && - user.items.lastDrop.count >= dropMultiplier * (5 + Math.floor(statsComputed(user).per / 25) + (user.contributor.level || 0))) { + if (daysSince(user.items.lastDrop.date, user.preferences) === 0 + && user.items.lastDrop.count >= dropMultiplier * (5 + Math.floor(statsComputed(user).per / 25) + (user.contributor.level || 0))) { return; } @@ -104,9 +103,7 @@ export default function randomDrop (user, options, req = {}, analytics) { } else { // common, 40% of 30% acceptableDrops = ['Base', 'White', 'Desert']; } - drop = cloneDropItem(randomVal(pickBy(content.hatchingPotions, (v, k) => { - return acceptableDrops.indexOf(k) >= 0; - }))); + drop = cloneDropItem(randomVal(pickBy(content.hatchingPotions, (v, k) => acceptableDrops.indexOf(k) >= 0))); user.items.hatchingPotions[drop.key] = user.items.hatchingPotions[drop.key] || 0; user.items.hatchingPotions[drop.key]++; diff --git a/website/common/script/fns/resetGear.js b/website/common/script/fns/resetGear.js index 91a9b12f6d..008b1a13b9 100644 --- a/website/common/script/fns/resetGear.js +++ b/website/common/script/fns/resetGear.js @@ -2,9 +2,9 @@ import each from 'lodash/each'; import content from '../content/index'; export default function resetGear (user) { - let gear = user.items.gear; + const { gear } = user.items; - each(['equipped', 'costume'], function resetUserGear (type) { + each(['equipped', 'costume'], type => { gear[type] = {}; gear[type].armor = 'armor_base_0'; gear[type].weapon = 'weapon_warrior_0'; @@ -14,7 +14,7 @@ export default function resetGear (user) { // Gear.owned is (was) a Mongo object so the _.each function iterates over hidden properties. // The content.gear.flat[k] check should prevent this causing an error - each(gear.owned, function resetOwnedGear (v, k) { + each(gear.owned, (v, k) => { if (gear.owned[k] && content.gear.flat[k] && content.gear.flat[k].value) { gear.owned[k] = false; } diff --git a/website/common/script/fns/ultimateGear.js b/website/common/script/fns/ultimateGear.js index ffd135ccbf..18cce620f7 100644 --- a/website/common/script/fns/ultimateGear.js +++ b/website/common/script/fns/ultimateGear.js @@ -1,15 +1,15 @@ -import content from '../content/index'; import lodashFind from 'lodash/find'; import reduce from 'lodash/reduce'; import includes from 'lodash/includes'; +import content from '../content/index'; export default function ultimateGear (user) { - let owned = user.items.gear.owned.toObject ? user.items.gear.owned.toObject() : user.items.gear.owned; + const owned = user.items.gear.owned.toObject ? user.items.gear.owned.toObject() : user.items.gear.owned; - content.classes.forEach((klass) => { + content.classes.forEach(klass => { if (user.achievements.ultimateGearSets[klass] !== true) { user.achievements.ultimateGearSets[klass] = reduce(['armor', 'shield', 'head', 'weapon'], (soFarGood, type) => { - let found = lodashFind(content.gear.tree[type][klass], { + const found = lodashFind(content.gear.tree[type][klass], { last: true, }); return soFarGood && (!found || owned[found.key] === true); @@ -28,11 +28,9 @@ export default function ultimateGear (user) { ultimateGearSetValues = Object.values(user.achievements.ultimateGearSets); } - let hasFullSet = includes(ultimateGearSetValues, true); + const hasFullSet = includes(ultimateGearSetValues, true); if (hasFullSet && user.flags.armoireEnabled !== true) { user.flags.armoireEnabled = true; } - - return; } diff --git a/website/common/script/i18n.js b/website/common/script/i18n.js index b144c3d87f..a66cf878be 100644 --- a/website/common/script/i18n.js +++ b/website/common/script/i18n.js @@ -19,7 +19,7 @@ function t (stringName) { locale = arguments[2]; } - let i18nNotSetup = !i18n.strings && !i18n.translations[locale]; + const i18nNotSetup = !i18n.strings && !i18n.translations[locale]; if (!locale || i18nNotSetup) { locale = 'en'; @@ -33,7 +33,7 @@ function t (stringName) { string = i18n.translations[locale] && i18n.translations[locale][stringName]; } - let clonedVars = clone(vars) || {}; + const clonedVars = clone(vars) || {}; clonedVars.locale = locale; diff --git a/website/common/script/index.js b/website/common/script/index.js index e70fe94f74..eb02863df8 100644 --- a/website/common/script/index.js +++ b/website/common/script/index.js @@ -1,20 +1,12 @@ // When using a common module from the website or the server NEVER import the module directly // but access it through `api` (the main common) module, otherwise you would require the non transpiled version of the file in production. -const api = {}; - import content from './content/index'; -api.content = content; import * as errors from './libs/errors'; -api.errors = errors; import i18n from './i18n'; -api.i18n = i18n; // TODO under api.libs.cron? import { shouldDo, daysSince, DAY_MAPPING } from './cron'; -api.shouldDo = shouldDo; -api.daysSince = daysSince; -api.DAY_MAPPING = DAY_MAPPING; import { MAX_HEALTH, @@ -34,90 +26,48 @@ import { CHAT_FLAG_FROM_SHADOW_MUTE, } from './constants'; -api.constants = { - MAX_INCENTIVES, - LARGE_GROUP_COUNT_MESSAGE_CUTOFF, - MAX_SUMMARY_SIZE_FOR_GUILDS, - MAX_SUMMARY_SIZE_FOR_CHALLENGES, - MIN_SHORTNAME_SIZE_FOR_CHALLENGES, - SUPPORTED_SOCIAL_NETWORKS, - GUILDS_PER_PAGE, - PARTY_LIMIT_MEMBERS, - CHAT_FLAG_LIMIT_FOR_HIDING, - CHAT_FLAG_FROM_MOD, - CHAT_FLAG_FROM_SHADOW_MUTE, -}; -// TODO Move these under api.constants -api.maxLevel = MAX_LEVEL; -api.maxHealth = MAX_HEALTH; -api.maxStatPoints = MAX_STAT_POINTS; -api.TAVERN_ID = TAVERN_ID; - // TODO under api.libs.statHelpers? import * as statHelpers from './statHelpers'; -api.capByLevel = statHelpers.capByLevel; -api.tnl = statHelpers.toNextLevel; -api.diminishingReturns = statHelpers.diminishingReturns; import splitWhitespace from './libs/splitWhitespace'; -api.$w = splitWhitespace; import refPush from './libs/refPush'; -api.refPush = refPush; import planGemLimits from './libs/planGemLimits'; -api.planGemLimits = planGemLimits; import preenTodos from './libs/preenTodos'; -api.preenTodos = preenTodos; import updateStore from './libs/updateStore'; -api.updateStore = updateStore; import inAppRewards from './libs/inAppRewards'; -api.inAppRewards = inAppRewards; import uuid from './libs/uuid'; -api.uuid = uuid; import taskDefaults from './libs/taskDefaults'; -api.taskDefaults = taskDefaults; import percent from './libs/percent'; -api.percent = percent; import gold from './libs/gold'; -api.gold = gold; import silver from './libs/silver'; -api.silver = silver; import noTags from './libs/noTags'; -api.noTags = noTags; import appliedTags from './libs/appliedTags'; -api.appliedTags = appliedTags; import pickDeep from './libs/pickDeep'; -api.pickDeep = pickDeep; import * as count from './count'; -api.count = count; import statsComputed from './libs/statsComputed'; -api.statsComputed = statsComputed; import shops from './libs/shops'; -api.shops = shops; import achievements from './libs/achievements'; -api.achievements = achievements; import randomVal from './libs/randomVal'; -api.randomVal = randomVal; import hasClass from './libs/hasClass'; -api.hasClass = hasClass; import autoAllocate from './fns/autoAllocate'; import crit from './fns/crit'; @@ -128,17 +78,6 @@ import resetGear from './fns/resetGear'; import ultimateGear from './fns/ultimateGear'; import updateStats from './fns/updateStats'; -api.fns = { - autoAllocate, - crit, - handleTwoHanded, - predictableRandom, - randomDrop, - resetGear, - ultimateGear, - updateStats, -}; - import scoreTask from './ops/scoreTask'; import sleep from './ops/sleep'; import allocateNow from './ops/stats/allocateNow'; @@ -166,6 +105,67 @@ import reset from './ops/reset'; import markPmsRead from './ops/markPMSRead'; import * as pinnedGearUtils from './ops/pinnedGearUtils'; +const api = {}; +api.content = content; +api.errors = errors; +api.i18n = i18n; +api.shouldDo = shouldDo; +api.daysSince = daysSince; +api.DAY_MAPPING = DAY_MAPPING; + +api.constants = { + MAX_INCENTIVES, + LARGE_GROUP_COUNT_MESSAGE_CUTOFF, + MAX_SUMMARY_SIZE_FOR_GUILDS, + MAX_SUMMARY_SIZE_FOR_CHALLENGES, + MIN_SHORTNAME_SIZE_FOR_CHALLENGES, + SUPPORTED_SOCIAL_NETWORKS, + GUILDS_PER_PAGE, + PARTY_LIMIT_MEMBERS, + CHAT_FLAG_LIMIT_FOR_HIDING, + CHAT_FLAG_FROM_MOD, + CHAT_FLAG_FROM_SHADOW_MUTE, +}; +// TODO Move these under api.constants +api.maxLevel = MAX_LEVEL; +api.maxHealth = MAX_HEALTH; +api.maxStatPoints = MAX_STAT_POINTS; +api.TAVERN_ID = TAVERN_ID; +api.capByLevel = statHelpers.capByLevel; +api.tnl = statHelpers.toNextLevel; +api.diminishingReturns = statHelpers.diminishingReturns; +api.$w = splitWhitespace; +api.refPush = refPush; +api.planGemLimits = planGemLimits; +api.preenTodos = preenTodos; +api.updateStore = updateStore; +api.inAppRewards = inAppRewards; +api.uuid = uuid; +api.taskDefaults = taskDefaults; +api.percent = percent; +api.gold = gold; +api.silver = silver; +api.noTags = noTags; +api.appliedTags = appliedTags; +api.pickDeep = pickDeep; +api.count = count; +api.statsComputed = statsComputed; +api.shops = shops; +api.achievements = achievements; +api.randomVal = randomVal; +api.hasClass = hasClass; + +api.fns = { + autoAllocate, + crit, + handleTwoHanded, + predictableRandom, + randomDrop, + resetGear, + ultimateGear, + updateStats, +}; + api.ops = { scoreTask, sleep, diff --git a/website/common/script/libs/achievements.js b/website/common/script/libs/achievements.js index 6e3f85c1ad..10b0020b0f 100644 --- a/website/common/script/libs/achievements.js +++ b/website/common/script/libs/achievements.js @@ -1,15 +1,15 @@ +import get from 'lodash/get'; import content from '../content/index'; import i18n from '../i18n'; -import get from 'lodash/get'; -let achievs = {}; -let achievsContent = content.achievements; +const achievs = {}; +const achievsContent = content.achievements; let index = 0; function contribText (contrib, backer, language) { if (!contrib && !backer) return; if (backer && backer.npc) return backer.npc; - let lvl = contrib && contrib.level; + const lvl = contrib && contrib.level; if (lvl && lvl > 0) { let contribTitle = ''; @@ -44,11 +44,11 @@ function _add (result, data) { } function _addSimpleWithCustomPath (result, user, data) { - let value = get(user, data.path); - let thisContent = achievsContent[data.key]; + const value = get(user, data.path); + const thisContent = achievsContent[data.key]; _add(result, { - title: i18n.t(thisContent.titleKey, {key: value}, data.language), + title: i18n.t(thisContent.titleKey, { key: value }, data.language), text: i18n.t(thisContent.textKey, data.language), icon: thisContent.icon, key: data.key, @@ -64,10 +64,10 @@ function _addQuest (result, user, data) { } function _addSimple (result, user, data) { - let value = user.achievements[data.path]; + const value = user.achievements[data.path]; - let key = data.key || data.path; - let thisContent = achievsContent[key]; + const key = data.key || data.path; + const thisContent = achievsContent[key]; _add(result, { title: i18n.t(thisContent.titleKey, data.language), @@ -80,14 +80,14 @@ function _addSimple (result, user, data) { } function _addSimpleWithMasterCount (result, user, data) { - let language = data.language; - let value = user.achievements[`${data.path}Count`] || 0; + const { language } = data; + const value = user.achievements[`${data.path}Count`] || 0; - let thisContent = achievsContent[data.path]; + const thisContent = achievsContent[data.path]; let text = i18n.t(thisContent.textKey, language); if (value > 0) { - text += i18n.t(thisContent.text2Key, {count: value}, language); + text += i18n.t(thisContent.text2Key, { count: value }, language); } _add(result, { @@ -102,14 +102,14 @@ function _addSimpleWithMasterCount (result, user, data) { } function _addSimpleWithCount (result, user, data) { - let value = user.achievements[data.path] || 0; + const value = user.achievements[data.path] || 0; - let key = data.key || data.path; - let thisContent = achievsContent[key]; + const key = data.key || data.path; + const thisContent = achievsContent[key]; _add(result, { title: i18n.t(thisContent.titleKey, data.language), - text: i18n.t(thisContent.textKey, {count: value}, data.language), + text: i18n.t(thisContent.textKey, { count: value }, data.language), icon: thisContent.icon, key, value, @@ -119,10 +119,10 @@ function _addSimpleWithCount (result, user, data) { } function _addPlural (result, user, data) { - let value = user.achievements[data.path] || 0; + const value = user.achievements[data.path] || 0; - let key = data.key || data.path; - let thisContent = achievsContent[key]; + const key = data.key || data.path; + const thisContent = achievsContent[key]; let titleKey; let textKey; @@ -137,8 +137,8 @@ function _addPlural (result, user, data) { } _add(result, { - title: i18n.t(titleKey, {count: value}, data.language), - text: i18n.t(textKey, {count: value}, data.language), + title: i18n.t(titleKey, { count: value }, data.language), + text: i18n.t(textKey, { count: value }, data.language), icon: thisContent.icon, key, value, @@ -152,14 +152,14 @@ function _addUltimateGear (result, user, data) { data.altPath = data.path; } - let value = user.achievements.ultimateGearSets[data.altPath]; + const value = user.achievements.ultimateGearSets[data.altPath]; - let key = `${data.path}UltimateGear`; - let thisContent = achievsContent[key]; + const key = `${data.path}UltimateGear`; + const thisContent = achievsContent[key]; - let localizedClass = i18n.t(data.path, data.language); - let title = i18n.t(thisContent.titleKey, {ultClass: localizedClass}, data.language); - let text = i18n.t(thisContent.textKey, {ultClass: localizedClass}, data.language); + const localizedClass = i18n.t(data.path, data.language); + const title = i18n.t(thisContent.titleKey, { ultClass: localizedClass }, data.language); + const text = i18n.t(thisContent.textKey, { ultClass: localizedClass }, data.language); _add(result, { title, @@ -172,44 +172,44 @@ function _addUltimateGear (result, user, data) { } function _getBasicAchievements (user, language) { - let result = {}; + const result = {}; - _addPlural(result, user, {path: 'streak', language}); - _addPlural(result, user, {path: 'perfect', language}); + _addPlural(result, user, { path: 'streak', language }); + _addPlural(result, user, { path: 'perfect', language }); - _addSimple(result, user, {path: 'partyUp', language}); - _addSimple(result, user, {path: 'partyOn', language}); - _addSimple(result, user, {path: 'joinedGuild', language}); - _addSimple(result, user, {path: 'royallyLoyal', language}); - _addSimple(result, user, {path: 'joinedChallenge', language}); - _addSimple(result, user, {path: 'invitedFriend', language}); - _addSimple(result, user, {path: 'lostMasterclasser', language}); - _addSimple(result, user, {path: 'mindOverMatter', language}); - _addSimple(result, user, {path: 'justAddWater', language}); - _addSimple(result, user, {path: 'backToBasics', language}); - _addSimple(result, user, {path: 'allYourBase', language}); - _addSimple(result, user, {path: 'dustDevil', language}); - _addSimple(result, user, {path: 'aridAuthority', language}); + _addSimple(result, user, { path: 'partyUp', language }); + _addSimple(result, user, { path: 'partyOn', language }); + _addSimple(result, user, { path: 'joinedGuild', language }); + _addSimple(result, user, { path: 'royallyLoyal', language }); + _addSimple(result, user, { path: 'joinedChallenge', language }); + _addSimple(result, user, { path: 'invitedFriend', language }); + _addSimple(result, user, { path: 'lostMasterclasser', language }); + _addSimple(result, user, { path: 'mindOverMatter', language }); + _addSimple(result, user, { path: 'justAddWater', language }); + _addSimple(result, user, { path: 'backToBasics', language }); + _addSimple(result, user, { path: 'allYourBase', language }); + _addSimple(result, user, { path: 'dustDevil', language }); + _addSimple(result, user, { path: 'aridAuthority', language }); - _addSimpleWithMasterCount(result, user, {path: 'beastMaster', language}); - _addSimpleWithMasterCount(result, user, {path: 'mountMaster', language}); - _addSimpleWithMasterCount(result, user, {path: 'triadBingo', language}); + _addSimpleWithMasterCount(result, user, { path: 'beastMaster', language }); + _addSimpleWithMasterCount(result, user, { path: 'mountMaster', language }); + _addSimpleWithMasterCount(result, user, { path: 'triadBingo', language }); - _addUltimateGear(result, user, {path: 'healer', language}); - _addUltimateGear(result, user, {path: 'rogue', language}); - _addUltimateGear(result, user, {path: 'warrior', language}); - _addUltimateGear(result, user, {path: 'mage', altPath: 'wizard', language}); + _addUltimateGear(result, user, { path: 'healer', language }); + _addUltimateGear(result, user, { path: 'rogue', language }); + _addUltimateGear(result, user, { path: 'warrior', language }); + _addUltimateGear(result, user, { path: 'mage', altPath: 'wizard', language }); - let cardAchievements = ['greeting', 'thankyou', 'birthday', 'congrats', 'getwell', 'goodluck']; + const cardAchievements = ['greeting', 'thankyou', 'birthday', 'congrats', 'getwell', 'goodluck']; cardAchievements.forEach(path => { - _addSimpleWithCount(result, user, {path, key: `${path}Cards`, language}); + _addSimpleWithCount(result, user, { path, key: `${path}Cards`, language }); }); let rebirthTitle; let rebirthText; if (user.achievements.rebirths > 1) { - rebirthTitle = i18n.t('rebirthText', {rebirths: user.achievements.rebirths}, language); + rebirthTitle = i18n.t('rebirthText', { rebirths: user.achievements.rebirths }, language); } else { rebirthTitle = i18n.t('rebirthBegan', language); } @@ -217,7 +217,7 @@ function _getBasicAchievements (user, language) { if (!user.achievements.rebirthLevel) { rebirthText = i18n.t('rebirthOrbNoLevel', language); } else if (user.achievements.rebirthLevel < 100) { - rebirthText = i18n.t('rebirthOrb', {level: user.achievements.rebirthLevel}, language); + rebirthText = i18n.t('rebirthOrb', { level: user.achievements.rebirthLevel }, language); } else { rebirthText = i18n.t('rebirthOrb100', language); } @@ -235,41 +235,41 @@ function _getBasicAchievements (user, language) { } function _getSeasonalAchievements (user, language) { - let result = {}; + const result = {}; - _addPlural(result, user, {path: 'habiticaDays', language}); - _addPlural(result, user, {path: 'habitBirthdays', language}); + _addPlural(result, user, { path: 'habiticaDays', language }); + _addPlural(result, user, { path: 'habitBirthdays', language }); - let spellAchievements = ['snowball', 'spookySparkles', 'shinySeed', 'seafoam']; + const spellAchievements = ['snowball', 'spookySparkles', 'shinySeed', 'seafoam']; spellAchievements.forEach(path => { - _addSimpleWithCount(result, user, {path, language}); + _addSimpleWithCount(result, user, { path, language }); }); - let questAchievements = ['dilatory', 'stressbeast', 'burnout', 'bewilder', 'dysheartener']; + const questAchievements = ['dilatory', 'stressbeast', 'burnout', 'bewilder', 'dysheartener']; questAchievements.forEach(path => { if (user.achievements.quests[path]) { - _addQuest(result, user, {path, language}); + _addQuest(result, user, { path, language }); } }); - _addPlural(result, user, {path: 'costumeContests', language}); + _addPlural(result, user, { path: 'costumeContests', language }); - let cardAchievements = ['nye', 'valentine']; + const cardAchievements = ['nye', 'valentine']; cardAchievements.forEach(path => { - _addSimpleWithCount(result, user, {path, key: `${path}Cards`, language}); + _addSimpleWithCount(result, user, { path, key: `${path}Cards`, language }); }); return result; } function _getSpecialAchievements (user, language) { - let result = {}; + const result = {}; - _addPlural(result, user, {path: 'habitSurveys', language}); + _addPlural(result, user, { path: 'habitSurveys', language }); - let contribKey = 'contributor'; - let contribContent = achievsContent[contribKey]; - let contributorAchiev = { + const contribKey = 'contributor'; + const contribContent = achievsContent[contribKey]; + const contributorAchiev = { key: contribKey, text: i18n.t(contribContent.textKey, language), icon: contribContent.icon, @@ -285,23 +285,23 @@ function _getSpecialAchievements (user, language) { _add(result, contributorAchiev); if (user.backer && user.backer.npc) { - _addSimpleWithCustomPath(result, user, {key: 'npc', path: 'backer.npc', language}); + _addSimpleWithCustomPath(result, user, { key: 'npc', path: 'backer.npc', language }); } if (user.backer && user.backer.tier) { - _addSimpleWithCustomPath(result, user, {key: 'kickstarter', path: 'backer.tier', language}); + _addSimpleWithCustomPath(result, user, { key: 'kickstarter', path: 'backer.tier', language }); } if (user.achievements.veteran) { - _addSimple(result, user, {path: 'veteran', language}); + _addSimple(result, user, { path: 'veteran', language }); } if (user.achievements.originalUser) { - _addSimple(result, user, {path: 'originalUser', language}); + _addSimple(result, user, { path: 'originalUser', language }); } if (user.achievements.kickstarter2019) { - _addSimple(result, user, {path: 'kickstarter2019', language}); + _addSimple(result, user, { path: 'kickstarter2019', language }); } return result; @@ -309,7 +309,7 @@ function _getSpecialAchievements (user, language) { // Build and return the given user's achievement data. achievs.getAchievementsForProfile = function getAchievementsForProfile (user, language) { - let result = { + const result = { basic: { label: 'Basic', achievements: _getBasicAchievements(user, language), diff --git a/website/common/script/libs/appliedTags.js b/website/common/script/libs/appliedTags.js index 14a0493a7e..cfbb8da2cc 100644 --- a/website/common/script/libs/appliedTags.js +++ b/website/common/script/libs/appliedTags.js @@ -5,10 +5,6 @@ Are there tags applied? // TODO move to client export default function appliedTags (userTags, taskTags = []) { - let arr = userTags.filter(tag => { - return taskTags.indexOf(tag.id) !== -1; - }).map(tag => { - return tag.name; - }); + const arr = userTags.filter(tag => taskTags.indexOf(tag.id) !== -1).map(tag => tag.name); return arr.join(', '); } diff --git a/website/common/script/libs/errorMessage.js b/website/common/script/libs/errorMessage.js index d083e6cdfa..bbd8aae20c 100644 --- a/website/common/script/libs/errorMessage.js +++ b/website/common/script/libs/errorMessage.js @@ -6,10 +6,10 @@ import _template from 'lodash/template'; import messages from '../../errors/commonErrorMessages'; export default function (msgKey, vars = {}) { - let message = messages[msgKey]; + const message = messages[msgKey]; if (!message) throw new Error(`Error processing the common message "${msgKey}".`); - let clonedVars = vars ? _clone(vars) : {}; + const clonedVars = vars ? _clone(vars) : {}; // TODO cache the result of template() ? More memory usage, faster output return _template(message)(clonedVars); diff --git a/website/common/script/libs/getItemByPathAndType.js b/website/common/script/libs/getItemByPathAndType.js index e921a2193e..039593e5b0 100644 --- a/website/common/script/libs/getItemByPathAndType.js +++ b/website/common/script/libs/getItemByPathAndType.js @@ -1,11 +1,11 @@ -import content from '../content/index'; import get from 'lodash/get'; +import content from '../content/index'; export default function getItemByPathAndType (type, path) { let item = get(content, path); if (type === 'timeTravelersStable') { - let [, animalType, key] = path.split('.'); + const [, animalType, key] = path.split('.'); item = { key, diff --git a/website/common/script/libs/getItemInfo.js b/website/common/script/libs/getItemInfo.js index f1b787c2a5..ced86dc0e9 100644 --- a/website/common/script/libs/getItemInfo.js +++ b/website/common/script/libs/getItemInfo.js @@ -1,3 +1,4 @@ +import _mapValues from 'lodash/mapValues'; import i18n from '../i18n'; import content from '../content/index'; import { BadRequest } from './errors'; @@ -7,7 +8,6 @@ import isPinned from './isPinned'; import isFreeRebirth from './isFreeRebirth'; import getOfficialPinnedItems from './getOfficialPinnedItems'; -import _mapValues from 'lodash/mapValues'; function lockQuest (quest, user) { if (quest.key === 'lostMasterclasser1') return !(user.achievements.quests.dilatoryDistress3 && user.achievements.quests.mayhemMistiflying3 && user.achievements.quests.stoikalmCalamity3 && user.achievements.quests.taskwoodsTerror3); @@ -20,9 +20,7 @@ function lockQuest (quest, user) { } function isItemSuggested (officialPinnedItems, itemInfo) { - return officialPinnedItems.findIndex(officialItem => { - return officialItem.type === itemInfo.pinType && officialItem.path === itemInfo.path; - }) > -1; + return officialPinnedItems.findIndex(officialItem => officialItem.type === itemInfo.pinType && officialItem.path === itemInfo.path) > -1; } function getDefaultGearProps (item, language) { @@ -57,7 +55,7 @@ export default function getItemInfo (user, type, item, officialPinnedItems, lang case 'eggs': itemInfo = { key: item.key, - text: i18n.t('egg', {eggType: item.text(language)}, language), + text: i18n.t('egg', { eggType: item.text(language) }, language), notes: item.notes(language), value: item.value, class: `Pet_Egg_${item.key}`, @@ -71,7 +69,7 @@ export default function getItemInfo (user, type, item, officialPinnedItems, lang case 'hatchingPotions': itemInfo = { key: item.key, - text: i18n.t('potion', {potionType: item.text(language)}), + text: i18n.t('potion', { potionType: item.text(language) }), notes: item.notes(language), class: `Pet_HatchingPotion_${item.key}`, value: item.value, @@ -85,7 +83,7 @@ export default function getItemInfo (user, type, item, officialPinnedItems, lang case 'premiumHatchingPotion': itemInfo = { key: item.key, - text: i18n.t('potion', {potionType: item.text(language)}), + text: i18n.t('potion', { potionType: item.text(language) }), notes: `${item.notes(language)} ${item._addlNotes(language)}`, class: `Pet_HatchingPotion_${item.key}`, value: item.value, @@ -137,12 +135,10 @@ export default function getItemInfo (user, type, item, officialPinnedItems, lang unlockCondition: item.unlockCondition, drop: item.drop, boss: item.boss, - collect: item.collect ? _mapValues(item.collect, (o) => { - return { - count: o.count, - text: o.text(), - }; - }) : undefined, + collect: item.collect ? _mapValues(item.collect, o => ({ + count: o.count, + text: o.text(), + })) : undefined, lvl: item.lvl, class: locked ? `inventory_quest_scroll_${item.key}_locked` : `inventory_quest_scroll_${item.key}`, purchaseType: 'quests', @@ -264,7 +260,7 @@ export default function getItemInfo (user, type, item, officialPinnedItems, lang }; break; case 'card': { - let spellInfo = content.spells.special[item.key]; + const spellInfo = content.spells.special[item.key]; itemInfo = { key: item.key, @@ -347,7 +343,7 @@ export default function getItemInfo (user, type, item, officialPinnedItems, lang itemInfo.isSuggested = isItemSuggested(officialPinnedItems, itemInfo); itemInfo.pinned = isPinned(user, itemInfo, officialPinnedItems); } else { - throw new BadRequest(i18n.t('wrongItemType', {type}, language)); + throw new BadRequest(i18n.t('wrongItemType', { type }, language)); } return itemInfo; diff --git a/website/common/script/libs/getOfficialPinnedItems.js b/website/common/script/libs/getOfficialPinnedItems.js index 53e2501f3b..82ca9ba866 100644 --- a/website/common/script/libs/getOfficialPinnedItems.js +++ b/website/common/script/libs/getOfficialPinnedItems.js @@ -1,22 +1,20 @@ -import content from '../content/index'; -import SeasonalShopConfig from '../libs/shops-seasonal.config'; import toArray from 'lodash/toArray'; +import content from '../content/index'; +import SeasonalShopConfig from './shops-seasonal.config'; -const officialPinnedItems = content.officialPinnedItems; +const { officialPinnedItems } = content; -let flatGearArray = toArray(content.gear.flat); +const flatGearArray = toArray(content.gear.flat); export default function getOfficialPinnedItems (user) { - let officialItemsArray = [...officialPinnedItems]; + const officialItemsArray = [...officialPinnedItems]; if (SeasonalShopConfig.pinnedSets && Boolean(user) && user.stats.class) { - let setToAdd = SeasonalShopConfig.pinnedSets[user.stats.class]; + const setToAdd = SeasonalShopConfig.pinnedSets[user.stats.class]; // pinnedSets == current seasonal class set are always gold purchaseable - flatGearArray.filter((gear) => { - return user.items.gear.owned[gear.key] === undefined && gear.set === setToAdd; - }).map((gear) => { + flatGearArray.filter(gear => user.items.gear.owned[gear.key] === undefined && gear.set === setToAdd).map(gear => { officialItemsArray.push({ type: 'marketGear', path: `gear.flat.${gear.key}`, diff --git a/website/common/script/libs/gold.js b/website/common/script/libs/gold.js index f55e125d91..0553035d25 100644 --- a/website/common/script/libs/gold.js +++ b/website/common/script/libs/gold.js @@ -3,7 +3,6 @@ export default function gold (num) { if (num) { return Math.floor(num); - } else { - return '0'; } + return '0'; } diff --git a/website/common/script/libs/hasClass.js b/website/common/script/libs/hasClass.js index 6db1607acb..9f0b84e2d3 100644 --- a/website/common/script/libs/hasClass.js +++ b/website/common/script/libs/hasClass.js @@ -1,8 +1,8 @@ // Check if user has Class system enabled export default function hasClass (member) { return ( - member.stats.lvl >= 10 && - !member.preferences.disableClasses && - member.flags.classSelected + member.stats.lvl >= 10 + && !member.preferences.disableClasses + && member.flags.classSelected ); } diff --git a/website/common/script/libs/inAppRewards.js b/website/common/script/libs/inAppRewards.js index 907c49e4c2..034fbb43e9 100644 --- a/website/common/script/libs/inAppRewards.js +++ b/website/common/script/libs/inAppRewards.js @@ -1,10 +1,10 @@ +import compactArray from 'lodash/compact'; import getItemInfo from './getItemInfo'; import shops from './shops'; import getOfficialPinnedItems from './getOfficialPinnedItems'; -import compactArray from 'lodash/compact'; import getItemByPathAndType from './getItemByPathAndType'; -import {checkPinnedAreasForNullEntries} from '../ops/pinnedGearUtils'; +import { checkPinnedAreasForNullEntries } from '../ops/pinnedGearUtils'; /** * Orders the pinned items so we always get our inAppRewards in the order @@ -15,12 +15,12 @@ import {checkPinnedAreasForNullEntries} from '../ops/pinnedGearUtils'; * @return items of ordered inAppRewards */ function sortInAppRewards (user, items) { - let pinnedItemsOrder = user.pinnedItemsOrder; + const { pinnedItemsOrder } = user; let orderedItems = []; - let unorderedItems = []; // what we want to add later + const unorderedItems = []; // what we want to add later items.forEach((item, index) => { - let i = pinnedItemsOrder[index] === item.path ? index : pinnedItemsOrder.indexOf(item.path); + const i = pinnedItemsOrder[index] === item.path ? index : pinnedItemsOrder.indexOf(item.path); if (i === -1) { unorderedItems.push(item); } else { @@ -35,7 +35,7 @@ function sortInAppRewards (user, items) { export default function getPinnedItems (user) { checkPinnedAreasForNullEntries(user); - let officialPinnedItems = getOfficialPinnedItems(user); + const officialPinnedItems = getOfficialPinnedItems(user); const officialPinnedItemsNotUnpinned = officialPinnedItems.filter(officialPin => { const isUnpinned = user.unpinnedItems.findIndex(unpinned => unpinned.path === officialPin.path) > -1; @@ -44,15 +44,15 @@ export default function getPinnedItems (user) { const pinnedItems = officialPinnedItemsNotUnpinned.concat(user.pinnedItems); - let items = pinnedItems - .map(({type, path}) => { - let item = getItemByPathAndType(type, path); + const items = pinnedItems + .map(({ type, path }) => { + const item = getItemByPathAndType(type, path); return getItemInfo(user, type, item, officialPinnedItems); }); shops.checkMarketGearLocked(user, items); - let orderedItems = sortInAppRewards(user, items); + const orderedItems = sortInAppRewards(user, items); return orderedItems; } diff --git a/website/common/script/libs/isPinned.js b/website/common/script/libs/isPinned.js index 4025c008cf..01dc35bbff 100644 --- a/website/common/script/libs/isPinned.js +++ b/website/common/script/libs/isPinned.js @@ -1,13 +1,11 @@ export default function isPinned (user, item, checkOfficialPinnedItems /* getOfficialPinnedItems */) { - if (user === null) - return false; + if (user === null) return false; const isPinnedOfficial = checkOfficialPinnedItems !== undefined && checkOfficialPinnedItems.findIndex(pinned => pinned.path === item.path) > -1; const isItemUnpinned = user.unpinnedItems !== undefined && user.unpinnedItems.findIndex(unpinned => unpinned.path === item.path) > -1; const isItemPinned = user.pinnedItems !== undefined && user.pinnedItems.findIndex(pinned => pinned.path === item.path) > -1; - if (isPinnedOfficial && !isItemUnpinned) - return true; + if (isPinnedOfficial && !isItemUnpinned) return true; return isItemPinned; } diff --git a/website/common/script/libs/noTags.js b/website/common/script/libs/noTags.js index 15c11d5589..aff2d45b8f 100644 --- a/website/common/script/libs/noTags.js +++ b/website/common/script/libs/noTags.js @@ -8,7 +8,5 @@ are any tags active? // TODO move to client export default function noTags (tags) { - return isEmpty(tags) || isEmpty(filter(tags, (t) => { - return t; - })); + return isEmpty(tags) || isEmpty(filter(tags, t => t)); } diff --git a/website/common/script/libs/pickDeep.js b/website/common/script/libs/pickDeep.js index 48330c3000..24eaf07275 100644 --- a/website/common/script/libs/pickDeep.js +++ b/website/common/script/libs/pickDeep.js @@ -8,8 +8,8 @@ import get from 'lodash/get'; export default function pickDeep (obj, properties) { if (!Array.isArray(properties)) throw new Error('"properties" must be an array'); - let result = {}; - each(properties, (prop) => set(result, prop, get(obj, prop))); + const result = {}; + each(properties, prop => set(result, prop, get(obj, prop))); return result; } diff --git a/website/common/script/libs/preenTodos.js b/website/common/script/libs/preenTodos.js index 1853b2fed9..9d3c44ab96 100644 --- a/website/common/script/libs/preenTodos.js +++ b/website/common/script/libs/preenTodos.js @@ -4,9 +4,7 @@ import filter from 'lodash/filter'; // TODO used only in v2 export default function preenTodos (tasks) { - return filter(tasks, (t) => { - return !t.completed || t.challenge && t.challenge.id || moment(t.dateCompleted).isAfter(moment().subtract({ - days: 3, - })); - }); + return filter(tasks, t => !t.completed || t.challenge && t.challenge.id || moment(t.dateCompleted).isAfter(moment().subtract({ + days: 3, + }))); } diff --git a/website/common/script/libs/randomVal.js b/website/common/script/libs/randomVal.js index dfbcc43a2c..acd13ebb59 100644 --- a/website/common/script/libs/randomVal.js +++ b/website/common/script/libs/randomVal.js @@ -8,12 +8,12 @@ export function trueRandom () { // Get a random property from an object // returns random property (the value) export default function randomVal (obj, options = {}) { - let array = options.key ? keys(obj) : values(obj); - let random = options.predictableRandom || trueRandom(); + const array = options.key ? keys(obj) : values(obj); + const random = options.predictableRandom || trueRandom(); array.sort(); - let randomIndex = Math.floor(random * array.length); + const randomIndex = Math.floor(random * array.length); return array[randomIndex]; -} \ No newline at end of file +} diff --git a/website/common/script/libs/shops.js b/website/common/script/libs/shops.js index efd1eb3713..33e32805d0 100644 --- a/website/common/script/libs/shops.js +++ b/website/common/script/libs/shops.js @@ -17,7 +17,7 @@ import featuredItems from '../content/shop-featuredItems'; import getOfficialPinnedItems from './getOfficialPinnedItems'; -let shops = {}; +const shops = {}; /* Market */ @@ -30,18 +30,16 @@ shops.getMarketShop = function getMarketShop (user, language) { categories: shops.getMarketCategories(user, language), featured: { text: i18n.t('featuredItems'), - items: featuredItems.market.map(i => { - return getItemInfo(user, i.type, get(content, i.path)); - }), + items: featuredItems.market.map(i => getItemInfo(user, i.type, get(content, i.path))), }, }; }; shops.getMarketCategories = function getMarket (user, language) { - let officialPinnedItems = getOfficialPinnedItems(user); + const officialPinnedItems = getOfficialPinnedItems(user); - let categories = []; - let eggsCategory = { + const categories = []; + const eggsCategory = { identifier: 'eggs', text: i18n.t('eggs', language), notes: i18n.t('dropsExplanationEggs', language), @@ -50,47 +48,39 @@ shops.getMarketCategories = function getMarket (user, language) { eggsCategory.items = sortBy(values(content.questEggs) .filter(egg => egg.canBuy(user)) .concat(values(content.dropEggs)) - .map(egg => { - return getItemInfo(user, 'eggs', egg, officialPinnedItems, language); - }), 'key'); + .map(egg => getItemInfo(user, 'eggs', egg, officialPinnedItems, language)), 'key'); categories.push(eggsCategory); - let hatchingPotionsCategory = { + const hatchingPotionsCategory = { identifier: 'hatchingPotions', text: i18n.t('hatchingPotions', language), notes: i18n.t('dropsExplanation', language), }; hatchingPotionsCategory.items = sortBy(values(content.hatchingPotions) .filter(hp => !hp.limited) - .map(hatchingPotion => { - return getItemInfo(user, 'hatchingPotions', hatchingPotion, officialPinnedItems, language); - }), 'key'); + .map(hatchingPotion => getItemInfo(user, 'hatchingPotions', hatchingPotion, officialPinnedItems, language)), 'key'); categories.push(hatchingPotionsCategory); - let premiumHatchingPotionsCategory = { + const premiumHatchingPotionsCategory = { identifier: 'premiumHatchingPotions', text: i18n.t('magicHatchingPotions', language), notes: i18n.t('premiumPotionNoDropExplanation', language), }; premiumHatchingPotionsCategory.items = sortBy(values(content.hatchingPotions) .filter(hp => hp.limited && hp.canBuy(user)) - .map(premiumHatchingPotion => { - return getItemInfo(user, 'premiumHatchingPotion', premiumHatchingPotion, officialPinnedItems, language); - }), 'key'); + .map(premiumHatchingPotion => getItemInfo(user, 'premiumHatchingPotion', premiumHatchingPotion, officialPinnedItems, language)), 'key'); if (premiumHatchingPotionsCategory.items.length > 0) { categories.push(premiumHatchingPotionsCategory); } - let foodCategory = { + const foodCategory = { identifier: 'food', text: i18n.t('food', language), notes: i18n.t('dropsExplanation', language), }; foodCategory.items = sortBy(values(content.food) .filter(food => food.canDrop || food.key === 'Saddle') - .map(foodItem => { - return getItemInfo(user, 'food', foodItem, officialPinnedItems, language); - }), 'key'); + .map(foodItem => getItemInfo(user, 'food', foodItem, officialPinnedItems, language)), 'key'); categories.push(foodCategory); return categories; @@ -99,27 +89,26 @@ shops.getMarketCategories = function getMarket (user, language) { function getClassName (classType, language) { if (classType === 'wizard') { return i18n.t('mage', language); - } else { - return i18n.t(classType, language); } + return i18n.t(classType, language); } // TODO Refactor the `.locked` logic shops.checkMarketGearLocked = function checkMarketGearLocked (user, items) { - let result = filter(items, ['pinType', 'marketGear']); + const result = filter(items, ['pinType', 'marketGear']); const officialPinnedItems = getOfficialPinnedItems(user); - let availableGear = map(updateStore(user), (item) => getItemInfo(user, 'marketGear', item, officialPinnedItems).path); - for (let gear of result) { + const availableGear = map(updateStore(user), item => getItemInfo(user, 'marketGear', item, officialPinnedItems).path); + for (const gear of result) { if (gear.klass !== user.stats.class) { gear.locked = true; } - if (!gear.locked && !availableGear.includes(gear.path)) { + if (!gear.locked && !availableGear.includes(gear.path)) { gear.locked = true; } if (Boolean(gear.specialClass) && Boolean(gear.set)) { - let currentSet = gear.set === seasonalShopConfig.pinnedSets[gear.specialClass]; + const currentSet = gear.set === seasonalShopConfig.pinnedSets[gear.specialClass]; gear.locked = currentSet && user.stats.class !== gear.specialClass; } @@ -128,7 +117,7 @@ shops.checkMarketGearLocked = function checkMarketGearLocked (user, items) { gear.locked = !gear.canOwn(user); } - let itemOwned = user.items.gear.owned[gear.key]; + const itemOwned = user.items.gear.owned[gear.key]; if (itemOwned === false && !availableGear.includes(gear.path)) { gear.locked = true; @@ -145,18 +134,18 @@ shops.checkMarketGearLocked = function checkMarketGearLocked (user, items) { }; shops.getMarketGearCategories = function getMarketGear (user, language) { - let categories = []; - let officialPinnedItems = getOfficialPinnedItems(user); + const categories = []; + const officialPinnedItems = getOfficialPinnedItems(user); - for (let classType of content.classes) { - let category = { + for (const classType of content.classes) { + const category = { identifier: classType, text: getClassName(classType, language), }; - let result = filter(content.gear.flat, function findClassGear (gearItem) { + const result = filter(content.gear.flat, gearItem => { if (gearItem.klass === classType) return true; - let classShift = { + const classShift = { items: user.items, stats: { class: classType, @@ -165,17 +154,13 @@ shops.getMarketGearCategories = function getMarketGear (user, language) { if (gearItem.specialClass === classType && user.items.gear.owned[gearItem.key] !== false) return gearItem.canOwn(classShift); }); - category.items = map(result, (e) => { - return getItemInfo(user, 'marketGear', e, officialPinnedItems); - }); + category.items = map(result, e => getItemInfo(user, 'marketGear', e, officialPinnedItems)); - let specialGear = filter(content.gear.flat, (gear) => { - return user.items.gear.owned[gear.key] === false && - gear.specialClass === classType && - gear.klass === 'special'; - }); + const specialGear = filter(content.gear.flat, gear => user.items.gear.owned[gear.key] === false + && gear.specialClass === classType + && gear.klass === 'special'); - each(specialGear, (gear) => { + each(specialGear, gear => { category.items.push(getItemInfo(user, 'marketGear', gear)); }); @@ -183,21 +168,17 @@ shops.getMarketGearCategories = function getMarketGear (user, language) { categories.push(category); } - let nonClassCategory = { + const nonClassCategory = { identifier: 'none', text: i18n.t('none', language), }; - let specialNonClassGear = filter(content.gear.flat, (gear) => { - return !user.items.gear.owned[gear.key] && - content.classes.indexOf(gear.klass) === -1 && - content.classes.indexOf(gear.specialClass) === -1 && - (gear.canOwn && gear.canOwn(user)); - }); + const specialNonClassGear = filter(content.gear.flat, gear => !user.items.gear.owned[gear.key] + && content.classes.indexOf(gear.klass) === -1 + && content.classes.indexOf(gear.specialClass) === -1 + && (gear.canOwn && gear.canOwn(user))); - nonClassCategory.items = map(specialNonClassGear, (e) => { - return getItemInfo(user, 'marketGear', e); - }); + nonClassCategory.items = map(specialNonClassGear, e => getItemInfo(user, 'marketGear', e)); shops.checkMarketGearLocked(user, nonClassCategory.items); categories.push(nonClassCategory); @@ -215,16 +196,14 @@ shops.getQuestShop = function getQuestShop (user, language) { categories: shops.getQuestShopCategories(user, language), featured: { text: i18n.t('featuredQuests'), - items: featuredItems.quests.map(i => { - return getItemInfo(user, i.type, get(content, i.path)); - }), + items: featuredItems.quests.map(i => getItemInfo(user, i.type, get(content, i.path))), }, }; }; shops.getQuestShopCategories = function getQuestShopCategories (user, language) { - let categories = []; - let officialPinnedItems = getOfficialPinnedItems(user); + const categories = []; + const officialPinnedItems = getOfficialPinnedItems(user); /* * --------------------------------------------------------------- @@ -280,32 +259,28 @@ shops.getQuestShopCategories = function getQuestShopCategories (user, language) * */ - let bundleCategory = { + const bundleCategory = { identifier: 'bundle', text: i18n.t('questBundles', language), }; bundleCategory.items = sortBy(values(content.bundles) .filter(bundle => bundle.type === 'quests' && bundle.canBuy()) - .map(bundle => { - return getItemInfo(user, 'bundles', bundle, officialPinnedItems, language); - })); + .map(bundle => getItemInfo(user, 'bundles', bundle, officialPinnedItems, language))); if (bundleCategory.items.length > 0) { categories.push(bundleCategory); } each(content.userCanOwnQuestCategories, type => { - let category = { + const category = { identifier: type, text: i18n.t(`${type}Quests`, language), }; category.items = content.questsByLevel .filter(quest => quest.canBuy(user) && quest.category === type) - .map(quest => { - return getItemInfo(user, 'quests', quest, officialPinnedItems, language); - }); + .map(quest => getItemInfo(user, 'quests', quest, officialPinnedItems, language)); categories.push(category); }); @@ -316,7 +291,7 @@ shops.getQuestShopCategories = function getQuestShopCategories (user, language) /* Time Travelers */ shops.getTimeTravelersShop = function getTimeTravelersShop (user, language) { - let hasTrinkets = user.purchased.plan.consecutive.trinkets > 0; + const hasTrinkets = user.purchased.plan.consecutive.trinkets > 0; return { identifier: 'timeTravelersShop', @@ -329,36 +304,36 @@ shops.getTimeTravelersShop = function getTimeTravelersShop (user, language) { }; shops.getTimeTravelersCategories = function getTimeTravelersCategories (user, language) { - let categories = []; - let stable = {pets: 'Pet-', mounts: 'Mount_Icon_'}; + const categories = []; + const stable = { pets: 'Pet-', mounts: 'Mount_Icon_' }; - let officialPinnedItems = getOfficialPinnedItems(user); + const officialPinnedItems = getOfficialPinnedItems(user); - let questCategory = { + const questCategory = { identifier: 'quests', text: i18n.t('quests', language), items: [], }; - for (let key in content.quests) { + for (const key in content.quests) { if (content.quests[key].category === 'timeTravelers') { - let item = getItemInfo(user, 'quests', content.quests[key], officialPinnedItems, language); + const item = getItemInfo(user, 'quests', content.quests[key], officialPinnedItems, language); questCategory.items.push(item); } } categories.push(questCategory); - for (let type in stable) { + for (const type in stable) { if (stable.hasOwnProperty(type)) { - let category = { + const category = { identifier: type, text: i18n.t(type, language), items: [], }; - for (let key in content.timeTravelStable[type]) { + for (const key in content.timeTravelStable[type]) { if (content.timeTravelStable[type].hasOwnProperty(key)) { if (!user.items[type][key]) { - let item = getItemInfo(user, 'timeTravelersStable', { + const item = getItemInfo(user, 'timeTravelersStable', { key, type, }, officialPinnedItems, language); @@ -372,11 +347,11 @@ shops.getTimeTravelersCategories = function getTimeTravelersCategories (user, la } } - let sets = content.timeTravelerStore(user); - for (let setKey in sets) { + const sets = content.timeTravelerStore(user); + for (const setKey in sets) { if (sets.hasOwnProperty(setKey)) { - let set = sets[setKey]; - let category = { + const set = sets[setKey]; + const category = { identifier: set.key, text: set.text(language), path: `mystery.${set.key}`, @@ -384,20 +359,18 @@ shops.getTimeTravelersCategories = function getTimeTravelersCategories (user, la purchaseAll: true, }; - category.items = map(set.items, item => { - return { - key: item.key, - text: item.text(language), - notes: item.notes(language), - type: item.type, - purchaseType: 'gear', - value: 1, - locked: false, - currency: 'hourglasses', - class: `shop_${item.key}`, - pinKey: `timeTravelers!gear.flat.${item.key}`, - }; - }); + category.items = map(set.items, item => ({ + key: item.key, + text: item.text(language), + notes: item.notes(language), + type: item.type, + purchaseType: 'gear', + value: 1, + locked: false, + currency: 'hourglasses', + class: `shop_${item.key}`, + pinKey: `timeTravelers!gear.flat.${item.key}`, + })); if (category.items.length > 0) { categories.push(category); } @@ -410,19 +383,18 @@ shops.getTimeTravelersCategories = function getTimeTravelersCategories (user, la /* Seasonal */ -let flatGearArray = toArray(content.gear.flat); +const flatGearArray = toArray(content.gear.flat); shops.getSeasonalGearBySet = function getSeasonalGearBySet (user, set, officialPinnedItems, language, ignoreAlreadyOwned = false) { - return flatGearArray.filter((gear) => { - if (!ignoreAlreadyOwned && user.items.gear.owned[gear.key] !== undefined) - return false; + return flatGearArray.filter(gear => { + if (!ignoreAlreadyOwned && user.items.gear.owned[gear.key] !== undefined) return false; return gear.set === set; }).map(gear => { - let currentSet = gear.set === seasonalShopConfig.pinnedSets[gear.specialClass]; + const currentSet = gear.set === seasonalShopConfig.pinnedSets[gear.specialClass]; // only the current season set can be purchased by gold - let itemInfo = getItemInfo(null, currentSet ? 'marketGear' : 'gear', gear, officialPinnedItems, language); + const itemInfo = getItemInfo(null, currentSet ? 'marketGear' : 'gear', gear, officialPinnedItems, language); itemInfo.locked = currentSet && user.stats.class !== gear.specialClass; return itemInfo; @@ -430,9 +402,9 @@ shops.getSeasonalGearBySet = function getSeasonalGearBySet (user, set, officialP }; shops.getSeasonalShop = function getSeasonalShop (user, language) { - let officialPinnedItems = getOfficialPinnedItems(user); + const officialPinnedItems = getOfficialPinnedItems(user); - let resObject = { + const resObject = { identifier: 'seasonalShop', text: i18n.t('seasonalShop'), notes: i18n.t(`seasonalShop${seasonalShopConfig.currentSeason}Text`), @@ -453,7 +425,7 @@ shops.getSeasonalShop = function getSeasonalShop (user, language) { // setKey: i18n.t('setTranslationString', language), // }; shops.getSeasonalShopCategories = function getSeasonalShopCategories (user, language) { - let officialPinnedItems = getOfficialPinnedItems(user); + const officialPinnedItems = getOfficialPinnedItems(user); const AVAILABLE_SPELLS = [ ...seasonalShopConfig.availableSpells, @@ -463,44 +435,36 @@ shops.getSeasonalShopCategories = function getSeasonalShopCategories (user, lang ...seasonalShopConfig.availableQuests, ]; - let categories = []; + const categories = []; - let spells = pickBy(content.spells.special, (spell, key) => { - return AVAILABLE_SPELLS.indexOf(key) !== -1; - }); + const spells = pickBy(content.spells.special, (spell, key) => AVAILABLE_SPELLS.indexOf(key) !== -1); if (keys(spells).length > 0) { - let category = { + const category = { identifier: 'spells', text: i18n.t('seasonalItems', language), }; - category.items = map(spells, (spell) => { - return getItemInfo(user, 'seasonalSpell', spell, officialPinnedItems, language); - }); + category.items = map(spells, spell => getItemInfo(user, 'seasonalSpell', spell, officialPinnedItems, language)); categories.push(category); } - let quests = pickBy(content.quests, (quest, key) => { - return AVAILABLE_QUESTS.indexOf(key) !== -1; - }); + const quests = pickBy(content.quests, (quest, key) => AVAILABLE_QUESTS.indexOf(key) !== -1); if (keys(quests).length > 0) { - let category = { + const category = { identifier: 'quests', text: i18n.t('quests', language), }; - category.items = map(quests, (quest) => { - return getItemInfo(user, 'seasonalQuest', quest, officialPinnedItems, language); - }); + category.items = map(quests, quest => getItemInfo(user, 'seasonalQuest', quest, officialPinnedItems, language)); categories.push(category); } - for (let set of seasonalShopConfig.availableSets) { - let category = { + for (const set of seasonalShopConfig.availableSets) { + const category = { identifier: set, text: i18n.t(set), }; @@ -508,7 +472,7 @@ shops.getSeasonalShopCategories = function getSeasonalShopCategories (user, lang category.items = shops.getSeasonalGearBySet(user, set, officialPinnedItems, language, false); if (category.items.length > 0) { - let item = category.items[0]; + const item = category.items[0]; category.specialClass = item.specialClass; category.event = item.event; @@ -520,18 +484,16 @@ shops.getSeasonalShopCategories = function getSeasonalShopCategories (user, lang }; shops.getBackgroundShopSets = function getBackgroundShopSets (language) { - let sets = []; - let officialPinnedItems = getOfficialPinnedItems(); + const sets = []; + const officialPinnedItems = getOfficialPinnedItems(); eachRight(content.backgrounds, (group, key) => { - let set = { + const set = { identifier: key, text: i18n.t(key, language), }; - set.items = map(group, (background) => { - return getItemInfo(null, 'background', background, officialPinnedItems, language); - }); + set.items = map(group, background => getItemInfo(null, 'background', background, officialPinnedItems, language)); sets.push(set); }); diff --git a/website/common/script/libs/silver.js b/website/common/script/libs/silver.js index 286a81343e..96ec97531a 100644 --- a/website/common/script/libs/silver.js +++ b/website/common/script/libs/silver.js @@ -6,9 +6,8 @@ Silver amount from their money export default function silver (num) { if (num) { - let centCount = Math.floor((num - Math.floor(num)) * 100); + const centCount = Math.floor((num - Math.floor(num)) * 100); return `0${centCount}`.slice(-2); - } else { - return '00'; } + return '00'; } diff --git a/website/common/script/libs/statsComputed.js b/website/common/script/libs/statsComputed.js index 07c2b74242..34b2066d24 100644 --- a/website/common/script/libs/statsComputed.js +++ b/website/common/script/libs/statsComputed.js @@ -5,23 +5,23 @@ import content from '../content/index'; import * as statHelpers from '../statHelpers'; function equipmentStatBonusComputed (stat, user) { - let gear = content.gear.flat; + const gear = content.gear.flat; let gearBonus = 0; let classBonus = 0; // toObject is required here due to lodash values not working well with mongoose doc objects. // if toObject doesn't exist, we can assume the object is already plain JSON // see http://stackoverflow.com/questions/25767334/underscore-js-keys-and-omit-not-working-as-expected - let equipped = user.items.gear.equipped; - let equippedKeys = values(!equipped.toObject ? equipped : equipped.toObject()); + const { equipped } = user.items.gear; + const equippedKeys = values(!equipped.toObject ? equipped : equipped.toObject()); - each(equippedKeys, (equippedItem) => { - let item = gear[equippedItem]; + each(equippedKeys, equippedItem => { + const item = gear[equippedItem]; if (item) { - let equipmentStat = item[stat]; - let classBonusMultiplier = item.klass === user.stats.class || - item.specialClass === user.stats.class ? 0.5 : 0; + const equipmentStat = item[stat]; + const classBonusMultiplier = item.klass === user.stats.class + || item.specialClass === user.stats.class ? 0.5 : 0; gearBonus += equipmentStat; classBonus += equipmentStat * classBonusMultiplier; } @@ -34,17 +34,17 @@ function equipmentStatBonusComputed (stat, user) { } export default function statsComputed (user) { - let statBreakdown = { + const statBreakdown = { gearBonus: {}, classBonus: {}, baseStat: {}, buff: {}, levelBonus: {}, }; - each(['per', 'con', 'str', 'int'], (stat) => { - let baseStat = get(user, 'stats')[stat]; - let buff = get(user, 'stats.buffs')[stat]; - let equipmentBonus = equipmentStatBonusComputed(stat, user); + each(['per', 'con', 'str', 'int'], stat => { + const baseStat = get(user, 'stats')[stat]; + const buff = get(user, 'stats.buffs')[stat]; + const equipmentBonus = equipmentStatBonusComputed(stat, user); statBreakdown[stat] = equipmentBonus.gearBonus + equipmentBonus.classBonus + baseStat + buff; statBreakdown[stat] += Math.floor(statHelpers.capByLevel(user.stats.lvl) / 2); diff --git a/website/common/script/libs/taskDefaults.js b/website/common/script/libs/taskDefaults.js index 2714041cb2..c8691077a8 100644 --- a/website/common/script/libs/taskDefaults.js +++ b/website/common/script/libs/taskDefaults.js @@ -14,8 +14,8 @@ export default function taskDefaults (task, user) { task.type = 'habit'; } - let defaultId = uuid(); - let defaultTaskObj = { + const defaultId = uuid(); + const defaultTaskObj = { _id: defaultId, text: task._id || defaultId, notes: '', @@ -65,9 +65,9 @@ export default function taskDefaults (task, user) { } if (task.type === 'daily') { - let now = moment().zone(user.preferences.timezoneOffset); - let startOfDay = now.clone().startOf('day'); - let startOfDayWithCDSTime = startOfDay + const now = moment().zone(user.preferences.timezoneOffset); + const startOfDay = now.clone().startOf('day'); + const startOfDayWithCDSTime = startOfDay .clone() .add({ hours: user.preferences.dayStart, @@ -85,9 +85,9 @@ export default function taskDefaults (task, user) { su: true, }, // If cron will happen today, start the daily yesterday - startDate: startOfDayWithCDSTime.isAfter(now) ? - startOfDay.clone().subtract(1, 'day').toDate() : - startOfDay.toDate(), + startDate: startOfDayWithCDSTime.isAfter(now) + ? startOfDay.clone().subtract(1, 'day').toDate() + : startOfDay.toDate(), everyX: 1, frequency: 'weekly', daysOfMonth: [], diff --git a/website/common/script/libs/updateStore.js b/website/common/script/libs/updateStore.js index a5b68f506d..b475689739 100644 --- a/website/common/script/libs/updateStore.js +++ b/website/common/script/libs/updateStore.js @@ -8,7 +8,7 @@ import content from '../content/index'; // Return the list of gear items available for purchase // TODO: Remove updateStore once the new client is live -let sortOrder = reduce(content.gearTypes, (accumulator, val, key) => { +const sortOrder = reduce(content.gearTypes, (accumulator, val, key) => { accumulator[val] = key; return accumulator; }, {}); @@ -16,21 +16,18 @@ let sortOrder = reduce(content.gearTypes, (accumulator, val, key) => { export default function updateStore (user) { let changes = []; - each(content.gearTypes, (type) => { - let found = lodashFind(content.gear.tree[type][user.stats.class], (item) => { - return !user.items.gear.owned[item.key]; - }); + each(content.gearTypes, type => { + const found = lodashFind(content.gear.tree[type][user.stats.class], item => !user.items.gear.owned[item.key]); if (found) changes.push(found); }); - changes = changes.concat(filter(content.gear.flat, (val) => { + changes = changes.concat(filter(content.gear.flat, val => { if (['special', 'mystery', 'armoire'].indexOf(val.klass) !== -1 && !user.items.gear.owned[val.key] && (val.canOwn ? val.canOwn(user) : false)) { return true; - } else { - return false; } + return false; })); - return sortBy(changes, (change) => sortOrder[change.type]); + return sortBy(changes, change => sortOrder[change.type]); } diff --git a/website/common/script/ops/addTag.js b/website/common/script/ops/addTag.js index 18961c5923..0e9db409e2 100644 --- a/website/common/script/ops/addTag.js +++ b/website/common/script/ops/addTag.js @@ -1,5 +1,5 @@ -import uuid from '../libs/uuid'; import get from 'lodash/get'; +import uuid from '../libs/uuid'; // TODO used only in client, move there? diff --git a/website/common/script/ops/addTask.js b/website/common/script/ops/addTask.js index 11bc1c2004..e89485bb3c 100644 --- a/website/common/script/ops/addTask.js +++ b/website/common/script/ops/addTask.js @@ -1,10 +1,10 @@ -import taskDefaults from '../libs/taskDefaults'; import clone from 'lodash/clone'; +import taskDefaults from '../libs/taskDefaults'; // TODO move to client since it's only used there? -export default function addTask (user, req = {body: {}}) { - let task = taskDefaults(req.body, user); +export default function addTask (user, req = { body: {} }) { + const task = taskDefaults(req.body, user); user.tasksOrder[`${task.type}s`].unshift(task._id); user[`${task.type}s`].unshift(task); diff --git a/website/common/script/ops/blockUser.js b/website/common/script/ops/blockUser.js index 90f3fe0d8f..9e9f58f675 100644 --- a/website/common/script/ops/blockUser.js +++ b/website/common/script/ops/blockUser.js @@ -8,7 +8,7 @@ export default function blockUser (user, req = {}) { if (!validator.isUUID(req.params.uuid)) throw new BadRequest(i18n.t('invalidUUID', req.language)); if (req.params.uuid === user._id) throw new BadRequest(i18n.t('blockYourself', req.language)); - let i = user.inbox.blocks.indexOf(req.params.uuid); + const i = user.inbox.blocks.indexOf(req.params.uuid); if (i === -1) { user.inbox.blocks.push(req.params.uuid); } else { diff --git a/website/common/script/ops/buy/abstractBuyOperation.js b/website/common/script/ops/buy/abstractBuyOperation.js index 3f87f08694..38d81517b5 100644 --- a/website/common/script/ops/buy/abstractBuyOperation.js +++ b/website/common/script/ops/buy/abstractBuyOperation.js @@ -1,11 +1,11 @@ +import _merge from 'lodash/merge'; +import _get from 'lodash/get'; import i18n from '../../i18n'; import { NotAuthorized, NotImplementedError, BadRequest, } from '../../libs/errors'; -import _merge from 'lodash/merge'; -import _get from 'lodash/get'; export class AbstractBuyOperation { /** @@ -18,7 +18,7 @@ export class AbstractBuyOperation { this.req = req || {}; this.analytics = analytics; - let quantity = _get(req, 'quantity'); + const quantity = _get(req, 'quantity'); this.quantity = quantity ? Number(quantity) : 1; if (this.quantity < 1 || !Number.isInteger(this.quantity)) throw new BadRequest(this.i18n('invalidQuantity')); @@ -48,8 +48,7 @@ export class AbstractBuyOperation { * @returns {String} */ getItemType (item) { - if (!item.type) - throw new NotImplementedError('item doesn\'t have a type property'); + if (!item.type) throw new NotImplementedError('item doesn\'t have a type property'); return item.type; } @@ -95,7 +94,7 @@ export class AbstractBuyOperation { this.extractAndValidateParams(this.user, this.req); - let resultObj = this.executeChanges(this.user, this.item, this.req); + const resultObj = this.executeChanges(this.user, this.item, this.req); if (this.analytics) { this.sendToAnalytics(this.analyticsData()); @@ -110,7 +109,7 @@ export class AbstractBuyOperation { sendToAnalytics (additionalData = {}) { // spread-operator produces an "unexpected token" error - let analyticsData = _merge(additionalData, { + const analyticsData = _merge(additionalData, { // ...additionalData, uuid: this.user._id, category: 'behavior', @@ -132,9 +131,9 @@ export class AbstractGoldItemOperation extends AbstractBuyOperation { canUserPurchase (user, item) { this.item = item; - let itemValue = this.getItemValue(item); + const itemValue = this.getItemValue(item); - let userGold = user.stats.gp; + const userGold = user.stats.gp; if (userGold < itemValue * this.quantity) { throw new NotAuthorized(this.i18n('messageNotEnoughGold')); @@ -146,7 +145,7 @@ export class AbstractGoldItemOperation extends AbstractBuyOperation { } subtractCurrency (user, item) { - let itemValue = this.getItemValue(item); + const itemValue = this.getItemValue(item); user.stats.gp -= itemValue * this.quantity; } @@ -168,7 +167,7 @@ export class AbstractGemItemOperation extends AbstractBuyOperation { canUserPurchase (user, item) { this.item = item; - let itemValue = this.getItemValue(item); + const itemValue = this.getItemValue(item); if (!item.canBuy(user)) { throw new NotAuthorized(this.i18n('messageNotAvailable')); @@ -180,7 +179,7 @@ export class AbstractGemItemOperation extends AbstractBuyOperation { } subtractCurrency (user, item) { - let itemValue = this.getItemValue(item); + const itemValue = this.getItemValue(item); user.balance -= itemValue * this.quantity; } diff --git a/website/common/script/ops/buy/buy.js b/website/common/script/ops/buy/buy.js index ee7d5a7768..50837375ed 100644 --- a/website/common/script/ops/buy/buy.js +++ b/website/common/script/ops/buy/buy.js @@ -2,28 +2,28 @@ import get from 'lodash/get'; import { BadRequest, } from '../../libs/errors'; -import {BuyArmoireOperation} from './buyArmoire'; -import {BuyHealthPotionOperation} from './buyHealthPotion'; -import {BuyMarketGearOperation} from './buyMarketGear'; +import { BuyArmoireOperation } from './buyArmoire'; +import { BuyHealthPotionOperation } from './buyHealthPotion'; +import { BuyMarketGearOperation } from './buyMarketGear'; import buyMysterySet from './buyMysterySet'; -import {BuyQuestWithGoldOperation} from './buyQuestGold'; -import {BuySpellOperation} from './buySpell'; +import { BuyQuestWithGoldOperation } from './buyQuestGold'; +import { BuySpellOperation } from './buySpell'; import purchaseOp from './purchase'; import hourglassPurchase from './hourglassPurchase'; import errorMessage from '../../libs/errorMessage'; -import {BuyGemOperation} from './buyGem'; -import {BuyQuestWithGemOperation} from './buyQuestGem'; -import {BuyHourglassMountOperation} from './buyMount'; +import { BuyGemOperation } from './buyGem'; +import { BuyQuestWithGemOperation } from './buyQuestGem'; +import { BuyHourglassMountOperation } from './buyMount'; // @TODO: remove the req option style. Dependency on express structure is an anti-pattern // We should either have more params or a set structure validated by a Type checker // @TODO: when we are sure buy is the only function used, let's move the buy files to a folder -export default function buy (user, req = {}, analytics, options = {quantity: 1, hourglass: false}) { - let key = get(req, 'params.key'); - const hourglass = options.hourglass; - const quantity = options.quantity; +export default function buy (user, req = {}, analytics, options = { quantity: 1, hourglass: false }) { + const key = get(req, 'params.key'); + const { hourglass } = options; + const { quantity } = options; if (!key) throw new BadRequest(errorMessage('missingKeyParam')); // @TODO: Slowly remove the need for key and use type instead diff --git a/website/common/script/ops/buy/buyArmoire.js b/website/common/script/ops/buy/buyArmoire.js index 985fcd023e..8852bde107 100644 --- a/website/common/script/ops/buy/buyArmoire.js +++ b/website/common/script/ops/buy/buyArmoire.js @@ -1,15 +1,15 @@ -import content from '../../content/index'; import filter from 'lodash/filter'; import isEmpty from 'lodash/isEmpty'; import pick from 'lodash/pick'; +import content from '../../content/index'; import * as count from '../../count'; import splitWhitespace from '../../libs/splitWhitespace'; import { NotAuthorized, } from '../../libs/errors'; import randomVal, * as randomValFns from '../../libs/randomVal'; -import {removeItemByPath} from '../pinnedGearUtils'; -import {AbstractGoldItemOperation} from './abstractBuyOperation'; +import { removeItemByPath } from '../pinnedGearUtils'; +import { AbstractGoldItemOperation } from './abstractBuyOperation'; // TODO this is only used on the server // move out of common? @@ -27,7 +27,7 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation { } extractAndValidateParams (user) { - let item = content.armoire; + const item = content.armoire; this.canUserPurchase(user, item); } @@ -35,11 +35,9 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation { executeChanges (user, item) { let result = {}; - let armoireResult = randomValFns.trueRandom(); - let eligibleEquipment = filter(content.gear.flat, (eligible) => { - return eligible.klass === 'armoire' && !user.items.gear.owned[eligible.key]; - }); - let armoireHasEquipment = !isEmpty(eligibleEquipment); + const armoireResult = randomValFns.trueRandom(); + const eligibleEquipment = filter(content.gear.flat, eligible => eligible.klass === 'armoire' && !user.items.gear.owned[eligible.key]); + const armoireHasEquipment = !isEmpty(eligibleEquipment); if (armoireHasEquipment && (armoireResult < YIELD_EQUIPMENT_THRESHOLD || !user.flags.armoireOpened)) { result = this._gearResult(user, eligibleEquipment); @@ -51,7 +49,7 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation { this.subtractCurrency(user, item); - let {message, armoireResp} = result; + let { message, armoireResp } = result; if (!message) { message = this.i18n('messageBought', { @@ -59,7 +57,7 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation { }); } - let resData = pick(user, splitWhitespace('items flags')); + const resData = pick(user, splitWhitespace('items flags')); if (armoireResp) resData.armoire = armoireResp; return [ @@ -83,7 +81,7 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation { _gearResult (user, eligibleEquipment) { eligibleEquipment.sort(); - let drop = randomVal(eligibleEquipment); + const drop = randomVal(eligibleEquipment); if (user.items.gear.owned[drop.key]) { throw new NotAuthorized(this.i18n('equipmentAlreadyOwned')); @@ -93,7 +91,7 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation { if (user.markModified) user.markModified('items.gear.owned'); user.flags.armoireOpened = true; - let message = this.i18n('armoireEquipment', { + const message = this.i18n('armoireEquipment', { image: ``, dropText: drop.text(this.req.language), }); @@ -108,7 +106,7 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation { this._trackDropAnalytics(user._id, drop.key); } - let armoireResp = { + const armoireResp = { type: 'gear', dropKey: drop.key, dropText: drop.text(this.req.language), @@ -121,7 +119,7 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation { } _foodResult (user) { - let drop = randomVal(filter(content.food, { + const drop = randomVal(filter(content.food, { canDrop: true, })); @@ -147,7 +145,7 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation { } _experienceResult (user) { - let armoireExp = Math.floor(randomValFns.trueRandom() * 40 + 10); + const armoireExp = Math.floor(randomValFns.trueRandom() * 40 + 10); user.stats.exp += armoireExp; return { diff --git a/website/common/script/ops/buy/buyGem.js b/website/common/script/ops/buy/buyGem.js index b2ea03046e..9ef30ae786 100644 --- a/website/common/script/ops/buy/buyGem.js +++ b/website/common/script/ops/buy/buyGem.js @@ -1,11 +1,11 @@ import pick from 'lodash/pick'; +import get from 'lodash/get'; import splitWhitespace from '../../libs/splitWhitespace'; import { BadRequest, NotAuthorized, } from '../../libs/errors'; -import {AbstractGoldItemOperation} from './abstractBuyOperation'; -import get from 'lodash/get'; +import { AbstractGoldItemOperation } from './abstractBuyOperation'; import planGemLimits from '../../libs/planGemLimits'; export class BuyGemOperation extends AbstractGoldItemOperation { @@ -30,10 +30,10 @@ export class BuyGemOperation extends AbstractGoldItemOperation { } extractAndValidateParams (user, req) { - let key = this.key = get(req, 'params.key'); + const key = this.key = get(req, 'params.key'); if (!key) throw new BadRequest(this.i18n('missingKeyParam')); - let convCap = planGemLimits.convCap; + let { convCap } = planGemLimits; convCap += user.purchased.plan.consecutive.gemCapExtra; // todo better name? @@ -50,7 +50,7 @@ export class BuyGemOperation extends AbstractGoldItemOperation { super.canUserPurchase(user, item); if (user.purchased.plan.gemsBought >= this.convCap) { - throw new NotAuthorized(this.i18n('reachedGoldToGemCap', {convCap: this.convCap})); + throw new NotAuthorized(this.i18n('reachedGoldToGemCap', { convCap: this.convCap })); } if (user.purchased.plan.gemsBought + this.quantity > this.convCap) { @@ -69,7 +69,7 @@ export class BuyGemOperation extends AbstractGoldItemOperation { return [ pick(user, splitWhitespace('stats balance')), - this.i18n('plusGem', {count: this.quantity}), + this.i18n('plusGem', { count: this.quantity }), ]; } diff --git a/website/common/script/ops/buy/buyHealthPotion.js b/website/common/script/ops/buy/buyHealthPotion.js index e3d6c1062a..bc0f5ef5f3 100644 --- a/website/common/script/ops/buy/buyHealthPotion.js +++ b/website/common/script/ops/buy/buyHealthPotion.js @@ -3,7 +3,7 @@ import { NotAuthorized, } from '../../libs/errors'; -import { AbstractGoldItemOperation} from './abstractBuyOperation'; +import { AbstractGoldItemOperation } from './abstractBuyOperation'; export class BuyHealthPotionOperation extends AbstractGoldItemOperation { constructor (user, req, analytics) { @@ -15,8 +15,8 @@ export class BuyHealthPotionOperation extends AbstractGoldItemOperation { } extractAndValidateParams (user) { - let item = content.potion; - let userHp = user.stats.hp; + const item = content.potion; + const userHp = user.stats.hp; super.canUserPurchase(user, item); @@ -37,7 +37,7 @@ export class BuyHealthPotionOperation extends AbstractGoldItemOperation { this.subtractCurrency(user, item, this.quantity); - let message = this.i18n('messageBought', { + const message = this.i18n('messageBought', { itemText: this.item.text(this.req.language), }); diff --git a/website/common/script/ops/buy/buyMarketGear.js b/website/common/script/ops/buy/buyMarketGear.js index 5f8728bf53..293b9bd5ec 100644 --- a/website/common/script/ops/buy/buyMarketGear.js +++ b/website/common/script/ops/buy/buyMarketGear.js @@ -1,6 +1,6 @@ -import content from '../../content/index'; import get from 'lodash/get'; import pick from 'lodash/pick'; +import content from '../../content/index'; import splitWhitespace from '../../libs/splitWhitespace'; import { BadRequest, @@ -10,7 +10,7 @@ import { import handleTwoHanded from '../../fns/handleTwoHanded'; import ultimateGear from '../../fns/ultimateGear'; -import {removePinnedGearAddPossibleNewOnes} from '../pinnedGearUtils'; +import { removePinnedGearAddPossibleNewOnes } from '../pinnedGearUtils'; import { AbstractGoldItemOperation } from './abstractBuyOperation'; import errorMessage from '../../libs/errorMessage'; @@ -24,7 +24,7 @@ export class BuyMarketGearOperation extends AbstractGoldItemOperation { return false; } - canUserPurchase (user, item) { + canUserPurchase (user, item) { super.canUserPurchase(user, item); const checkKlass = item.klass && !['special', 'armoire', user.stats.class].includes(item.klass); @@ -37,11 +37,11 @@ export class BuyMarketGearOperation extends AbstractGoldItemOperation { } extractAndValidateParams (user, req) { - let key = this.key = get(req, 'params.key'); + const key = this.key = get(req, 'params.key'); if (!key) throw new BadRequest(errorMessage('missingKeyParam')); - let item = content.gear.flat[key]; - if (!item) throw new NotFound(errorMessage('itemNotFound', {key})); + const item = content.gear.flat[key]; + if (!item) throw new NotFound(errorMessage('itemNotFound', { key })); this.canUserPurchase(user, item); @@ -49,12 +49,12 @@ export class BuyMarketGearOperation extends AbstractGoldItemOperation { throw new NotAuthorized(this.i18n('equipmentAlreadyOwned')); } - let itemIndex = Number(item.index); + const itemIndex = Number(item.index); if (Number.isInteger(itemIndex) && content.classes.includes(item.klass)) { - let previousLevelGear = key.replace(/[0-9]/, itemIndex - 1); - let hasPreviousLevelGear = user.items.gear.owned[previousLevelGear]; - let checkIndexToType = itemIndex > (item.type === 'weapon' || item.type === 'shield' && item.klass === 'rogue' ? 0 : 1); + const previousLevelGear = key.replace(/[0-9]/, itemIndex - 1); + const hasPreviousLevelGear = user.items.gear.owned[previousLevelGear]; + const checkIndexToType = itemIndex > (item.type === 'weapon' || item.type === 'shield' && item.klass === 'rogue' ? 0 : 1); if (checkIndexToType && !hasPreviousLevelGear) { throw new NotAuthorized(this.i18n('previousGearNotOwned')); diff --git a/website/common/script/ops/buy/buyMount.js b/website/common/script/ops/buy/buyMount.js index ced5cddfc8..50fde36ce7 100644 --- a/website/common/script/ops/buy/buyMount.js +++ b/website/common/script/ops/buy/buyMount.js @@ -1,13 +1,13 @@ +import get from 'lodash/get'; +import includes from 'lodash/includes'; +import keys from 'lodash/keys'; import content from '../../content/index'; import { BadRequest, NotAuthorized, } from '../../libs/errors'; -import {AbstractHourglassItemOperation} from './abstractBuyOperation'; -import get from 'lodash/get'; -import includes from 'lodash/includes'; -import keys from 'lodash/keys'; +import { AbstractHourglassItemOperation } from './abstractBuyOperation'; export class BuyHourglassMountOperation extends AbstractHourglassItemOperation { constructor (user, req, analytics) { @@ -19,7 +19,7 @@ export class BuyHourglassMountOperation extends AbstractHourglassItemOperation { } extractAndValidateParams (user, req) { - let key = this.key = get(req, 'params.key'); + const key = this.key = get(req, 'params.key'); if (!key) throw new BadRequest(this.i18n('missingKeyParam')); @@ -43,7 +43,7 @@ export class BuyHourglassMountOperation extends AbstractHourglassItemOperation { this.subtractCurrency(user); - let message = this.i18n('hourglassPurchase'); + const message = this.i18n('hourglassPurchase'); return [ { items: user.items, purchasedPlanConsecutive: user.purchased.plan.consecutive }, @@ -52,7 +52,7 @@ export class BuyHourglassMountOperation extends AbstractHourglassItemOperation { } analyticsData () { - let data = super.analyticsData(); + const data = super.analyticsData(); data.itemType = 'mounts'; return data; } diff --git a/website/common/script/ops/buy/buyMysterySet.js b/website/common/script/ops/buy/buyMysterySet.js index 6b2aeab148..570f297959 100644 --- a/website/common/script/ops/buy/buyMysterySet.js +++ b/website/common/script/ops/buy/buyMysterySet.js @@ -1,7 +1,7 @@ -import i18n from '../../i18n'; -import content from '../../content/index'; import get from 'lodash/get'; import each from 'lodash/each'; +import i18n from '../../i18n'; +import content from '../../content/index'; import { BadRequest, NotAuthorized, @@ -10,15 +10,15 @@ import { import errorMessage from '../../libs/errorMessage'; export default function buyMysterySet (user, req = {}, analytics) { - let key = get(req, 'params.key'); + const key = get(req, 'params.key'); if (!key) throw new BadRequest(errorMessage('missingKeyParam')); if (!(user.purchased.plan.consecutive.trinkets > 0)) { throw new NotAuthorized(i18n.t('notEnoughHourglasses', req.language)); } - let ref = content.timeTravelerStore(user); - let mysterySet = ref ? ref[key] : undefined; + const ref = content.timeTravelerStore(user); + const mysterySet = ref ? ref[key] : undefined; if (!mysterySet) { throw new NotFound(i18n.t('mysterySetNotFound', req.language)); diff --git a/website/common/script/ops/buy/buyQuestGem.js b/website/common/script/ops/buy/buyQuestGem.js index 63ba528ce3..893062e398 100644 --- a/website/common/script/ops/buy/buyQuestGem.js +++ b/website/common/script/ops/buy/buyQuestGem.js @@ -1,13 +1,13 @@ +import get from 'lodash/get'; import { BadRequest, NotAuthorized, NotFound, } from '../../libs/errors'; import content from '../../content/index'; -import get from 'lodash/get'; import errorMessage from '../../libs/errorMessage'; -import {AbstractGemItemOperation} from './abstractBuyOperation'; +import { AbstractGemItemOperation } from './abstractBuyOperation'; export class BuyQuestWithGemOperation extends AbstractGemItemOperation { constructor (user, req, analytics) { @@ -31,15 +31,15 @@ export class BuyQuestWithGemOperation extends AbstractGemItemOperation { } extractAndValidateParams (user, req) { - let key = this.key = get(req, 'params.key'); + const key = this.key = get(req, 'params.key'); if (!key) throw new BadRequest(errorMessage('missingKeyParam')); - let item = content.quests[key]; + const item = content.quests[key]; - if (!item) throw new NotFound(errorMessage('questNotFound', {key})); + if (!item) throw new NotFound(errorMessage('questNotFound', { key })); if (item.category === 'gold') { - throw new NotAuthorized(this.i18n('questNotGemPurchasable', {key})); + throw new NotAuthorized(this.i18n('questNotGemPurchasable', { key })); } this.canUserPurchase(user, item); diff --git a/website/common/script/ops/buy/buyQuestGold.js b/website/common/script/ops/buy/buyQuestGold.js index 09f04dcb59..89a0be0edd 100644 --- a/website/common/script/ops/buy/buyQuestGold.js +++ b/website/common/script/ops/buy/buyQuestGold.js @@ -1,12 +1,12 @@ +import get from 'lodash/get'; import { BadRequest, NotAuthorized, NotFound, } from '../../libs/errors'; import content from '../../content/index'; -import get from 'lodash/get'; -import {AbstractGoldItemOperation} from './abstractBuyOperation'; +import { AbstractGoldItemOperation } from './abstractBuyOperation'; import errorMessage from '../../libs/errorMessage'; export class BuyQuestWithGoldOperation extends AbstractGoldItemOperation { @@ -19,10 +19,10 @@ export class BuyQuestWithGoldOperation extends AbstractGoldItemOperation { } userAbleToStartMasterClasser (user) { - return user.achievements.quests.dilatoryDistress3 && - user.achievements.quests.mayhemMistiflying3 && - user.achievements.quests.stoikalmCalamity3 && - user.achievements.quests.taskwoodsTerror3; + return user.achievements.quests.dilatoryDistress3 + && user.achievements.quests.mayhemMistiflying3 + && user.achievements.quests.stoikalmCalamity3 + && user.achievements.quests.taskwoodsTerror3; } getItemKey () { @@ -38,15 +38,15 @@ export class BuyQuestWithGoldOperation extends AbstractGoldItemOperation { } extractAndValidateParams (user, req) { - let key = this.key = get(req, 'params.key'); + const key = this.key = get(req, 'params.key'); if (!key) throw new BadRequest(errorMessage('missingKeyParam')); - let item = content.quests[key]; + const item = content.quests[key]; - if (!item) throw new NotFound(errorMessage('questNotFound', {key})); + if (!item) throw new NotFound(errorMessage('questNotFound', { key })); if (!(item.category === 'gold' && item.goldValue)) { - throw new NotAuthorized(this.i18n('questNotGoldPurchasable', {key})); + throw new NotAuthorized(this.i18n('questNotGoldPurchasable', { key })); } this.checkPrerequisites(user, key); @@ -61,7 +61,7 @@ export class BuyQuestWithGoldOperation extends AbstractGoldItemOperation { } if (item && item.previous && !user.achievements.quests[item.previous]) { - throw new NotAuthorized(this.i18n('mustComplete', {quest: item.previous})); + throw new NotAuthorized(this.i18n('mustComplete', { quest: item.previous })); } } diff --git a/website/common/script/ops/buy/buySpell.js b/website/common/script/ops/buy/buySpell.js index 46e73a5d81..123455d6c7 100644 --- a/website/common/script/ops/buy/buySpell.js +++ b/website/common/script/ops/buy/buySpell.js @@ -1,12 +1,12 @@ -import content from '../../content/index'; import get from 'lodash/get'; import pick from 'lodash/pick'; +import content from '../../content/index'; import splitWhitespace from '../../libs/splitWhitespace'; import { BadRequest, NotFound, } from '../../libs/errors'; -import {AbstractGoldItemOperation} from './abstractBuyOperation'; +import { AbstractGoldItemOperation } from './abstractBuyOperation'; import errorMessage from '../../libs/errorMessage'; export class BuySpellOperation extends AbstractGoldItemOperation { @@ -27,11 +27,11 @@ export class BuySpellOperation extends AbstractGoldItemOperation { } extractAndValidateParams (user, req) { - let key = this.key = get(req, 'params.key'); + const key = this.key = get(req, 'params.key'); if (!key) throw new BadRequest(errorMessage('missingKeyParam')); - let item = content.special[key]; - if (!item) throw new NotFound(errorMessage('spellNotFound', {spellId: key})); + const item = content.special[key]; + if (!item) throw new NotFound(errorMessage('spellNotFound', { spellId: key })); this.canUserPurchase(user, item); } diff --git a/website/common/script/ops/buy/hourglassPurchase.js b/website/common/script/ops/buy/hourglassPurchase.js index fbea9d2541..dba5b93c30 100644 --- a/website/common/script/ops/buy/hourglassPurchase.js +++ b/website/common/script/ops/buy/hourglassPurchase.js @@ -1,8 +1,8 @@ -import content from '../../content/index'; -import i18n from '../../i18n'; import get from 'lodash/get'; import includes from 'lodash/includes'; import keys from 'lodash/keys'; +import i18n from '../../i18n'; +import content from '../../content/index'; import { BadRequest, NotAuthorized, @@ -10,10 +10,10 @@ import { import errorMessage from '../../libs/errorMessage'; export default function purchaseHourglass (user, req = {}, analytics, quantity = 1) { - let key = get(req, 'params.key'); + const key = get(req, 'params.key'); if (!key) throw new BadRequest(errorMessage('missingKeyParam')); - let type = get(req, 'params.type'); + const type = get(req, 'params.type'); if (!type) throw new BadRequest(errorMessage('missingTypeParam')); if (type === 'quests') { @@ -29,7 +29,7 @@ export default function purchaseHourglass (user, req = {}, analytics, quantity = if (user.markModified) user.markModified('items.quests'); } else { if (!content.timeTravelStable[type]) { - throw new NotAuthorized(i18n.t('typeNotAllowedHourglass', {allowedTypes: keys(content.timeTravelStable).toString()}, req.language)); + throw new NotAuthorized(i18n.t('typeNotAllowedHourglass', { allowedTypes: keys(content.timeTravelStable).toString() }, req.language)); } if (!includes(keys(content.timeTravelStable[type]), key)) { diff --git a/website/common/script/ops/buy/purchase.js b/website/common/script/ops/buy/purchase.js index 8b6bd1b017..e69fcfaed5 100644 --- a/website/common/script/ops/buy/purchase.js +++ b/website/common/script/ops/buy/purchase.js @@ -1,8 +1,8 @@ -import content from '../../content/index'; -import i18n from '../../i18n'; import get from 'lodash/get'; import pick from 'lodash/pick'; import forEach from 'lodash/forEach'; +import i18n from '../../i18n'; +import content from '../../content/index'; import splitWhitespace from '../../libs/splitWhitespace'; import { NotFound, @@ -21,7 +21,7 @@ function getItemAndPrice (user, type, key, req) { item = content.gear.flat[key]; if (!item) { - throw new NotFound(i18n.t('contentKeyNotFound', {type}, req.language)); + throw new NotFound(i18n.t('contentKeyNotFound', { type }, req.language)); } if (user.items.gear.owned[key]) { @@ -33,13 +33,13 @@ function getItemAndPrice (user, type, key, req) { item = content[type][key]; if (!item) { - throw new NotFound(i18n.t('contentKeyNotFound', {type}, req.language)); + throw new NotFound(i18n.t('contentKeyNotFound', { type }, req.language)); } price = item.value / 4; } - return {item, price}; + return { item, price }; } function purchaseItem (user, item, price, type, key) { @@ -49,8 +49,8 @@ function purchaseItem (user, item, price, type, key) { user.items.gear.owned[key] = true; if (user.markModified) user.markModified('items.gear.owned'); } else if (type === 'bundles') { - let subType = item.type; - forEach(item.bundleKeys, function addBundledItems (bundledKey) { + const subType = item.type; + forEach(item.bundleKeys, bundledKey => { if (!user.items[subType][bundledKey] || user.items[subType][key] < 0) { user.items[subType][bundledKey] = 0; } @@ -69,10 +69,10 @@ function purchaseItem (user, item, price, type, key) { const acceptedTypes = ['eggs', 'hatchingPotions', 'food', 'gear', 'bundles']; const singlePurchaseTypes = ['gear']; export default function purchase (user, req = {}, analytics) { - let type = get(req.params, 'type'); - let key = get(req.params, 'key'); + const type = get(req.params, 'type'); + const key = get(req.params, 'key'); - let quantity = req.quantity ? Number(req.quantity) : 1; + const quantity = req.quantity ? Number(req.quantity) : 1; if (quantity < 1 || !Number.isInteger(quantity)) throw new BadRequest(i18n.t('invalidQuantity', req.language)); if (!type) { @@ -87,7 +87,7 @@ export default function purchase (user, req = {}, analytics) { throw new NotFound(i18n.t('notAccteptedType', req.language)); } - let {price, item} = getItemAndPrice(user, type, key, req); + const { price, item } = getItemAndPrice(user, type, key, req); if (!item.canBuy(user)) { throw new NotAuthorized(i18n.t('messageNotAvailable', req.language)); @@ -98,7 +98,7 @@ export default function purchase (user, req = {}, analytics) { } if (singlePurchaseTypes.includes(type)) { - let itemInfo = getItemInfo(user, type, item); + const itemInfo = getItemInfo(user, type, item); removeItemByPath(user, itemInfo.path); } diff --git a/website/common/script/ops/changeClass.js b/website/common/script/ops/changeClass.js index fe76572e7e..c15d652248 100644 --- a/website/common/script/ops/changeClass.js +++ b/website/common/script/ops/changeClass.js @@ -1,6 +1,6 @@ -import i18n from '../i18n'; import get from 'lodash/get'; import pick from 'lodash/pick'; +import i18n from '../i18n'; import splitWhitespace from '../libs/splitWhitespace'; import { capByLevel } from '../statHelpers'; import { @@ -34,7 +34,7 @@ function resetClass (user, req = {}) { } export default function changeClass (user, req = {}, analytics) { - let klass = get(req, 'query.class'); + const klass = get(req, 'query.class'); let balanceRemoved = 0; // user.flags.classSelected is set to false after the user paid the 3 gems if (user.stats.lvl < 10) { diff --git a/website/common/script/ops/deleteTag.js b/website/common/script/ops/deleteTag.js index d9e3da8643..70249c3a5b 100644 --- a/website/common/script/ops/deleteTag.js +++ b/website/common/script/ops/deleteTag.js @@ -1,15 +1,15 @@ -import i18n from '../i18n'; import get from 'lodash/get'; import findIndex from 'lodash/findIndex'; import each from 'lodash/each'; +import i18n from '../i18n'; import { NotFound } from '../libs/errors'; // TODO used only in client, move there? export default function deleteTag (user, req = {}) { - let tid = get(req, 'params.id'); + const tid = get(req, 'params.id'); - let index = findIndex(user.tags, { + const index = findIndex(user.tags, { id: tid, }); @@ -17,16 +17,14 @@ export default function deleteTag (user, req = {}) { throw new NotFound(i18n.t('messageTagNotFound', req.language)); } - let tag = user.tags[index]; + const tag = user.tags[index]; delete user.filters[tag.id]; user.tags.splice(index, 1); - each(user.tasks, (task) => { - return delete task.tags[tag.id]; - }); + each(user.tasks, task => delete task.tags[tag.id]); - each(['habits', 'dailys', 'todos', 'rewards'], (type) => { + each(['habits', 'dailys', 'todos', 'rewards'], type => { if (user.markModified) user.markModified(type); }); diff --git a/website/common/script/ops/deleteTask.js b/website/common/script/ops/deleteTask.js index 376dfb932e..6a9cff9a6b 100644 --- a/website/common/script/ops/deleteTask.js +++ b/website/common/script/ops/deleteTask.js @@ -1,17 +1,15 @@ -import i18n from '../i18n'; -import { NotFound } from '../libs/errors'; import get from 'lodash/get'; import findIndex from 'lodash/findIndex'; +import i18n from '../i18n'; +import { NotFound } from '../libs/errors'; // TODO used only in client, move there? export default function deleteTask (user, req = {}) { - let tid = get(req, 'params.id'); - let taskType = get(req, 'params.taskType'); + const tid = get(req, 'params.id'); + const taskType = get(req, 'params.taskType'); - let index = findIndex(user[`${taskType}s`], function findById (task) { - return task._id === tid; - }); + const index = findIndex(user[`${taskType}s`], task => task._id === tid); if (index === -1) { throw new NotFound(i18n.t('messageTaskNotFound', req.language)); diff --git a/website/common/script/ops/disableClasses.js b/website/common/script/ops/disableClasses.js index 7ef15802b1..b499b84b9b 100644 --- a/website/common/script/ops/disableClasses.js +++ b/website/common/script/ops/disableClasses.js @@ -1,6 +1,6 @@ +import pick from 'lodash/pick'; import splitWhitespace from '../libs/splitWhitespace'; import { capByLevel } from '../statHelpers'; -import pick from 'lodash/pick'; export default function disableClasses (user) { user.stats.class = 'warrior'; diff --git a/website/common/script/ops/equip.js b/website/common/script/ops/equip.js index d12e25043e..2d55a6cb6f 100644 --- a/website/common/script/ops/equip.js +++ b/website/common/script/ops/equip.js @@ -1,3 +1,4 @@ +import get from 'lodash/get'; import content from '../content/index'; import i18n from '../i18n'; import handleTwoHanded from '../fns/handleTwoHanded'; @@ -5,14 +6,13 @@ import { NotFound, BadRequest, } from '../libs/errors'; -import get from 'lodash/get'; import errorMessage from '../libs/errorMessage'; export default function equip (user, req = {}) { // Being type a parameter followed by another parameter // when using the API it must be passes specifically in the URL, it's won't default to equipped - let type = get(req, 'params.type', 'equipped'); - let key = get(req, 'params.key'); + const type = get(req, 'params.type', 'equipped'); + const key = get(req, 'params.key'); if (!key || !type) throw new BadRequest(errorMessage('missingTypeKeyEquip')); if (['mount', 'pet', 'costume', 'equipped'].indexOf(type) === -1) { @@ -44,25 +44,25 @@ export default function equip (user, req = {}) { throw new NotFound(i18n.t('gearNotOwned', req.language)); } - let item = content.gear.flat[key]; + const item = content.gear.flat[key]; if (user.items.gear[type][item.type] === key) { - user.items.gear[type] = Object.assign( - {}, - user.items.gear[type].toObject ? user.items.gear[type].toObject() : user.items.gear[type], - {[item.type]: `${item.type}_base_0`} - ); + user.items.gear[type] = { + + ...(user.items.gear[type].toObject ? user.items.gear[type].toObject() : user.items.gear[type]), + [item.type]: `${item.type}_base_0`, + }; if (user.markModified && type === 'owned') user.markModified('items.gear.owned'); message = i18n.t('messageUnEquipped', { itemText: item.text(req.language), }, req.language); } else { - user.items.gear[type] = Object.assign( - {}, - user.items.gear[type].toObject ? user.items.gear[type].toObject() : user.items.gear[type], - {[item.type]: item.key} - ); + user.items.gear[type] = { + + ...(user.items.gear[type].toObject ? user.items.gear[type].toObject() : user.items.gear[type]), + [item.type]: item.key, + }; if (user.markModified && type === 'owned') user.markModified('items.gear.owned'); message = handleTwoHanded(user, item, type, req); @@ -71,7 +71,7 @@ export default function equip (user, req = {}) { } } - let res = [user.items]; + const res = [user.items]; if (message) res.push(message); return res; } diff --git a/website/common/script/ops/feed.js b/website/common/script/ops/feed.js index 1087212cba..bd7c4fcb29 100644 --- a/website/common/script/ops/feed.js +++ b/website/common/script/ops/feed.js @@ -1,10 +1,10 @@ -import content from '../content/index'; -import i18n from '../i18n'; import forEach from 'lodash/forEach'; import findIndex from 'lodash/findIndex'; import get from 'lodash/get'; import keys from 'lodash/keys'; import upperFirst from 'lodash/upperFirst'; +import i18n from '../i18n'; +import content from '../content/index'; import { BadRequest, NotAuthorized, @@ -32,7 +32,7 @@ function evolve (user, pet, req) { export default function feed (user, req = {}) { let pet = get(req, 'params.pet'); - let foodK = get(req, 'params.food'); + const foodK = get(req, 'params.food'); if (!pet || !foodK) throw new BadRequest(errorMessage('missingPetFoodFeed')); @@ -42,12 +42,12 @@ export default function feed (user, req = {}) { throw new BadRequest(errorMessage('invalidPetName')); } - let food = content.food[foodK]; + const food = content.food[foodK]; if (!food) { throw new NotFound(errorMessage('invalidFoodName', req.language)); } - let userPets = user.items.pets; + const userPets = user.items.pets; if (!userPets[pet.key]) { throw new NotFound(i18n.t('messagePetNotFound', req.language)); @@ -70,7 +70,7 @@ export default function feed (user, req = {}) { if (food.key === 'Saddle') { message = evolve(user, pet, req); } else { - let messageParams = { + const messageParams = { egg: pet.text(req.language), foodText: food.textThe(req.language), }; @@ -93,11 +93,9 @@ export default function feed (user, req = {}) { user.items.food[food.key]--; if (user.markModified) user.markModified('items.food'); - forEach(content.animalColorAchievements, (achievement) => { + forEach(content.animalColorAchievements, achievement => { if (!user.achievements[achievement.mountAchievement]) { - const mountIndex = findIndex(keys(content.dropEggs), (animal) => { - return !user.items.mounts[`${animal}-${achievement.color}`]; - }); + const mountIndex = findIndex(keys(content.dropEggs), animal => !user.items.mounts[`${animal}-${achievement.color}`]); if (mountIndex === -1) { user.achievements[achievement.mountAchievement] = true; if (user.addNotification) { diff --git a/website/common/script/ops/hatch.js b/website/common/script/ops/hatch.js index e867b69395..2401c52d0c 100644 --- a/website/common/script/ops/hatch.js +++ b/website/common/script/ops/hatch.js @@ -1,10 +1,10 @@ -import content from '../content/index'; -import i18n from '../i18n'; import findIndex from 'lodash/findIndex'; import forEach from 'lodash/forEach'; import get from 'lodash/get'; import keys from 'lodash/keys'; import upperFirst from 'lodash/upperFirst'; +import i18n from '../i18n'; +import content from '../content/index'; import { BadRequest, NotAuthorized, @@ -13,8 +13,8 @@ import { import errorMessage from '../libs/errorMessage'; export default function hatch (user, req = {}) { - let egg = get(req, 'params.egg'); - let hatchingPotion = get(req, 'params.hatchingPotion'); + const egg = get(req, 'params.egg'); + const hatchingPotion = get(req, 'params.hatchingPotion'); if (!(egg && hatchingPotion)) { throw new BadRequest(errorMessage('missingEggHatchingPotion')); @@ -28,7 +28,7 @@ export default function hatch (user, req = {}) { throw new BadRequest(i18n.t('messageInvalidEggPotionCombo', req.language)); } - let pet = `${egg}-${hatchingPotion}`; + const pet = `${egg}-${hatchingPotion}`; if (user.items.pets[pet] && user.items.pets[pet] > 0) { throw new NotAuthorized(i18n.t('messageAlreadyPet', req.language)); @@ -43,11 +43,9 @@ export default function hatch (user, req = {}) { user.markModified('items.hatchingPotions'); } - forEach(content.animalColorAchievements, (achievement) => { + forEach(content.animalColorAchievements, achievement => { if (!user.achievements[achievement.petAchievement]) { - const petIndex = findIndex(keys(content.dropEggs), (animal) => { - return isNaN(user.items.pets[`${animal}-${achievement.color}`]) || user.items.pets[`${animal}-${achievement.color}`] <= 0; - }); + const petIndex = findIndex(keys(content.dropEggs), animal => isNaN(user.items.pets[`${animal}-${achievement.color}`]) || user.items.pets[`${animal}-${achievement.color}`] <= 0); if (petIndex === -1) { user.achievements[achievement.petAchievement] = true; if (user.addNotification) { diff --git a/website/common/script/ops/openMysteryItem.js b/website/common/script/ops/openMysteryItem.js index 5b72720667..0ab7926c40 100644 --- a/website/common/script/ops/openMysteryItem.js +++ b/website/common/script/ops/openMysteryItem.js @@ -1,20 +1,18 @@ +import cloneDeep from 'lodash/cloneDeep'; import content from '../content/index'; import i18n from '../i18n'; import { BadRequest, } from '../libs/errors'; -import cloneDeep from 'lodash/cloneDeep'; function markNotificationAsRead (user) { - const index = user.notifications.findIndex(notification => { - return notification && notification.type === 'NEW_MYSTERY_ITEMS'; - }); + const index = user.notifications.findIndex(notification => notification && notification.type === 'NEW_MYSTERY_ITEMS'); if (index !== -1) user.notifications.splice(index, 1); } export default function openMysteryItem (user, req = {}, analytics) { - const mysteryItems = user.purchased.plan.mysteryItems; + const { mysteryItems } = user.purchased.plan; let item = mysteryItems.shift(); if (!item) { diff --git a/website/common/script/ops/pinnedGearUtils.js b/website/common/script/ops/pinnedGearUtils.js index 0afcfa1898..fea7994b35 100644 --- a/website/common/script/ops/pinnedGearUtils.js +++ b/website/common/script/ops/pinnedGearUtils.js @@ -1,3 +1,7 @@ +import each from 'lodash/each'; +import sortBy from 'lodash/sortBy'; +import lodashFind from 'lodash/find'; +import reduce from 'lodash/reduce'; import content from '../content/index'; import getItemInfo from '../libs/getItemInfo'; import { BadRequest } from '../libs/errors'; @@ -5,12 +9,8 @@ import i18n from '../i18n'; import getItemByPathAndType from '../libs/getItemByPathAndType'; import getOfficialPinnedItems from '../libs/getOfficialPinnedItems'; -import each from 'lodash/each'; -import sortBy from 'lodash/sortBy'; -import lodashFind from 'lodash/find'; -import reduce from 'lodash/reduce'; -let sortOrder = reduce(content.gearTypes, (accumulator, val, key) => { +const sortOrder = reduce(content.gearTypes, (accumulator, val, key) => { accumulator[val] = key; return accumulator; }, {}); @@ -21,9 +21,7 @@ let sortOrder = reduce(content.gearTypes, (accumulator, val, key) => { * @param String path */ function pathExistsInArray (array, path) { - return array.findIndex(item => { - return item.path === path; - }); + return array.findIndex(item => item.path === path); } function checkForNullEntries (array) { @@ -36,17 +34,15 @@ export function checkPinnedAreasForNullEntries (user) { } export function selectGearToPin (user) { - let changes = []; + const changes = []; - each(content.gearTypes, (type) => { - let found = lodashFind(content.gear.tree[type][user.stats.class], (item) => { - return !user.items.gear.owned[item.key]; - }); + each(content.gearTypes, type => { + const found = lodashFind(content.gear.tree[type][user.stats.class], item => !user.items.gear.owned[item.key]); if (found) changes.push(found); }); - return sortBy(changes, (change) => sortOrder[change.type]); + return sortBy(changes, change => sortOrder[change.type]); } export function addPinnedGear (user, type, path) { @@ -61,10 +57,10 @@ export function addPinnedGear (user, type, path) { } export function addPinnedGearByClass (user) { - let newPinnedItems = selectGearToPin(user); + const newPinnedItems = selectGearToPin(user); - for (let item of newPinnedItems) { - let itemInfo = getItemInfo(user, 'marketGear', item); + for (const item of newPinnedItems) { + const itemInfo = getItemInfo(user, 'marketGear', item); addPinnedGear(user, itemInfo.pinType, itemInfo.path); } @@ -82,10 +78,10 @@ export function removeItemByPath (user, path) { } export function removePinnedGearByClass (user) { - let currentPinnedItems = selectGearToPin(user); + const currentPinnedItems = selectGearToPin(user); - for (let item of currentPinnedItems) { - let itemInfo = getItemInfo(user, 'marketGear', item); + for (const item of currentPinnedItems) { + const itemInfo = getItemInfo(user, 'marketGear', item); removeItemByPath(user, itemInfo.path); } @@ -124,9 +120,9 @@ const PATHS_WITHOUT_ITEM = ['special.gems', 'special.rebirth_orb', 'special.fort /** * @returns {boolean} TRUE added the item / FALSE removed it */ -export function togglePinnedItem (user, {item, type, path}, req = {}) { +export function togglePinnedItem (user, { item, type, path }, req = {}) { let arrayToChange; - let officialPinnedItems = getOfficialPinnedItems(user); + const officialPinnedItems = getOfficialPinnedItems(user); if (!path) { // If path isn't passed it means an item was passed @@ -137,7 +133,7 @@ export function togglePinnedItem (user, {item, type, path}, req = {}) { if (!item && PATHS_WITHOUT_ITEM.indexOf(path) === -1) { // path not exists in our content structure - throw new BadRequest(i18n.t('wrongItemPath', {path}, req.language)); + throw new BadRequest(i18n.t('wrongItemPath', { path }, req.language)); } // check if item exists & valid to be pinned @@ -171,10 +167,9 @@ export function togglePinnedItem (user, {item, type, path}, req = {}) { if (foundIndex >= 0) { arrayToChange.splice(foundIndex, 1); return isOfficialPinned; - } else { - arrayToChange.push({path, type}); - return !isOfficialPinned; } + arrayToChange.push({ path, type }); + return !isOfficialPinned; } export { default as isPinned } from '../libs/isPinned'; diff --git a/website/common/script/ops/readCard.js b/website/common/script/ops/readCard.js index 8602016c1c..c3be9a1d7e 100644 --- a/website/common/script/ops/readCard.js +++ b/website/common/script/ops/readCard.js @@ -11,10 +11,10 @@ import content from '../content/index'; function markNotificationAsRead (user, cardType) { const indexToRemove = user.notifications.findIndex(notification => { if ( - notification && - notification.type === 'CARD_RECEIVED' && - notification.data && - notification.data.card === cardType + notification + && notification.type === 'CARD_RECEIVED' + && notification.data + && notification.data.card === cardType ) return true; }); @@ -23,7 +23,7 @@ function markNotificationAsRead (user, cardType) { export default function readCard (user, req = {}) { - let cardType = get(req.params, 'cardType'); + const cardType = get(req.params, 'cardType'); if (!cardType) { throw new BadRequest(i18n.t('cardTypeRequired', req.language)); @@ -40,6 +40,6 @@ export default function readCard (user, req = {}) { return [ { specialItems: user.items.special, cardReceived: user.flags.cardReceived }, - i18n.t('readCard', {cardType}, req.language), + i18n.t('readCard', { cardType }, req.language), ]; } diff --git a/website/common/script/ops/rebirth.js b/website/common/script/ops/rebirth.js index d56904634a..09cd63db5a 100644 --- a/website/common/script/ops/rebirth.js +++ b/website/common/script/ops/rebirth.js @@ -1,5 +1,5 @@ -import i18n from '../i18n'; import each from 'lodash/each'; +import i18n from '../i18n'; import { capByLevel } from '../statHelpers'; import { MAX_LEVEL } from '../constants'; import { @@ -18,7 +18,7 @@ export default function rebirth (user, tasks = [], req = {}, analytics) { throw new NotAuthorized(i18n.t('notEnoughGems', req.language)); } - let analyticsData = { + const analyticsData = { uuid: user._id, category: 'behavior', }; @@ -37,9 +37,9 @@ export default function rebirth (user, tasks = [], req = {}, analytics) { analytics.track('Rebirth', analyticsData); } - let lvl = capByLevel(user.stats.lvl); + const lvl = capByLevel(user.stats.lvl); - each(tasks, function resetTasks (task) { + each(tasks, task => { if (!task.challenge || !task.challenge.id || task.challenge.broken) { if (task.type !== 'reward') { task.value = 0; @@ -56,7 +56,7 @@ export default function rebirth (user, tasks = [], req = {}, analytics) { removePinnedGearByClass(user); - let stats = user.stats; + const { stats } = user; stats.buffs = {}; stats.hp = 50; stats.lvl = 1; @@ -64,7 +64,7 @@ export default function rebirth (user, tasks = [], req = {}, analytics) { user.preferences.automaticAllocation = false; - each(USERSTATSLIST, function resetStats (value) { + each(USERSTATSLIST, value => { stats[value] = 0; }); @@ -86,7 +86,7 @@ export default function rebirth (user, tasks = [], req = {}, analytics) { }); } - let flags = user.flags; + const { flags } = user; flags.itemsEnabled = false; flags.dropsEnabled = false; flags.classSelected = false; @@ -110,7 +110,7 @@ export default function rebirth (user, tasks = [], req = {}, analytics) { user.stats.buffs = {}; return [ - {user, tasks}, + { user, tasks }, i18n.t('rebirthComplete'), ]; } diff --git a/website/common/script/ops/releaseBoth.js b/website/common/script/ops/releaseBoth.js index 1948f93033..669c330fcd 100644 --- a/website/common/script/ops/releaseBoth.js +++ b/website/common/script/ops/releaseBoth.js @@ -1,11 +1,11 @@ +import pick from 'lodash/pick'; import content from '../content/index'; -import {beastMasterProgress, mountMasterProgress} from '../count'; +import { beastMasterProgress, mountMasterProgress } from '../count'; import i18n from '../i18n'; import { NotAuthorized, } from '../libs/errors'; import splitWhitespace from '../libs/splitWhitespace'; -import pick from 'lodash/pick'; export default function releaseBoth (user, req = {}) { let animal; @@ -37,13 +37,13 @@ export default function releaseBoth (user, req = {}) { // user.balance -= 1.5; // } - let mountInfo = content.mountInfo[user.items.currentMount]; + const mountInfo = content.mountInfo[user.items.currentMount]; if (mountInfo && mountInfo.type === 'drop') { user.items.currentMount = ''; } - let petInfo = content.petInfo[user.items.currentPet]; + const petInfo = content.petInfo[user.items.currentPet]; if (petInfo && petInfo.type === 'drop') { user.items.currentPet = ''; diff --git a/website/common/script/ops/releaseMounts.js b/website/common/script/ops/releaseMounts.js index f3733e3e05..06247e6d9e 100644 --- a/website/common/script/ops/releaseMounts.js +++ b/website/common/script/ops/releaseMounts.js @@ -1,5 +1,5 @@ import content from '../content/index'; -import {mountMasterProgress} from '../count'; +import { mountMasterProgress } from '../count'; import i18n from '../i18n'; import { NotAuthorized, @@ -18,13 +18,13 @@ export default function releaseMounts (user, req = {}, analytics) { let giveMountMasterAchievement = true; - let mountInfo = content.mountInfo[user.items.currentMount]; + const mountInfo = content.mountInfo[user.items.currentMount]; if (mountInfo && mountInfo.type === 'drop') { user.items.currentMount = ''; } - for (let mount in content.pets) { + for (const mount in content.pets) { if (user.items.mounts[mount] === null || user.items.mounts[mount] === undefined) { giveMountMasterAchievement = false; } diff --git a/website/common/script/ops/releasePets.js b/website/common/script/ops/releasePets.js index 7d2ea051d8..436f205d3a 100644 --- a/website/common/script/ops/releasePets.js +++ b/website/common/script/ops/releasePets.js @@ -1,5 +1,5 @@ import content from '../content/index'; -import {beastMasterProgress} from '../count'; +import { beastMasterProgress } from '../count'; import i18n from '../i18n'; import { NotAuthorized, @@ -18,13 +18,13 @@ export default function releasePets (user, req = {}, analytics) { let giveBeastMasterAchievement = true; - let petInfo = content.petInfo[user.items.currentPet]; + const petInfo = content.petInfo[user.items.currentPet]; if (petInfo && petInfo.type === 'drop') { user.items.currentPet = ''; } - for (let pet in content.pets) { + for (const pet in content.pets) { if (!user.items.pets[pet]) { giveBeastMasterAchievement = false; } diff --git a/website/common/script/ops/reroll.js b/website/common/script/ops/reroll.js index 35add88e42..c5d8d2c82d 100644 --- a/website/common/script/ops/reroll.js +++ b/website/common/script/ops/reroll.js @@ -1,5 +1,5 @@ -import i18n from '../i18n'; import each from 'lodash/each'; +import i18n from '../i18n'; import { NotAuthorized, } from '../libs/errors'; @@ -12,7 +12,7 @@ export default function reroll (user, tasks = [], req = {}, analytics) { user.balance--; user.stats.hp = 50; - each(tasks, function resetTaskValues (task) { + each(tasks, task => { if (!task.challenge || !task.challenge.id || task.challenge.broken) { if (task.type !== 'reward') { task.value = 0; @@ -31,7 +31,7 @@ export default function reroll (user, tasks = [], req = {}, analytics) { } return [ - {user, tasks}, + { user, tasks }, i18n.t('fortifyComplete'), ]; } diff --git a/website/common/script/ops/reset.js b/website/common/script/ops/reset.js index 23f466a528..1964afb0fd 100644 --- a/website/common/script/ops/reset.js +++ b/website/common/script/ops/reset.js @@ -7,14 +7,14 @@ export default function reset (user, tasks = []) { user.stats.gp = 0; user.stats.exp = 0; - let tasksToRemove = []; + const tasksToRemove = []; tasks.forEach(task => { - let isNotChallengeTask = !task.challenge || !task.challenge.id || task.challenge.broken; - let isNotGroupTask = !task.group || !task.group.id || task.group.broken; + const isNotChallengeTask = !task.challenge || !task.challenge.id || task.challenge.broken; + const isNotGroupTask = !task.group || !task.group.id || task.group.broken; if (isNotChallengeTask && isNotGroupTask) { tasksToRemove.push(task._id); - let i = user.tasksOrder[`${task.type}s`].indexOf(task._id); + const i = user.tasksOrder[`${task.type}s`].indexOf(task._id); if (i !== -1) user.tasksOrder[`${task.type}s`].splice(i, 1); } }); @@ -24,7 +24,7 @@ export default function reset (user, tasks = []) { user.preferences.automaticAllocation = false; return [ - {user, tasksToRemove}, + { user, tasksToRemove }, i18n.t('resetComplete'), ]; } diff --git a/website/common/script/ops/revive.js b/website/common/script/ops/revive.js index d87b71b77b..5436520d12 100644 --- a/website/common/script/ops/revive.js +++ b/website/common/script/ops/revive.js @@ -1,8 +1,8 @@ -import content from '../content/index'; -import i18n from '../i18n'; import merge from 'lodash/merge'; import reduce from 'lodash/reduce'; import each from 'lodash/each'; +import i18n from '../i18n'; +import content from '../content/index'; import { NotAuthorized, } from '../libs/errors'; @@ -27,7 +27,7 @@ export default function revive (user, req = {}, analytics) { user.stats.lvl--; } - let lostStat = randomVal(reduce(['str', 'con', 'per', 'int'], function findRandomStat (m, k) { + const lostStat = randomVal(reduce(['str', 'con', 'per', 'int'], (m, k) => { if (user.stats[k]) { m[k] = k; } @@ -40,7 +40,7 @@ export default function revive (user, req = {}, analytics) { user.stats[lostStat]--; } - let base = user.items.gear.owned; + const base = user.items.gear.owned; let gearOwned; if (typeof base.toObject === 'function') { @@ -49,24 +49,24 @@ export default function revive (user, req = {}, analytics) { gearOwned = user.items.gear.owned; } - let losableItems = {}; - let userClass = user.stats.class; + const losableItems = {}; + const userClass = user.stats.class; - each(gearOwned, function findLosableItems (value, key) { + each(gearOwned, (value, key) => { let itm; if (value) { itm = content.gear.flat[key]; if (itm) { - let itemHasValueOrWarrior0 = itm.value > 0 || key === 'weapon_warrior_0'; + const itemHasValueOrWarrior0 = itm.value > 0 || key === 'weapon_warrior_0'; - let itemClassEqualsUserClass = itm.klass === userClass; + const itemClassEqualsUserClass = itm.klass === userClass; - let itemClassSpecial = itm.klass === 'special'; - let itemNotSpecialOrUserClassIsSpecial = !itm.specialClass || itm.specialClass === userClass; - let itemIsSpecial = itemNotSpecialOrUserClassIsSpecial && itemClassSpecial; + const itemClassSpecial = itm.klass === 'special'; + const itemNotSpecialOrUserClassIsSpecial = !itm.specialClass || itm.specialClass === userClass; + const itemIsSpecial = itemNotSpecialOrUserClassIsSpecial && itemClassSpecial; - let itemIsArmoire = itm.klass === 'armoire'; + const itemIsArmoire = itm.klass === 'armoire'; if (itemHasValueOrWarrior0 && (itemClassEqualsUserClass || itemIsSpecial || itemIsArmoire)) { losableItems[key] = key; @@ -76,12 +76,12 @@ export default function revive (user, req = {}, analytics) { } }); - let lostItem = randomVal(losableItems, { + const lostItem = randomVal(losableItems, { predictableRandom: predictableRandom(user), }); let message = ''; - let item = content.gear.flat[lostItem]; + const item = content.gear.flat[lostItem]; if (item) { removePinnedGearByClass(user); @@ -91,18 +91,18 @@ export default function revive (user, req = {}, analytics) { addPinnedGearByClass(user); - let itemInfo = getItemInfo(user, 'marketGear', item); + const itemInfo = getItemInfo(user, 'marketGear', item); addPinnedGear(user, itemInfo.pinType, itemInfo.path); if (user.items.gear.equipped[item.type] === lostItem) { - user.items.gear.equipped[item.type] = `${item.type}_base_0`; + user.items.gear.equipped[item.type] = `${item.type}_base_0`; } if (user.items.gear.costume[item.type] === lostItem) { user.items.gear.costume[item.type] = `${item.type}_base_0`; } - message = i18n.t('messageLostItem', { itemText: item.text(req.language)}, req.language); + message = i18n.t('messageLostItem', { itemText: item.text(req.language) }, req.language); } if (analytics) { diff --git a/website/common/script/ops/scoreTask.js b/website/common/script/ops/scoreTask.js index 6b6d85c889..3a7da608db 100644 --- a/website/common/script/ops/scoreTask.js +++ b/website/common/script/ops/scoreTask.js @@ -17,18 +17,17 @@ const CLOSE_ENOUGH = 0.00001; function _getTaskValue (taskValue) { if (taskValue < MIN_TASK_VALUE) { return MIN_TASK_VALUE; - } else if (taskValue > MAX_TASK_VALUE) { + } if (taskValue > MAX_TASK_VALUE) { return MAX_TASK_VALUE; - } else { - return taskValue; } + return taskValue; } // Calculates the next task.value based on direction // Uses a capped inverse log y=.95^x, y>= -5 function _calculateDelta (task, direction, cron) { // Min/max on task redness - let currVal = _getTaskValue(task.value); + const currVal = _getTaskValue(task.value); let nextDelta = Math.pow(0.9747, currVal) * (direction === 'down' ? -1 : 1); // Checklists @@ -52,15 +51,15 @@ function _calculateDelta (task, direction, cron) { // First, calculate the value using the normal way for our first guess although // it will be a bit off function _calculateReverseDelta (task, direction) { - let currVal = _getTaskValue(task.value); + const currVal = _getTaskValue(task.value); let testVal = currVal + Math.pow(0.9747, currVal) * (direction === 'down' ? -1 : 1); // Now keep moving closer to the original value until we get "close enough" // Check how close we are to the original value by computing the delta off our guess // and looking at the difference between that and our current value. while (true) { // eslint-disable-line no-constant-condition - let calc = testVal + Math.pow(0.9747, testVal); - let diff = currVal - calc; + const calc = testVal + Math.pow(0.9747, testVal); + const diff = currVal - calc; if (Math.abs(diff) < CLOSE_ENOUGH) break; @@ -101,30 +100,30 @@ function _subtractPoints (user, task, stats, delta) { let conBonus = 1 - statsComputed(user).con / 250; if (conBonus < 0.1) conBonus = 0.1; - let hpMod = delta * conBonus * task.priority * 2; // constant 2 multiplier for better results + const hpMod = delta * conBonus * task.priority * 2; // constant 2 multiplier for better results stats.hp += Math.round(hpMod * 10) / 10; // round to 1dp return stats.hp; } function _addPoints (user, task, stats, direction, delta) { - let _crit = user._tmp.crit || 1; + const _crit = user._tmp.crit || 1; // Exp Modifier // ===== Intelligence ===== // TODO Increases Experience gain by .2% per point. - let intBonus = 1 + statsComputed(user).int * 0.025; + const intBonus = 1 + statsComputed(user).int * 0.025; stats.exp += Math.round(delta * intBonus * task.priority * _crit * 6); // GP modifier // ===== PERCEPTION ===== // TODO Increases Gold gained from tasks by .3% per point. - let perBonus = 1 + statsComputed(user).per * 0.02; - let gpMod = delta * task.priority * _crit * perBonus; + const perBonus = 1 + statsComputed(user).per * 0.02; + const gpMod = delta * task.priority * _crit * perBonus; if (task.streak) { - let currStreak = direction === 'down' ? task.streak - 1 : task.streak; - let streakBonus = currStreak / 100 + 1; // eg, 1-day streak is 1.01, 2-day is 1.02, etc - let afterStreak = gpMod * streakBonus; + const currStreak = direction === 'down' ? task.streak - 1 : task.streak; + const streakBonus = currStreak / 100 + 1; // eg, 1-day streak is 1.01, 2-day is 1.02, etc + const afterStreak = gpMod * streakBonus; if (currStreak > 0 && gpMod > 0) { user._tmp.streakBonus = afterStreak - gpMod; // keep this on-hand for later, so we can notify streak-bonus } @@ -140,14 +139,14 @@ function _changeTaskValue (user, task, direction, times, cron) { // ===== CRITICAL HITS ===== // allow critical hit only when checking off a task, not when unchecking it: - let _crit = direction === 'up' ? crit.crit(user) : 1; + const _crit = direction === 'up' ? crit.crit(user) : 1; // if there was a crit, alert the user via notification if (_crit > 1) user._tmp.crit = _crit; // If multiple days have passed, multiply times days missed timesLodash(times, () => { // Each iteration calculate the nextDelta, which is then accumulated in the total delta. - let nextDelta = !cron && direction === 'down' ? _calculateReverseDelta(task, direction) : _calculateDelta(task, direction, cron); + const nextDelta = !cron && direction === 'down' ? _calculateReverseDelta(task, direction) : _calculateDelta(task, direction, cron); if (task.type !== 'reward') { if (user.preferences.automaticAllocation === true && user.preferences.allocationMode === 'taskbased' && !(task.type === 'todo' && direction === 'down')) { @@ -156,7 +155,7 @@ function _changeTaskValue (user, task, direction, times, cron) { if (direction === 'up') { // Make progress on quest based on STR user.party.quest.progress.up = user.party.quest.progress.up || 0; - let prevProgress = user.party.quest.progress.up; + const prevProgress = user.party.quest.progress.up; if (task.type === 'todo' || task.type === 'daily') { user.party.quest.progress.up += nextDelta * _crit * (1 + statsComputed(user).str / 200); @@ -185,9 +184,11 @@ function _updateCounter (task, direction, times) { } export default function scoreTask (options = {}, req = {}) { - let {user, task, direction, times = 1, cron = false} = options; + const { + user, task, direction, times = 1, cron = false, + } = options; let delta = 0; - let stats = { + const stats = { gp: user.stats.gp, hp: user.stats.hp, exp: user.stats.exp, @@ -215,8 +216,8 @@ export default function scoreTask (options = {}, req = {}) { // Save history entry for habit task.history = task.history || []; - const timezoneOffset = user.preferences.timezoneOffset; - const dayStart = user.preferences.dayStart; + const { timezoneOffset } = user.preferences; + const { dayStart } = user.preferences; const historyLength = task.history.length; const lastHistoryEntry = task.history[historyLength - 1]; @@ -229,8 +230,8 @@ export default function scoreTask (options = {}, req = {}) { } if ( - lastHistoryEntryDate && - moment().zone(timezoneOffset).isSame(lastHistoryEntryDate, 'day') + lastHistoryEntryDate + && moment().zone(timezoneOffset).isSame(lastHistoryEntryDate, 'day') ) { lastHistoryEntry.value = task.value; lastHistoryEntry.date = Number(new Date()); @@ -278,7 +279,7 @@ export default function scoreTask (options = {}, req = {}) { // Save history entry for daily task.history = task.history || []; - let historyEntry = { + const historyEntry = { date: Number(new Date()), value: task.value, }; @@ -312,7 +313,7 @@ export default function scoreTask (options = {}, req = {}) { _addPoints(user, task, stats, direction, delta); // MP++ per checklist item in ToDo, bonus per CLI - let multiplier = max([reduce(task.checklist, (m, i) => m + (i.completed ? 1 : 0), 1), 1]); + const multiplier = max([reduce(task.checklist, (m, i) => m + (i.completed ? 1 : 0), 1), 1]); _gainMP(user, max([multiplier, 0.01 * statsComputed(user).maxMP * multiplier]) * (direction === 'down' ? -1 : 1)); } } else if (task.type === 'reward') { diff --git a/website/common/script/ops/sell.js b/website/common/script/ops/sell.js index 2eb5aa9b99..23f14cd851 100644 --- a/website/common/script/ops/sell.js +++ b/website/common/script/ops/sell.js @@ -1,7 +1,7 @@ -import content from '../content/index'; -import i18n from '../i18n'; import get from 'lodash/get'; import pick from 'lodash/pick'; +import content from '../content/index'; +import i18n from '../i18n'; import splitWhitespace from '../libs/splitWhitespace'; import { NotFound, @@ -13,9 +13,9 @@ import { const ACCEPTEDTYPES = ['eggs', 'hatchingPotions', 'food']; export default function sell (user, req = {}) { - let key = get(req.params, 'key'); - let type = get(req.params, 'type'); - let amount = get(req.query, 'amount', 1); + const key = get(req.params, 'key'); + const type = get(req.params, 'type'); + const amount = get(req.query, 'amount', 1); if (amount < 0) { throw new BadRequest(i18n.t('positiveAmountRequired', req.language)); @@ -30,17 +30,17 @@ export default function sell (user, req = {}) { } if (ACCEPTEDTYPES.indexOf(type) === -1) { - throw new NotAuthorized(i18n.t('typeNotSellable', {acceptedTypes: ACCEPTEDTYPES.join(', ')}, req.language)); + throw new NotAuthorized(i18n.t('typeNotSellable', { acceptedTypes: ACCEPTEDTYPES.join(', ') }, req.language)); } if (!user.items[type][key]) { - throw new NotFound(i18n.t('userItemsKeyNotFound', {type}, req.language)); + throw new NotFound(i18n.t('userItemsKeyNotFound', { type }, req.language)); } - let currentAmount = user.items[type][key]; + const currentAmount = user.items[type][key]; if (amount > currentAmount) { - throw new NotFound(i18n.t('userItemsNotEnough', {type}, req.language)); + throw new NotFound(i18n.t('userItemsNotEnough', { type }, req.language)); } if (type === 'food' && key === 'Saddle') { diff --git a/website/common/script/ops/sortTag.js b/website/common/script/ops/sortTag.js index 65d7dbf530..11dffeae4c 100644 --- a/website/common/script/ops/sortTag.js +++ b/website/common/script/ops/sortTag.js @@ -1,14 +1,14 @@ -import { BadRequest } from '../libs/errors'; import get from 'lodash/get'; +import { BadRequest } from '../libs/errors'; // TODO used only in client, move there? export default function sortTag (user, req = {}) { - let to = get(req, 'query.to'); - let fromParam = get(req, 'query.from'); + const to = get(req, 'query.to'); + const fromParam = get(req, 'query.from'); - let invalidTo = !to && to !== 0; - let invalidFrom = !fromParam && fromParam !== 0; + const invalidTo = !to && to !== 0; + const invalidFrom = !fromParam && fromParam !== 0; if (invalidTo || invalidFrom) { throw new BadRequest('?to=__&from=__ are required'); diff --git a/website/common/script/ops/sortTask.js b/website/common/script/ops/sortTask.js index 996976dd26..0e461f5a88 100644 --- a/website/common/script/ops/sortTask.js +++ b/website/common/script/ops/sortTask.js @@ -1,23 +1,21 @@ +import get from 'lodash/get'; +import findIndex from 'lodash/findIndex'; import i18n from '../i18n'; import preenTodos from '../libs/preenTodos'; import { NotFound, BadRequest, } from '../libs/errors'; -import get from 'lodash/get'; -import findIndex from 'lodash/findIndex'; // TODO used only in client, move there? export default function sortTask (user, req = {}) { - let id = get(req, 'params.id'); + const id = get(req, 'params.id'); let to = get(req, 'query.to'); let fromParam = get(req, 'query.from'); - let taskType = get(req, 'params.taskType'); + const taskType = get(req, 'params.taskType'); - let index = findIndex(user[`${taskType}s`], function findById (task) { - return task._id === id; - }); + const index = findIndex(user[`${taskType}s`], task => task._id === id); if (index === -1) { throw new NotFound(i18n.t('messageTaskNotFound', req.language)); @@ -26,10 +24,10 @@ export default function sortTask (user, req = {}) { throw new BadRequest('?to=__&from=__ are required'); } - let tasks = user[`${taskType}s`]; + const tasks = user[`${taskType}s`]; if (taskType === 'todo') { - let preenedTasks = preenTodos(tasks); + const preenedTasks = preenTodos(tasks); if (to !== -1) { to = tasks.indexOf(preenedTasks[to]); @@ -38,7 +36,7 @@ export default function sortTask (user, req = {}) { fromParam = tasks.indexOf(preenedTasks[fromParam]); } - let movedTask = tasks.splice(fromParam, 1)[0]; + const movedTask = tasks.splice(fromParam, 1)[0]; if (to === -1) { tasks.push(movedTask); diff --git a/website/common/script/ops/stats/allocate.js b/website/common/script/ops/stats/allocate.js index e1350571e6..28b76695f3 100644 --- a/website/common/script/ops/stats/allocate.js +++ b/website/common/script/ops/stats/allocate.js @@ -11,10 +11,10 @@ import errorMessage from '../../libs/errorMessage'; import hasClass from '../../libs/hasClass'; export default function allocate (user, req = {}) { - let stat = get(req, 'query.stat', 'str'); + const stat = get(req, 'query.stat', 'str'); if (ATTRIBUTES.indexOf(stat) === -1) { - throw new BadRequest(errorMessage('invalidAttribute', {attr: stat})); + throw new BadRequest(errorMessage('invalidAttribute', { attr: stat })); } if (!hasClass(user)) { diff --git a/website/common/script/ops/stats/allocateBulk.js b/website/common/script/ops/stats/allocateBulk.js index f5ae89949d..e532525d89 100644 --- a/website/common/script/ops/stats/allocateBulk.js +++ b/website/common/script/ops/stats/allocateBulk.js @@ -15,11 +15,9 @@ export default function allocateBulk (user, req = {}) { if (!stats) throw new BadRequest(errorMessage('statsObjectRequired')); const statKeys = Object.keys(stats); - const invalidStats = statKeys.filter(statKey => { - return ATTRIBUTES.indexOf(statKey) === -1; - }); + const invalidStats = statKeys.filter(statKey => ATTRIBUTES.indexOf(statKey) === -1); if (invalidStats.length > 0) { - throw new BadRequest(errorMessage('invalidAttribute', {attr: invalidStats.join(',')})); + throw new BadRequest(errorMessage('invalidAttribute', { attr: invalidStats.join(',') })); } if (!hasClass(user)) { @@ -31,14 +29,12 @@ export default function allocateBulk (user, req = {}) { } const newStatValues = Object.values(stats); - const totalPointsToAllocate = newStatValues.reduce((sum, value) => { - return sum + value; - }, 0); + const totalPointsToAllocate = newStatValues.reduce((sum, value) => sum + value, 0); if (user.stats.points < totalPointsToAllocate) { throw new NotAuthorized(i18n.t('notEnoughAttrPoints', req.language)); } - for (let [stat, value] of Object.entries(stats)) { + for (const [stat, value] of Object.entries(stats)) { user.stats[stat] += value; user.stats.points -= value; if (stat === 'int') user.stats.mp += value; diff --git a/website/common/script/ops/unlock.js b/website/common/script/ops/unlock.js index 891eeb946c..b72fab6fbb 100644 --- a/website/common/script/ops/unlock.js +++ b/website/common/script/ops/unlock.js @@ -1,8 +1,8 @@ -import i18n from '../i18n'; import get from 'lodash/get'; import each from 'lodash/each'; import pick from 'lodash/pick'; import setWith from 'lodash/setWith'; +import i18n from '../i18n'; import splitWhitespace from '../libs/splitWhitespace'; import { NotAuthorized, @@ -16,14 +16,14 @@ import content from '../content/index'; // If item is already purchased -> equip it // Otherwise unlock it export default function unlock (user, req = {}, analytics) { - let path = get(req.query, 'path'); + const path = get(req.query, 'path'); if (!path) { throw new BadRequest(i18n.t('pathRequired', req.language)); } - let isFullSet = path.indexOf(',') !== -1; - let isBackground = path.indexOf('background.') !== -1; + const isFullSet = path.indexOf(',') !== -1; + const isBackground = path.indexOf('background.') !== -1; let cost; if (isBackground && isFullSet) { @@ -69,11 +69,11 @@ export default function unlock (user, req = {}, analytics) { } if (isFullSet) { - each(setPaths, function markItemsAsPurchased (pathPart) { + each(setPaths, pathPart => { if (path.indexOf('gear.') !== -1) { // Using Object so path[1] won't create an array but an object {path: {1: value}} setWith(user, pathPart, true, Object); - let itemName = pathPart.split('.').pop(); + const itemName = pathPart.split('.').pop(); removeItemByPath(user, `gear.flat.${itemName}`); if (user.markModified && path.indexOf('gear.owned') !== -1) user.markModified('items.gear.owned'); } @@ -82,9 +82,9 @@ export default function unlock (user, req = {}, analytics) { setWith(user, `purchased.${pathPart}`, true, Object); }); } else { - let split = path.split('.'); + const split = path.split('.'); let value = split.pop(); - let key = split.join('.'); + const key = split.join('.'); if (alreadyOwns) { // eslint-disable-line no-lonely-if if (key === 'background' && value === user.preferences.background) { @@ -104,8 +104,8 @@ export default function unlock (user, req = {}, analytics) { // @TODO: Test and check test coverage if (isBackground) { - let backgroundContent = content.backgroundsFlat[value]; - let itemInfo = getItemInfo(user, 'background', backgroundContent); + const backgroundContent = content.backgroundsFlat[value]; + const itemInfo = getItemInfo(user, 'background', backgroundContent); removeItemByPath(user, itemInfo.path); } } @@ -131,7 +131,7 @@ export default function unlock (user, req = {}, analytics) { } } - let response = [ + const response = [ pick(user, splitWhitespace('purchased preferences items')), ]; diff --git a/website/common/script/ops/updateTag.js b/website/common/script/ops/updateTag.js index ca4264d130..a939e24cc2 100644 --- a/website/common/script/ops/updateTag.js +++ b/website/common/script/ops/updateTag.js @@ -1,14 +1,14 @@ -import i18n from '../i18n'; import get from 'lodash/get'; import findIndex from 'lodash/findIndex'; +import i18n from '../i18n'; import { NotFound } from '../libs/errors'; // TODO used only in client, move there? export default function updateTag (user, req = {}) { - let tid = get(req, 'params.id'); + const tid = get(req, 'params.id'); - let index = findIndex(user.tags, { + const index = findIndex(user.tags, { id: tid, }); diff --git a/website/common/script/ops/updateTask.js b/website/common/script/ops/updateTask.js index e504184f45..dcca31c529 100644 --- a/website/common/script/ops/updateTask.js +++ b/website/common/script/ops/updateTask.js @@ -3,7 +3,7 @@ import omit from 'lodash/omit'; // From server pass task.toObject() not the task document directly export default function updateTask (task, req = {}) { - let body = req.body || {}; + const body = req.body || {}; // If reminders are updated -> replace the original ones if (body.reminders) { diff --git a/website/common/script/statHelpers.js b/website/common/script/statHelpers.js index b678f08ef1..81e4cc2fc7 100644 --- a/website/common/script/statHelpers.js +++ b/website/common/script/statHelpers.js @@ -11,9 +11,8 @@ import { export function capByLevel (lvl) { if (lvl > MAX_LEVEL) { return MAX_LEVEL; - } else { - return lvl; } + return lvl; } /* @@ -25,7 +24,7 @@ export function capByLevel (lvl) { export function toNextLevel (lvl) { if (lvl < 5) { return 25 * lvl; - } else if (lvl === 5) { + } if (lvl === 5) { return 150; } return Math.round((Math.pow(lvl, 2) * 0.25 + 10 * lvl + 139.75) / 10) * 10; diff --git a/website/server/.eslintrc b/website/server/.eslintrc index 7b3c05adf0..b62d7f2160 100644 --- a/website/server/.eslintrc +++ b/website/server/.eslintrc @@ -1,6 +1,5 @@ { "extends": [ - "habitrpg/server", - "habitrpg/esnext" + "habitrpg/lib/node", ] } \ No newline at end of file diff --git a/website/server/api-doc.js b/website/server/api-doc.js index 14d136ef30..83b417afed 100644 --- a/website/server/api-doc.js +++ b/website/server/api-doc.js @@ -1,4 +1,3 @@ -'use strict'; // This file defines some globals for use in the API Doc comments diff --git a/website/server/controllers/api-v3/auth.js b/website/server/controllers/api-v3/auth.js index 782e52c5ba..588a6b7273 100644 --- a/website/server/controllers/api-v3/auth.js +++ b/website/server/controllers/api-v3/auth.js @@ -12,7 +12,7 @@ import { } from '../../libs/errors'; import * as passwordUtils from '../../libs/password'; import { sendTxn as sendTxnEmail } from '../../libs/email'; -import { validatePasswordResetCodeAndFindUser, convertToBcrypt} from '../../libs/password'; +import { validatePasswordResetCodeAndFindUser, convertToBcrypt } from '../../libs/password'; import { encrypt } from '../../libs/encryption'; import { loginRes, @@ -20,12 +20,12 @@ import { loginSocial, registerLocal, } from '../../libs/auth'; -import {verifyUsername} from '../../libs/user/validation'; +import { verifyUsername } from '../../libs/user/validation'; const BASE_URL = nconf.get('BASE_URL'); const TECH_ASSISTANCE_EMAIL = nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL'); -let api = {}; +const api = {}; /** * @api {post} /api/v3/user/auth/local/register Register @@ -79,24 +79,24 @@ api.loginLocal = { errorMessage: res.t('missingPassword'), }, }); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; req.sanitizeBody('username').trim(); req.sanitizeBody('password').trim(); let login; - let username = req.body.username; - let password = req.body.password; + const { username } = req.body; + const { password } = req.body; if (validator.isEmail(String(username))) { - login = {'auth.local.email': username.toLowerCase()}; // Emails are stored lowercase + login = { 'auth.local.email': username.toLowerCase() }; // Emails are stored lowercase } else { - login = {'auth.local.username': username}; + login = { 'auth.local.username': username }; } // load the entire user because we may have to save it to convert the password to bcrypt - let user = await User.findOne(login).exec(); + const user = await User.findOne(login).exec(); // if user is using social login, then user will not have a hashed_password stored if (!user || !user.auth.local.hashed_password) throw new NotAuthorized(res.t('invalidLoginCredentialsLong')); @@ -150,17 +150,17 @@ api.loginSocial = { * @apiParam (Body) {String} username The new username * @apiSuccess {String} data.username The new username - **/ + * */ api.updateUsername = { method: 'PUT', middlewares: [authWithHeaders()], url: '/user/auth/update-username', async handler (req, res) { - const user = res.locals.user; + const { user } = res.locals; req.checkBody({ username: { - notEmpty: {errorMessage: res.t('missingUsername')}, + notEmpty: { errorMessage: res.t('missingUsername') }, }, }); @@ -172,13 +172,13 @@ api.updateUsername = { const issues = verifyUsername(newUsername, res); if (issues.length > 0) throw new BadRequest(issues.join(' ')); - const password = req.body.password; + const { password } = req.body; if (password !== undefined) { - let isValidPassword = await passwordUtils.compare(user, password); + const isValidPassword = await passwordUtils.compare(user, password); if (!isValidPassword) throw new NotAuthorized(res.t('wrongPassword')); } - const existingUser = await User.findOne({ 'auth.local.lowerCaseUsername': newUsername.toLowerCase() }, {auth: 1}).exec(); + const existingUser = await User.findOne({ 'auth.local.lowerCaseUsername': newUsername.toLowerCase() }, { auth: 1 }).exec(); if (existingUser !== undefined && existingUser !== null && existingUser._id !== user._id) { throw new BadRequest(res.t('usernameTaken')); } @@ -224,39 +224,39 @@ api.updateUsername = { * @apiParam (Body) {String} confirmPassword New password confirmation * * @apiSuccess {Object} data An empty object - **/ + * */ api.updatePassword = { method: 'PUT', middlewares: [authWithHeaders()], url: '/user/auth/update-password', async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; if (!user.auth.local.hashed_password) throw new BadRequest(res.t('userHasNoLocalRegistration')); req.checkBody({ password: { - notEmpty: {errorMessage: res.t('missingPassword')}, + notEmpty: { errorMessage: res.t('missingPassword') }, }, newPassword: { - notEmpty: {errorMessage: res.t('missingNewPassword')}, + notEmpty: { errorMessage: res.t('missingNewPassword') }, }, confirmPassword: { - notEmpty: {errorMessage: res.t('missingNewPassword')}, + notEmpty: { errorMessage: res.t('missingNewPassword') }, }, }); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) { throw validationErrors; } - let oldPassword = req.body.password; - let isValidPassword = await passwordUtils.compare(user, oldPassword); + const oldPassword = req.body.password; + const isValidPassword = await passwordUtils.compare(user, oldPassword); if (!isValidPassword) throw new NotAuthorized(res.t('wrongPassword')); - let newPassword = req.body.newPassword; + const { newPassword } = req.body; if (newPassword !== req.body.confirmPassword) throw new NotAuthorized(res.t('passwordConfirmationMatch')); // set new password and make sure it's using bcrypt for hashing @@ -276,7 +276,7 @@ api.updatePassword = { * @apiParam (Body) {String} email The email address of the user * * @apiSuccess {String} message The localized success message - **/ + * */ api.resetPassword = { method: 'POST', middlewares: [], @@ -284,14 +284,14 @@ api.resetPassword = { async handler (req, res) { req.checkBody({ email: { - notEmpty: {errorMessage: res.t('missingEmail')}, + notEmpty: { errorMessage: res.t('missingEmail') }, }, }); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let email = req.body.email.toLowerCase(); - let user = await User.findOne({ 'auth.local.email': email }).exec(); + const email = req.body.email.toLowerCase(); + const user = await User.findOne({ 'auth.local.email': email }).exec(); if (user) { // create an encrypted link to be used to reset the password @@ -299,12 +299,12 @@ api.resetPassword = { userId: user._id, expiresAt: moment().add({ hours: 24 }), })); - let link = `${BASE_URL}/static/user/auth/local/reset-password-set-new-one?code=${passwordResetCode}`; + const link = `${BASE_URL}/static/user/auth/local/reset-password-set-new-one?code=${passwordResetCode}`; user.auth.local.passwordResetCode = passwordResetCode; sendTxnEmail(user, 'reset-password', [ - {name: 'PASSWORD_RESET_LINK', content: link}, + { name: 'PASSWORD_RESET_LINK', content: link }, ]); await user.save(); @@ -330,23 +330,23 @@ api.updateEmail = { middlewares: [authWithHeaders()], url: '/user/auth/update-email', async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; if (!user.auth.local.email) throw new BadRequest(res.t('userHasNoLocalRegistration')); req.checkBody('newEmail', res.t('newEmailRequired')).notEmpty().isEmail(); req.checkBody('password', res.t('missingPassword')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let emailAlreadyInUse = await User.findOne({ + const emailAlreadyInUse = await User.findOne({ 'auth.local.email': req.body.newEmail.toLowerCase(), - }).select({_id: 1}).lean().exec(); + }).select({ _id: 1 }).lean().exec(); if (emailAlreadyInUse) throw new NotAuthorized(res.t('cannotFulfillReq', { techAssistanceEmail: TECH_ASSISTANCE_EMAIL })); - let password = req.body.password; - let isValidPassword = await passwordUtils.compare(user, password); + const { password } = req.body; + const isValidPassword = await passwordUtils.compare(user, password); if (!isValidPassword) throw new NotAuthorized(res.t('wrongPassword')); // if password is using old sha1 encryption, change it @@ -377,18 +377,18 @@ api.resetPasswordSetNewOne = { method: 'POST', url: '/user/auth/reset-password-set-new-one', async handler (req, res) { - let user = await validatePasswordResetCodeAndFindUser(req.body.code); - let isValidCode = Boolean(user); + const user = await validatePasswordResetCodeAndFindUser(req.body.code); + const isValidCode = Boolean(user); if (!isValidCode) throw new NotAuthorized(res.t('invalidPasswordResetCode')); req.checkBody('newPassword', res.t('missingNewPassword')).notEmpty(); req.checkBody('confirmPassword', res.t('missingNewPassword')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let newPassword = req.body.newPassword; - let confirmPassword = req.body.confirmPassword; + const { newPassword } = req.body; + const { confirmPassword } = req.body; if (newPassword !== confirmPassword) { throw new BadRequest(res.t('passwordConfirmationMatch')); @@ -416,17 +416,15 @@ api.deleteSocial = { url: '/user/auth/social/:network', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let network = req.params.network; - let isSupportedNetwork = common.constants.SUPPORTED_SOCIAL_NETWORKS.find(supportedNetwork => { - return supportedNetwork.key === network; - }); + const { user } = res.locals; + const { network } = req.params; + const isSupportedNetwork = common.constants.SUPPORTED_SOCIAL_NETWORKS.find(supportedNetwork => supportedNetwork.key === network); if (!isSupportedNetwork) throw new BadRequest(res.t('unsupportedNetwork')); if (!hasBackupAuth(user, network)) throw new NotAuthorized(res.t('cantDetachSocial')); - let unset = { + const unset = { [`auth.${network}`]: 1, }; - await User.update({_id: user._id}, {$unset: unset}).exec(); + await User.update({ _id: user._id }, { $unset: unset }).exec(); res.respond(200, {}); }, diff --git a/website/server/controllers/api-v3/challenges.js b/website/server/controllers/api-v3/challenges.js index 655bd4585b..aa0aa316bd 100644 --- a/website/server/controllers/api-v3/challenges.js +++ b/website/server/controllers/api-v3/challenges.js @@ -1,6 +1,6 @@ -import { authWithHeaders, authWithSession } from '../../middlewares/auth'; import _ from 'lodash'; import cloneDeep from 'lodash/cloneDeep'; +import { authWithHeaders, authWithSession } from '../../middlewares/auth'; import { model as Challenge } from '../../models/challenge'; import { model as Group, @@ -29,7 +29,7 @@ import { } from '../../libs/challenges'; import apiError from '../../libs/apiError'; -let api = {}; +const api = {}; /** * @apiDefine ChallengeLeader Challenge Leader @@ -186,19 +186,19 @@ api.createChallenge = { url: '/challenges', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkBody('group', apiError('groupIdRequired')).notEmpty(); const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - const {savedChal, group} = await createChallenge(user, req, res); + const { savedChal, group } = await createChallenge(user, req, res); - let response = savedChal.toJSON(); + const response = savedChal.toJSON(); response.leader = { // the leader is the authenticated user _id: user._id, - profile: {name: user.profile.name}, + profile: { name: user.profile.name }, }; response.group = getChallengeGroupResponse(group); @@ -235,18 +235,20 @@ api.joinChallenge = { url: '/challenges/:challengeId/join', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let challenge = await Challenge.findOne({ _id: req.params.challengeId }).exec(); + const challenge = await Challenge.findOne({ _id: req.params.challengeId }).exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); if (challenge.isMember(user)) throw new NotAuthorized(res.t('userAlreadyInChallenge')); - let group = await Group.getGroup({user, groupId: challenge.group, fields: basicGroupFields, optionalMembership: true}); + const group = await Group.getGroup({ + user, groupId: challenge.group, fields: basicGroupFields, optionalMembership: true, + }); if (!group || !challenge.canJoin(user, group)) throw new NotFound(res.t('challengeNotFound')); challenge.memberCount += 1; @@ -254,12 +256,12 @@ api.joinChallenge = { addUserJoinChallengeNotification(user); // Add all challenge's tasks to user's tasks and save the challenge - let results = await Promise.all([challenge.syncToUser(user), challenge.save()]); + const results = await Promise.all([challenge.syncToUser(user), challenge.save()]); - let response = results[1].toJSON(); + const response = results[1].toJSON(); response.group = getChallengeGroupResponse(group); - let chalLeader = await User.findById(response.leader).select(nameFields).exec(); - response.leader = chalLeader ? chalLeader.toJSON({minimize: true}) : null; + const chalLeader = await User.findById(response.leader).select(nameFields).exec(); + response.leader = chalLeader ? chalLeader.toJSON({ minimize: true }) : null; res.analytics.track('challenge join', { uuid: user._id, @@ -292,15 +294,15 @@ api.leaveChallenge = { url: '/challenges/:challengeId/leave', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let keep = req.body.keep === 'remove-all' ? 'remove-all' : 'keep-all'; + const { user } = res.locals; + const keep = req.body.keep === 'remove-all' ? 'remove-all' : 'keep-all'; req.checkParams('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let challenge = await Challenge.findOne({ _id: req.params.challengeId }).exec(); + const challenge = await Challenge.findOne({ _id: req.params.challengeId }).exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); if (!challenge.isMember(user)) throw new NotAuthorized(res.t('challengeMemberNotFound')); @@ -342,50 +344,50 @@ api.getUserChallenges = { middlewares: [authWithHeaders()], async handler (req, res) { const CHALLENGES_PER_PAGE = 10; - const page = req.query.page; + const { page } = req.query; - const user = res.locals.user; - let orOptions = [ - {_id: {$in: user.challenges}}, // Challenges where the user is participating + const { user } = res.locals; + const orOptions = [ + { _id: { $in: user.challenges } }, // Challenges where the user is participating ]; - const owned = req.query.owned; - if (!owned) { - orOptions.push({leader: user._id}); + const { owned } = req.query; + if (!owned) { + orOptions.push({ leader: user._id }); } if (!req.query.member) { orOptions.push({ - group: {$in: user.getGroups()}, + group: { $in: user.getGroups() }, }); // Challenges in groups where I'm a member } - let query = { - $and: [{$or: orOptions}], + const query = { + $and: [{ $or: orOptions }], }; if (owned) { if (owned === 'not_owned') { - query.$and.push({leader: {$ne: user._id}}); + query.$and.push({ leader: { $ne: user._id } }); } if (owned === 'owned') { - query.$and.push({leader: user._id}); + query.$and.push({ leader: user._id }); } } if (req.query.search) { - const searchOr = {$or: []}; + const searchOr = { $or: [] }; const searchWords = _.escapeRegExp(req.query.search).split(' ').join('|'); const searchQuery = { $regex: new RegExp(`${searchWords}`, 'i') }; - searchOr.$or.push({name: searchQuery}); - searchOr.$or.push({description: searchQuery}); + searchOr.$or.push({ name: searchQuery }); + searchOr.$or.push({ description: searchQuery }); query.$and.push(searchOr); } if (req.query.categories) { - let categorySlugs = req.query.categories.split(','); - query.categories = { $elemMatch: { slug: {$in: categorySlugs} } }; + const categorySlugs = req.query.categories.split(','); + query.categories = { $elemMatch: { slug: { $in: categorySlugs } } }; } let mongoQuery = Challenge.find(query) @@ -404,20 +406,16 @@ api.getUserChallenges = { let resChals = challenges.map(challenge => challenge.toJSON()); - resChals = _.orderBy(resChals, [challenge => { - return challenge.categories.map(category => category.slug).includes('habitica_official'); - }], ['desc']); + resChals = _.orderBy(resChals, [challenge => challenge.categories.map(category => category.slug).includes('habitica_official')], ['desc']); // Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 - await Promise.all(resChals.map((chal, index) => { - return Promise.all([ - User.findById(chal.leader).select(`${nameFields} backer contributor`).exec(), - Group.findById(chal.group).select(basicGroupFields).exec(), - ]).then(populatedData => { - resChals[index].leader = populatedData[0] ? populatedData[0].toJSON({minimize: true}) : null; - resChals[index].group = populatedData[1] ? populatedData[1].toJSON({minimize: true}) : null; - }); - })); + await Promise.all(resChals.map((chal, index) => Promise.all([ + User.findById(chal.leader).select(`${nameFields} backer contributor`).exec(), + Group.findById(chal.group).select(basicGroupFields).exec(), + ]).then(populatedData => { + resChals[index].leader = populatedData[0] ? populatedData[0].toJSON({ minimize: true }) : null; + resChals[index].group = populatedData[1] ? populatedData[1].toJSON({ minimize: true }) : null; + }))); res.respond(200, resChals); }, @@ -446,12 +444,12 @@ api.getGroupChallenges = { userFieldsToInclude: ['party', 'guilds'], // Some fields are always loaded (see middlewares/auth) })], async handler (req, res) { - let user = res.locals.user; - let groupId = req.params.groupId; + const { user } = res.locals; + let { groupId } = req.params; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; if (groupId === 'party') groupId = user.party._id; @@ -467,20 +465,16 @@ api.getGroupChallenges = { let resChals = challenges.map(challenge => challenge.toJSON()); - resChals = _.orderBy(resChals, [challenge => { - return challenge.categories.map(category => category.slug).includes('habitica_official'); - }], ['desc']); + resChals = _.orderBy(resChals, [challenge => challenge.categories.map(category => category.slug).includes('habitica_official')], ['desc']); // Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 - await Promise.all(resChals.map((chal, index) => { - return User - .findById(chal.leader) - .select(nameFields) - .exec() - .then(populatedLeader => { - resChals[index].leader = populatedLeader ? populatedLeader.toJSON({minimize: true}) : null; - }); - })); + await Promise.all(resChals.map((chal, index) => User + .findById(chal.leader) + .select(nameFields) + .exec() + .then(populatedLeader => { + resChals[index].leader = populatedLeader ? populatedLeader.toJSON({ minimize: true }) : null; + }))); res.respond(200, resChals); }, @@ -506,27 +500,29 @@ api.getChallenge = { async handler (req, res) { req.checkParams('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let user = res.locals.user; - let challengeId = req.params.challengeId; + const { user } = res.locals; + const { challengeId } = req.params; // Don't populate the group as we'll fetch it manually later // .populate('leader', nameFields) - let challenge = await Challenge.findById(challengeId).exec(); + const challenge = await Challenge.findById(challengeId).exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); // Fetching basic group data - let group = await Group.getGroup({user, groupId: challenge.group, fields: basicGroupFields, optionalMembership: true}); + const group = await Group.getGroup({ + user, groupId: challenge.group, fields: basicGroupFields, optionalMembership: true, + }); if (!group || !challenge.canView(user, group)) throw new NotFound(res.t('challengeNotFound')); - let chalRes = challenge.toJSON(); - chalRes.group = group.toJSON({minimize: true}); + const chalRes = challenge.toJSON(); + chalRes.group = group.toJSON({ minimize: true }); // Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 - let chalLeader = await User.findById(chalRes.leader).select(nameFields).exec(); - chalRes.leader = chalLeader ? chalLeader.toJSON({minimize: true}) : null; + const chalLeader = await User.findById(chalRes.leader).select(nameFields).exec(); + chalRes.leader = chalLeader ? chalLeader.toJSON({ minimize: true }) : null; res.respond(200, chalRes); }, @@ -550,33 +546,36 @@ api.exportChallengeCsv = { async handler (req, res) { req.checkParams('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let user = res.locals.user; - let challengeId = req.params.challengeId; + const { user } = res.locals; + const { challengeId } = req.params; - let challenge = await Challenge.findById(challengeId).select('_id group leader tasksOrder').exec(); + const challenge = await Challenge.findById(challengeId).select('_id group leader tasksOrder').exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); - let group = await Group.getGroup({user, groupId: challenge.group, fields: '_id type privacy', optionalMembership: true}); + const group = await Group.getGroup({ + user, groupId: challenge.group, fields: '_id type privacy', optionalMembership: true, + }); if (!group || !challenge.canView(user, group)) throw new NotFound(res.t('challengeNotFound')); // In v2 this used the aggregation framework to run some computation on MongoDB but then iterated through all // results on the server so the perf difference isn't that big (hopefully) - let [members, tasks] = await Promise.all([ - User.find({challenges: challengeId}) + const [members, tasks] = await Promise.all([ + User.find({ challenges: challengeId }) .select(nameFields) - .sort({_id: 1}) + .sort({ _id: 1 }) .lean() // so we don't involve mongoose .exec(), Tasks.Task.find({ 'challenge.id': challengeId, - userId: {$exists: true}, - }).sort({userId: 1, text: 1}) + userId: { $exists: true }, + }).sort({ userId: 1, text: 1 }) .select('userId type text value notes streak') - .lean().exec(), + .lean() + .exec(), ]); let resArray = members.map(member => [member._id, member.profile.name, member.auth.local.username]); @@ -605,15 +604,13 @@ api.exportChallengeCsv = { }); // The first row is going to be UUID name Task Value Notes repeated n times for the n challenge tasks - let challengeTasks = _.reduce(challenge.tasksOrder.toObject(), (result, array) => { - return result.concat(array); - }, []).sort(); + const challengeTasks = _.reduce(challenge.tasksOrder.toObject(), (result, array) => result.concat(array), []).sort(); resArray.unshift(['UUID', 'Display Name', 'Username']); _.times(challengeTasks.length, () => resArray[0].push('Task', 'Value', 'Notes', 'Streak')); // Remove lines for users without tasks info - resArray = resArray.filter((line) => { + resArray = resArray.filter(line => { if (line.length === 2) { // only user data ([id, profile name]), no task data return false; } @@ -626,7 +623,7 @@ api.exportChallengeCsv = { 'Content-disposition': `attachment; filename=${challengeId}.csv`, }); - let csvRes = await csvStringify(resArray); + const csvRes = await csvStringify(resArray); res.status(200).send(csvRes); }, }; @@ -660,26 +657,28 @@ api.updateChallenge = { async handler (req, res) { req.checkParams('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let user = res.locals.user; - let challengeId = req.params.challengeId; + const { user } = res.locals; + const { challengeId } = req.params; - let challenge = await Challenge.findById(challengeId).exec(); + const challenge = await Challenge.findById(challengeId).exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); - let group = await Group.getGroup({user, groupId: challenge.group, fields: basicGroupFields, optionalMembership: true}); + const group = await Group.getGroup({ + user, groupId: challenge.group, fields: basicGroupFields, optionalMembership: true, + }); if (!group || !challenge.canView(user, group)) throw new NotFound(res.t('challengeNotFound')); if (!challenge.canModify(user)) throw new NotAuthorized(res.t('onlyLeaderUpdateChal')); _.merge(challenge, Challenge.sanitizeUpdate(req.body)); - let savedChal = await challenge.save(); - let response = savedChal.toJSON(); + const savedChal = await challenge.save(); + const response = savedChal.toJSON(); response.group = getChallengeGroupResponse(group); - let chalLeader = await User.findById(response.leader).select(nameFields).exec(); - response.leader = chalLeader ? chalLeader.toJSON({minimize: true}) : null; + const chalLeader = await User.findById(response.leader).select(nameFields).exec(); + response.leader = chalLeader ? chalLeader.toJSON({ minimize: true }) : null; res.respond(200, response); }, }; @@ -700,19 +699,19 @@ api.deleteChallenge = { url: '/challenges/:challengeId', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let challenge = await Challenge.findOne({_id: req.params.challengeId}).exec(); + const challenge = await Challenge.findOne({ _id: req.params.challengeId }).exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); if (!challenge.canModify(user)) throw new NotAuthorized(res.t('onlyLeaderDeleteChal')); // Close channel in background, some ops are run in the background without `await`ing - await challenge.closeChal({broken: 'CHALLENGE_DELETED'}); + await challenge.closeChal({ broken: 'CHALLENGE_DELETED' }); res.analytics.track('challenge delete', { uuid: user._id, @@ -745,23 +744,23 @@ api.selectChallengeWinner = { url: '/challenges/:challengeId/selectWinner/:winnerId', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID(); req.checkParams('winnerId', res.t('winnerIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let challenge = await Challenge.findOne({_id: req.params.challengeId}).exec(); + const challenge = await Challenge.findOne({ _id: req.params.challengeId }).exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); if (!challenge.canModify(user)) throw new NotAuthorized(res.t('onlyLeaderDeleteChal')); - let winner = await User.findOne({_id: req.params.winnerId}).exec(); - if (!winner || winner.challenges.indexOf(challenge._id) === -1) throw new NotFound(res.t('winnerNotFound', {userId: req.params.winnerId})); + const winner = await User.findOne({ _id: req.params.winnerId }).exec(); + if (!winner || winner.challenges.indexOf(challenge._id) === -1) throw new NotFound(res.t('winnerNotFound', { userId: req.params.winnerId })); // Close channel in background, some ops are run in the background without `await`ing - await challenge.closeChal({broken: 'CHALLENGE_CLOSED', winner}); + await challenge.closeChal({ broken: 'CHALLENGE_CLOSED', winner }); res.analytics.track('challenge close', { uuid: user._id, @@ -794,26 +793,26 @@ api.cloneChallenge = { url: '/challenges/:challengeId/clone', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - const challengeToClone = await Challenge.findOne({_id: req.params.challengeId}).exec(); + const challengeToClone = await Challenge.findOne({ _id: req.params.challengeId }).exec(); if (!challengeToClone) throw new NotFound(res.t('challengeNotFound')); - const {savedChal} = await createChallenge(user, req, res); + const { savedChal } = await createChallenge(user, req, res); const challengeTasks = await Tasks.Task.find({ 'challenge.id': challengeToClone._id, - userId: {$exists: false}, + userId: { $exists: false }, }).exec(); const tasksToClone = challengeTasks.map(task => { - let clonedTask = cloneDeep(task.toObject()); - let omittedTask = cleanUpTask(clonedTask); + const clonedTask = cloneDeep(task.toObject()); + const omittedTask = cleanUpTask(clonedTask); return omittedTask; }); @@ -821,9 +820,9 @@ api.cloneChallenge = { body: tasksToClone, }; - const clonedTasks = await createTasks(taskRequest, res, {user, challenge: savedChal}); + const clonedTasks = await createTasks(taskRequest, res, { user, challenge: savedChal }); - res.respond(200, {clonedTasks, clonedChallenge: savedChal}); + res.respond(200, { clonedTasks, clonedChallenge: savedChal }); }, }; diff --git a/website/server/controllers/api-v3/chat.js b/website/server/controllers/api-v3/chat.js index 1d4a9d7b7d..67ca3eca60 100644 --- a/website/server/controllers/api-v3/chat.js +++ b/website/server/controllers/api-v3/chat.js @@ -1,3 +1,4 @@ +import nconf from 'nconf'; import { authWithHeaders } from '../../middlewares/auth'; import { model as Group } from '../../models/group'; import { model as User } from '../../models/user'; @@ -12,17 +13,14 @@ import { removeFromArray } from '../../libs/collectionManipulators'; import { getUserInfo, getGroupUrl, sendTxn } from '../../libs/email'; import * as slack from '../../libs/slack'; import { chatReporterFactory } from '../../libs/chatReporting/chatReporterFactory'; -import { getAuthorEmailFromMessage} from '../../libs/chat'; -import nconf from 'nconf'; +import { getAuthorEmailFromMessage } from '../../libs/chat'; import bannedWords from '../../libs/bannedWords'; import guildsAllowingBannedWords from '../../libs/guildsAllowingBannedWords'; import { getMatchesByWordArray } from '../../libs/stringUtils'; import bannedSlurs from '../../libs/bannedSlurs'; import apiError from '../../libs/apiError'; -const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map((email) => { - return { email, canSend: true }; -}); +const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map(email => ({ email, canSend: true })); /** * @apiDefine MessageNotFound @@ -44,10 +42,10 @@ const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map((email) * @apiError (400) {badRequest} messageIdRequired A message ID is required */ -let api = {}; +const api = {}; function textContainsBannedSlur (message) { - let bannedSlursMatched = getMatchesByWordArray(message, bannedSlurs); + const bannedSlursMatched = getMatchesByWordArray(message, bannedSlurs); return bannedSlursMatched.length > 0; } @@ -69,15 +67,15 @@ api.getChat = { url: '/groups/:groupId/chat', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - const groupId = req.params.groupId; - let group = await Group.getGroup({user, groupId, fields: 'chat'}); + const { groupId } = req.params; + const group = await Group.getGroup({ user, groupId, fields: 'chat' }); if (!group) throw new NotFound(res.t('groupNotFound')); const groupChat = await Group.toJSONCleanChat(group, user); @@ -110,42 +108,42 @@ api.postChat = { url: '/groups/:groupId/chat', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let groupId = req.params.groupId; + const { user } = res.locals; + const { groupId } = req.params; let chatUpdated; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); req.sanitize('message').trim(); req.checkBody('message', res.t('messageGroupChatBlankMessage')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let group = await Group.getGroup({user, groupId}); + const group = await Group.getGroup({ user, groupId }); // Check message for banned slurs if (textContainsBannedSlur(req.body.message)) { - let message = req.body.message; + const { message } = req.body; user.flags.chatRevoked = true; await user.save(); // Email the mods - let authorEmail = getUserInfo(user, ['email']).email; - let groupUrl = getGroupUrl(group); + const authorEmail = getUserInfo(user, ['email']).email; + const groupUrl = getGroupUrl(group); - let report = [ - {name: 'MESSAGE_TIME', content: (new Date()).toString()}, - {name: 'MESSAGE_TEXT', content: message}, + const report = [ + { name: 'MESSAGE_TIME', content: (new Date()).toString() }, + { name: 'MESSAGE_TEXT', content: message }, - {name: 'AUTHOR_USERNAME', content: user.profile.name}, - {name: 'AUTHOR_UUID', content: user._id}, - {name: 'AUTHOR_EMAIL', content: authorEmail}, - {name: 'AUTHOR_MODAL_URL', content: `/profile/${user._id}`}, + { name: 'AUTHOR_USERNAME', content: user.profile.name }, + { name: 'AUTHOR_UUID', content: user._id }, + { name: 'AUTHOR_EMAIL', content: authorEmail }, + { name: 'AUTHOR_MODAL_URL', content: `/profile/${user._id}` }, - {name: 'GROUP_NAME', content: group.name}, - {name: 'GROUP_TYPE', content: group.type}, - {name: 'GROUP_ID', content: group._id}, - {name: 'GROUP_URL', content: groupUrl}, + { name: 'GROUP_NAME', content: group.name }, + { name: 'GROUP_TYPE', content: group.type }, + { name: 'GROUP_ID', content: group._id }, + { name: 'GROUP_URL', content: groupUrl }, ]; sendTxn(FLAG_REPORT_EMAILS, 'slur-report-to-mods', report); @@ -169,15 +167,15 @@ api.postChat = { // prevent banned words being posted, except in private guilds/parties and in certain public guilds with specific topics if (group.privacy === 'public' && !guildsAllowingBannedWords[group._id]) { - let matchedBadWords = getBannedWordsFromText(req.body.message); + const matchedBadWords = getBannedWordsFromText(req.body.message); if (matchedBadWords.length > 0) { - throw new BadRequest(res.t('bannedWordUsed', {swearWordsUsed: matchedBadWords.join(', ')})); + throw new BadRequest(res.t('bannedWordUsed', { swearWordsUsed: matchedBadWords.join(', ') })); } } const chatRes = await Group.toJSONCleanChat(group, user); const lastClientMsg = req.query.previousMsg; - chatUpdated = lastClientMsg && group.chat && group.chat[0] && group.chat[0].id !== lastClientMsg ? true : false; + chatUpdated = !!(lastClientMsg && group.chat && group.chat[0] && group.chat[0].id !== lastClientMsg); if (group.checkChatSpam(user)) { throw new NotAuthorized(res.t('messageGroupChatSpam')); @@ -191,25 +189,25 @@ api.postChat = { let flagCount = 0; if (group.privacy === 'public' && user.flags.chatShadowMuted) { flagCount = common.constants.CHAT_FLAG_FROM_SHADOW_MUTE; - let message = req.body.message; + const { message } = req.body; // Email the mods - let authorEmail = getUserInfo(user, ['email']).email; - let groupUrl = getGroupUrl(group); + const authorEmail = getUserInfo(user, ['email']).email; + const groupUrl = getGroupUrl(group); - let report = [ - {name: 'MESSAGE_TIME', content: (new Date()).toString()}, - {name: 'MESSAGE_TEXT', content: message}, + const report = [ + { name: 'MESSAGE_TIME', content: (new Date()).toString() }, + { name: 'MESSAGE_TEXT', content: message }, - {name: 'AUTHOR_USERNAME', content: user.profile.name}, - {name: 'AUTHOR_UUID', content: user._id}, - {name: 'AUTHOR_EMAIL', content: authorEmail}, - {name: 'AUTHOR_MODAL_URL', content: `/profile/${user._id}`}, + { name: 'AUTHOR_USERNAME', content: user.profile.name }, + { name: 'AUTHOR_UUID', content: user._id }, + { name: 'AUTHOR_EMAIL', content: authorEmail }, + { name: 'AUTHOR_MODAL_URL', content: `/profile/${user._id}` }, - {name: 'GROUP_NAME', content: group.name}, - {name: 'GROUP_TYPE', content: group.type}, - {name: 'GROUP_ID', content: group._id}, - {name: 'GROUP_URL', content: groupUrl}, + { name: 'GROUP_NAME', content: group.name }, + { name: 'GROUP_TYPE', content: group.type }, + { name: 'GROUP_ID', content: group._id }, + { name: 'GROUP_URL', content: groupUrl }, ]; sendTxn(FLAG_REPORT_EMAILS, 'shadow-muted-post-report-to-mods', report); @@ -223,8 +221,10 @@ api.postChat = { }); } - const newChatMessage = group.sendChat({message: req.body.message, user, flagCount, metaData: null, client, translate: res.t}); - let toSave = [newChatMessage.save()]; + const newChatMessage = group.sendChat({ + message: req.body.message, user, flagCount, metaData: null, client, translate: res.t, + }); + const toSave = [newChatMessage.save()]; if (group.type === 'party') { user.party.lastMessageSeen = newChatMessage.id; @@ -233,7 +233,7 @@ api.postChat = { await Promise.all(toSave); - let analyticsObject = { + const analyticsObject = { uuid: user._id, hitType: 'event', category: 'behavior', @@ -255,9 +255,9 @@ api.postChat = { res.analytics.track('group chat', analyticsObject); if (chatUpdated) { - res.respond(200, {chat: chatRes.chat}); + res.respond(200, { chat: chatRes.chat }); } else { - res.respond(200, {message: newChatMessage}); + res.respond(200, { message: newChatMessage }); } }, }; @@ -284,19 +284,19 @@ api.likeChat = { url: '/groups/:groupId/chat/:chatId/like', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let groupId = req.params.groupId; + const { user } = res.locals; + const { groupId } = req.params; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); req.checkParams('chatId', apiError('chatIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let group = await Group.getGroup({user, groupId}); + const group = await Group.getGroup({ user, groupId }); if (!group) throw new NotFound(res.t('groupNotFound')); - let message = await Chat.findOne({_id: req.params.chatId}).exec(); + const message = await Chat.findOne({ _id: req.params.chatId }).exec(); if (!message) throw new NotFound(res.t('messageGroupChatNotFound')); // @TODO correct this error type if (message.uuid === user._id) throw new NotFound(res.t('messageGroupChatLikeOwnMessage')); @@ -371,55 +371,55 @@ api.clearChatFlags = { url: '/groups/:groupId/chat/:chatId/clearflags', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let groupId = req.params.groupId; - let chatId = req.params.chatId; + const { user } = res.locals; + const { groupId } = req.params; + const { chatId } = req.params; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); req.checkParams('chatId', apiError('chatIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; if (!user.contributor.admin) { throw new NotAuthorized(res.t('messageGroupChatAdminClearFlagCount')); } - let group = await Group.getGroup({ + const group = await Group.getGroup({ user, groupId, optionalMembership: user.contributor.admin, }); if (!group) throw new NotFound(res.t('groupNotFound')); - let message = await Chat.findOne({_id: chatId}).exec(); + const message = await Chat.findOne({ _id: chatId }).exec(); if (!message) throw new NotFound(res.t('messageGroupChatNotFound')); message.flagCount = 0; await message.save(); - let adminEmailContent = getUserInfo(user, ['email']).email; - let authorEmail = getAuthorEmailFromMessage(message); - let groupUrl = getGroupUrl(group); + const adminEmailContent = getUserInfo(user, ['email']).email; + const authorEmail = getAuthorEmailFromMessage(message); + const groupUrl = getGroupUrl(group); sendTxn(FLAG_REPORT_EMAILS, 'unflag-report-to-mods', [ - {name: 'MESSAGE_TIME', content: (new Date(message.timestamp)).toString()}, - {name: 'MESSAGE_TEXT', content: message.text}, + { name: 'MESSAGE_TIME', content: (new Date(message.timestamp)).toString() }, + { name: 'MESSAGE_TEXT', content: message.text }, - {name: 'ADMIN_USERNAME', content: user.profile.name}, - {name: 'ADMIN_UUID', content: user._id}, - {name: 'ADMIN_EMAIL', content: adminEmailContent}, - {name: 'ADMIN_MODAL_URL', content: `/profile/${user._id}`}, + { name: 'ADMIN_USERNAME', content: user.profile.name }, + { name: 'ADMIN_UUID', content: user._id }, + { name: 'ADMIN_EMAIL', content: adminEmailContent }, + { name: 'ADMIN_MODAL_URL', content: `/profile/${user._id}` }, - {name: 'AUTHOR_USERNAME', content: message.user}, - {name: 'AUTHOR_UUID', content: message.uuid}, - {name: 'AUTHOR_EMAIL', content: authorEmail}, - {name: 'AUTHOR_MODAL_URL', content: `/profile/${message.uuid}`}, + { name: 'AUTHOR_USERNAME', content: message.user }, + { name: 'AUTHOR_UUID', content: message.uuid }, + { name: 'AUTHOR_EMAIL', content: authorEmail }, + { name: 'AUTHOR_MODAL_URL', content: `/profile/${message.uuid}` }, - {name: 'GROUP_NAME', content: group.name}, - {name: 'GROUP_TYPE', content: group.type}, - {name: 'GROUP_ID', content: group._id}, - {name: 'GROUP_URL', content: groupUrl}, + { name: 'GROUP_NAME', content: group.name }, + { name: 'GROUP_TYPE', content: group.type }, + { name: 'GROUP_ID', content: group._id }, + { name: 'GROUP_URL', content: groupUrl }, ]); res.respond(200, {}); @@ -441,19 +441,19 @@ api.seenChat = { url: '/groups/:groupId/chat/seen', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let groupId = req.params.groupId; + const { user } = res.locals; + const { groupId } = req.params; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; // Do not validate group existence, it doesn't really matter and make it works if the group gets deleted // let group = await Group.getGroup({user, groupId}); // if (!group) throw new NotFound(res.t('groupNotFound')); - let update = { + const update = { $unset: {}, $pull: {}, }; @@ -478,7 +478,7 @@ api.seenChat = { // See https://github.com/HabitRPG/habitica/pull/9321#issuecomment-354187666 for more info user._v++; - await User.update({_id: user._id}, update).exec(); + await User.update({ _id: user._id }, update).exec(); res.respond(200, {}); }, }; @@ -507,20 +507,20 @@ api.deleteChat = { url: '/groups/:groupId/chat/:chatId', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let groupId = req.params.groupId; - let chatId = req.params.chatId; + const { user } = res.locals; + const { groupId } = req.params; + const { chatId } = req.params; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); req.checkParams('chatId', apiError('chatIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let group = await Group.getGroup({user, groupId, fields: 'chat'}); + const group = await Group.getGroup({ user, groupId, fields: 'chat' }); if (!group) throw new NotFound(res.t('groupNotFound')); - let message = await Chat.findOne({_id: chatId}).exec(); + const message = await Chat.findOne({ _id: chatId }).exec(); if (!message) throw new NotFound(res.t('messageGroupChatNotFound')); if (user._id !== message.uuid && !user.contributor.admin) { @@ -529,12 +529,12 @@ api.deleteChat = { const chatRes = await Group.toJSONCleanChat(group, user); const lastClientMsg = req.query.previousMsg; - const chatUpdated = lastClientMsg && group.chat && group.chat[0] && group.chat[0].id !== lastClientMsg ? true : false; + const chatUpdated = !!(lastClientMsg && group.chat && group.chat[0] && group.chat[0].id !== lastClientMsg); - await Chat.remove({_id: message._id}).exec(); + await Chat.remove({ _id: message._id }).exec(); if (chatUpdated) { - removeFromArray(chatRes.chat, {id: chatId}); + removeFromArray(chatRes.chat, { id: chatId }); res.respond(200, chatRes.chat); } else { res.respond(200, {}); diff --git a/website/server/controllers/api-v3/content.js b/website/server/controllers/api-v3/content.js index f175872033..75bef8096b 100644 --- a/website/server/controllers/api-v3/content.js +++ b/website/server/controllers/api-v3/content.js @@ -1,10 +1,10 @@ -import common from '../../../common'; import _ from 'lodash'; -import { langCodes } from '../../libs/i18n'; import fsCallback from 'fs'; import path from 'path'; -import logger from '../../libs/logger'; import util from 'util'; +import logger from '../../libs/logger'; +import { langCodes } from '../../libs/i18n'; +import common from '../../../common'; // Transform fs methods that accept callbacks in ones that return promises const fs = { @@ -14,7 +14,7 @@ const fs = { mkdir: util.promisify(fsCallback.mkdir).bind(fsCallback), }; -let api = {}; +const api = {}; function walkContent (obj, lang) { _.each(obj, (item, key, source) => { @@ -26,10 +26,10 @@ function walkContent (obj, lang) { // After the getContent route is called the first time for a certain language // the response is saved on disk and subsequentially served directly from there to reduce computation. // Example: if `cachedContentResponses.en` is true it means that the response is cached -let cachedContentResponses = {}; +const cachedContentResponses = {}; // Language key set to true while the cache file is being written -let cacheBeingWritten = {}; +const cacheBeingWritten = {}; _.each(langCodes, code => { cachedContentResponses[code] = false; @@ -52,11 +52,9 @@ async function saveContentToDisk (language, content) { if (err.code === 'ENOENT' && err.syscall === 'stat') { // the directory doesn't exists, create it and retry await fs.mkdir(CONTENT_CACHE_PATH); return saveContentToDisk(language, content); - } else { - cacheBeingWritten[language] = false; - logger.error(err); - return; } + cacheBeingWritten[language] = false; + logger.error(err); } } @@ -104,7 +102,7 @@ api.getContent = { noLanguage: true, async handler (req, res) { let language = 'en'; - let proposedLang = req.query.language && req.query.language.toString(); + const proposedLang = req.query.language && req.query.language.toString(); if (proposedLang in cachedContentResponses) { language = proposedLang; @@ -125,7 +123,7 @@ api.getContent = { 'Content-Type': 'application/json', }); - let jsonResString = `{"success": true, "data": ${content}}`; + const jsonResString = `{"success": true, "data": ${content}}`; res.status(200).send(jsonResString); // save the file in background unless it's already cached or being written right now diff --git a/website/server/controllers/api-v3/coupon.js b/website/server/controllers/api-v3/coupon.js index 38109f51be..efbc7345fa 100644 --- a/website/server/controllers/api-v3/coupon.js +++ b/website/server/controllers/api-v3/coupon.js @@ -1,16 +1,16 @@ +import _ from 'lodash'; +import couponCode from 'coupon-code'; import csvStringify from '../../libs/csvStringify'; import { authWithHeaders, authWithSession, } from '../../middlewares/auth'; import { ensureSudo } from '../../middlewares/ensureAccessRight'; -import _ from 'lodash'; import * as couponsLib from '../../libs/coupons'; -import couponCode from 'coupon-code'; import apiError from '../../libs/apiError'; import { model as Coupon } from '../../models/coupon'; -let api = {}; +const api = {}; /** * @apiDefine Sudo Sudo Users @@ -37,12 +37,10 @@ api.getCoupons = { url: '/coupons', middlewares: [authWithSession, ensureSudo], async handler (req, res) { - let coupons = await Coupon.find().sort('createdAt').lean().exec(); + const coupons = await Coupon.find().sort('createdAt').lean().exec(); - let output = [['code', 'event', 'date', 'user']].concat(_.map(coupons, coupon => { - return [coupon._id, coupon.event, coupon.createdAt, coupon.user]; - })); - let csv = await csvStringify(output); + const output = [['code', 'event', 'date', 'user']].concat(_.map(coupons, coupon => [coupon._id, coupon.event, coupon.createdAt, coupon.user])); + const csv = await csvStringify(output); res.set({ 'Content-Type': 'text/csv', @@ -74,10 +72,10 @@ api.generateCoupons = { req.checkParams('event', apiError('eventRequired')).notEmpty(); req.checkQuery('count', apiError('countRequired')).notEmpty().isNumeric(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let coupons = await Coupon.generate(req.params.event, req.query.count); + const coupons = await Coupon.generate(req.params.event, req.query.count); res.respond(200, coupons); }, }; @@ -98,7 +96,7 @@ api.enterCouponCode = { url: '/coupons/enter/:code', middlewares: [authWithHeaders()], async handler (req, res) { - const user = res.locals.user; + const { user } = res.locals; await couponsLib.enterCode(req, res, user); const userToJSON = await user.toJSONWithInbox(); res.respond(200, userToJSON); @@ -123,17 +121,17 @@ api.validateCoupon = { async handler (req, res) { req.checkParams('code', res.t('couponCodeRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; let valid = false; - let code = couponCode.validate(req.params.code); + const code = couponCode.validate(req.params.code); if (code) { - let coupon = await Coupon.findOne({_id: code}).exec(); - valid = coupon ? true : false; + const coupon = await Coupon.findOne({ _id: code }).exec(); + valid = !!coupon; } - res.respond(200, {valid}); + res.respond(200, { valid }); }, }; diff --git a/website/server/controllers/api-v3/cron.js b/website/server/controllers/api-v3/cron.js index ecba00df92..0016650bcf 100644 --- a/website/server/controllers/api-v3/cron.js +++ b/website/server/controllers/api-v3/cron.js @@ -1,7 +1,7 @@ import { authWithHeaders } from '../../middlewares/auth'; import cron from '../../middlewares/cron'; -let api = {}; +const api = {}; /** * @api {post} /api/v3/cron Runs cron diff --git a/website/server/controllers/api-v3/debug.js b/website/server/controllers/api-v3/debug.js index 46297f14ea..65a9487a09 100644 --- a/website/server/controllers/api-v3/debug.js +++ b/website/server/controllers/api-v3/debug.js @@ -1,10 +1,10 @@ +import _ from 'lodash'; import { authWithHeaders } from '../../middlewares/auth'; import ensureDevelpmentMode from '../../middlewares/ensureDevelpmentMode'; import { BadRequest } from '../../libs/errors'; import common from '../../../common'; -import _ from 'lodash'; -const content = common.content; +const { content } = common; /** * @apiDefine Development Development @@ -16,7 +16,7 @@ const content = common.content; * This route only exists when developing Habitica in non-production environment. */ -let api = {}; +const api = {}; /** * @api {post} /api/v3/debug/add-ten-gems Add ten gems to the current user @@ -31,7 +31,7 @@ api.addTenGems = { url: '/debug/add-ten-gems', middlewares: [ensureDevelpmentMode, authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; user.balance += 2.5; @@ -54,7 +54,7 @@ api.addHourglass = { url: '/debug/add-hourglass', middlewares: [ensureDevelpmentMode, authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; user.purchased.plan.consecutive.trinkets += 1; @@ -77,8 +77,8 @@ api.setCron = { url: '/debug/set-cron', middlewares: [ensureDevelpmentMode, authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let cron = req.body.lastCron; + const { user } = res.locals; + const cron = req.body.lastCron; user.lastCron = cron; @@ -101,7 +101,7 @@ api.makeAdmin = { url: '/debug/make-admin', middlewares: [ensureDevelpmentMode, authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; user.contributor.admin = true; @@ -132,8 +132,8 @@ api.modifyInventory = { url: '/debug/modify-inventory', middlewares: [ensureDevelpmentMode, authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let { gear } = req.body; + const { user } = res.locals; + const { gear } = req.body; if (gear) { user.items.gear.owned = gear; @@ -148,7 +148,7 @@ api.modifyInventory = { 'hatchingPotions', 'food', 'quests', - ].forEach((type) => { + ].forEach(type => { if (req.body[type]) { user.items[type] = req.body[type]; user.markModified(`items.${type}`); @@ -174,9 +174,9 @@ api.questProgress = { url: '/debug/quest-progress', middlewares: [ensureDevelpmentMode, authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let key = _.get(user, 'party.quest.key'); - let quest = content.quests[key]; + const { user } = res.locals; + const key = _.get(user, 'party.quest.key'); + const quest = content.quests[key]; if (!quest) { throw new BadRequest('User is not on a valid quest.'); diff --git a/website/server/controllers/api-v3/groups.js b/website/server/controllers/api-v3/groups.js index a5b6039aef..bfd2d17eca 100644 --- a/website/server/controllers/api-v3/groups.js +++ b/website/server/controllers/api-v3/groups.js @@ -1,6 +1,6 @@ -import { authWithHeaders } from '../../middlewares/auth'; import _ from 'lodash'; import nconf from 'nconf'; +import { authWithHeaders } from '../../middlewares/auth'; import { model as Group, basicFields as basicGroupFields, @@ -61,7 +61,7 @@ const TECH_ASSISTANCE_EMAIL = nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL'); * The group leader can use this route. */ -let api = {}; +const api = {}; /** * @api {post} /api/v3/groups Create group @@ -112,8 +112,8 @@ api.createGroup = { url: '/groups', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let group = new Group(Group.sanitize(req.body)); + const { user } = res.locals; + const group = new Group(Group.sanitize(req.body)); group.leader = user._id; if (group.type === 'guild') { @@ -135,19 +135,19 @@ api.createGroup = { user.party._id = group._id; } - let results = await Promise.all([user.save(), group.save()]); - let savedGroup = results[1]; + const results = await Promise.all([user.save(), group.save()]); + const savedGroup = results[1]; // Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 // await Q.ninvoke(savedGroup, 'populate', ['leader', nameFields]); // doc.populate doesn't return a promise - let response = savedGroup.toJSON(); + const response = savedGroup.toJSON(); // the leader is the authenticated user response.leader = { _id: user._id, - profile: {name: user.profile.name}, + profile: { name: user.profile.name }, }; - let analyticsObject = { + const analyticsObject = { uuid: user._id, hitType: 'event', category: 'behavior', @@ -179,12 +179,12 @@ api.createGroupPlan = { url: '/groups/create-plan', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let group = new Group(Group.sanitize(req.body.groupToCreate)); + const { user } = res.locals; + const group = new Group(Group.sanitize(req.body.groupToCreate)); req.checkBody('paymentType', res.t('paymentTypeRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; // @TODO: Change message @@ -192,11 +192,11 @@ api.createGroupPlan = { group.leader = user._id; user.guilds.push(group._id); - let results = await Promise.all([user.save(), group.save()]); - let savedGroup = results[1]; + const results = await Promise.all([user.save(), group.save()]); + const savedGroup = results[1]; // Analytics - let analyticsObject = { + const analyticsObject = { uuid: user._id, hitType: 'event', category: 'behavior', @@ -208,13 +208,13 @@ api.createGroupPlan = { res.analytics.track('join group', analyticsObject); if (req.body.paymentType === 'Stripe') { - let token = req.body.id; - let gift = req.query.gift ? JSON.parse(req.query.gift) : undefined; - let sub = req.query.sub ? shared.content.subscriptionBlocks[req.query.sub] : false; - let groupId = savedGroup._id; - let email = req.body.email; - let headers = req.headers; - let coupon = req.query.coupon; + const token = req.body.id; + const gift = req.query.gift ? JSON.parse(req.query.gift) : undefined; + const sub = req.query.sub ? shared.content.subscriptionBlocks[req.query.sub] : false; + const groupId = savedGroup._id; + const { email } = req.body; + const { headers } = req; + const { coupon } = req.query; await stripePayments.checkout({ token, @@ -227,11 +227,11 @@ api.createGroupPlan = { coupon, }); } else if (req.body.paymentType === 'Amazon') { - let billingAgreementId = req.body.billingAgreementId; - let sub = req.body.subscription ? shared.content.subscriptionBlocks[req.body.subscription] : false; - let coupon = req.body.coupon; - let groupId = savedGroup._id; - let headers = req.headers; + const { billingAgreementId } = req.body; + const sub = req.body.subscription ? shared.content.subscriptionBlocks[req.body.subscription] : false; + const { coupon } = req.body; + const groupId = savedGroup._id; + const { headers } = req; await amzLib.subscribe({ billingAgreementId, @@ -245,11 +245,11 @@ api.createGroupPlan = { // Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 // await Q.ninvoke(savedGroup, 'populate', ['leader', nameFields]); // doc.populate doesn't return a promise - let response = savedGroup.toJSON(); + const response = savedGroup.toJSON(); // the leader is the authenticated user response.leader = { _id: user._id, - profile: {name: user.profile.name}, + profile: { name: user.profile.name }, }; res.respond(201, response); // do not remove chat flags data as we've just created the group @@ -288,30 +288,30 @@ api.getGroups = { url: '/groups', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkQuery('type', res.t('groupTypesRequired')).notEmpty(); // pagination options, can only be used with public guilds req.checkQuery('paginate').optional().isIn(['true', 'false'], apiError('guildsPaginateBooleanString')); - req.checkQuery('page').optional().isInt({min: 0}, apiError('queryPageInteger')); + req.checkQuery('page').optional().isInt({ min: 0 }, apiError('queryPageInteger')); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let types = req.query.type.split(','); + const types = req.query.type.split(','); - let paginate = req.query.paginate === 'true' ? true : false; + const paginate = req.query.paginate === 'true'; if (paginate && !_.includes(types, 'publicGuilds')) { throw new BadRequest(apiError('guildsOnlyPaginate')); } - let groupFields = basicGroupFields.concat(' description memberCount balance'); - let sort = '-memberCount'; + const groupFields = basicGroupFields.concat(' description memberCount balance'); + const sort = '-memberCount'; - let filters = {}; + const filters = {}; if (req.query.categories) { - let categorySlugs = req.query.categories.split(','); - filters.categories = { $elemMatch: { slug: {$in: categorySlugs} } }; + const categorySlugs = req.query.categories.split(','); + filters.categories = { $elemMatch: { slug: { $in: categorySlugs } } }; } if (req.query.minMemberCount) { @@ -337,13 +337,18 @@ api.getGroups = { filters.$or = []; const searchWords = _.escapeRegExp(req.query.search).split(' ').join('|'); const searchQuery = { $regex: new RegExp(`${searchWords}`, 'i') }; - filters.$or.push({name: searchQuery}); - filters.$or.push({description: searchQuery}); + filters.$or.push({ name: searchQuery }); + filters.$or.push({ description: searchQuery }); } - let results = await Group.getGroups({ - user, types, groupFields, sort, - paginate, page: req.query.page, filters, + const results = await Group.getGroups({ + user, + types, + groupFields, + sort, + paginate, + page: req.query.page, + filters, }); res.respond(200, results); }, @@ -379,28 +384,28 @@ api.getGroup = { userFieldsToInclude: ['party', 'guilds', 'contributor'], })], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let groupId = req.params.groupId; - let group = await Group.getGroup({user, groupId, populateLeader: false}); + const { groupId } = req.params; + const group = await Group.getGroup({ user, groupId, populateLeader: false }); if (!group) { throw new NotFound(res.t('groupNotFound')); } - let groupJson = await Group.toJSONCleanChat(group, user); + const groupJson = await Group.toJSONCleanChat(group, user); if (groupJson.leader === user._id) { groupJson.purchased.plan = group.purchased.plan.toObject(); } // Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 - let leader = await User.findById(groupJson.leader).select(nameFields).exec(); - if (leader) groupJson.leader = leader.toJSON({minimize: true}); + const leader = await User.findById(groupJson.leader).select(nameFields).exec(); + if (leader) groupJson.leader = leader.toJSON({ minimize: true }); res.respond(200, groupJson); }, @@ -437,14 +442,14 @@ api.updateGroup = { url: '/groups/:groupId', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let optionalMembership = Boolean(user.contributor.admin); - let group = await Group.getGroup({user, groupId: req.params.groupId, optionalMembership}); + const optionalMembership = Boolean(user.contributor.admin); + const group = await Group.getGroup({ user, groupId: req.params.groupId, optionalMembership }); if (!group) throw new NotFound(res.t('groupNotFound')); @@ -455,17 +460,17 @@ api.updateGroup = { _.assign(group, _.merge(group.toObject(), Group.sanitizeUpdate(req.body))); - let savedGroup = await group.save(); - let response = await Group.toJSONCleanChat(savedGroup, user); + const savedGroup = await group.save(); + const response = await Group.toJSONCleanChat(savedGroup, user); // If the leader changed fetch new data, otherwise use authenticated user if (response.leader !== user._id) { - let rawLeader = await User.findById(response.leader).select(nameFields).exec(); - response.leader = rawLeader.toJSON({minimize: true}); + const rawLeader = await User.findById(response.leader).select(nameFields).exec(); + response.leader = rawLeader.toJSON({ minimize: true }); } else { response.leader = { _id: user._id, - profile: {name: user.profile.name}, + profile: { name: user.profile.name }, }; } res.respond(200, response); @@ -500,30 +505,30 @@ api.joinGroup = { url: '/groups/:groupId/join', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; let inviter; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); // .isUUID(); can't be used because it would block 'habitrpg' or 'party' - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; // Works even if the user is not yet a member of the group - let group = await Group.getGroup({user, groupId: req.params.groupId, optionalMembership: true}); // Do not fetch chat and work even if the user is not yet a member of the group + const group = await Group.getGroup({ user, groupId: req.params.groupId, optionalMembership: true }); // Do not fetch chat and work even if the user is not yet a member of the group if (!group) throw new NotFound(res.t('groupNotFound')); let isUserInvited = false; if (group.type === 'party') { // Check if was invited to party - let inviterParty = _.find(user.invitations.parties, {id: group._id}); + const inviterParty = _.find(user.invitations.parties, { id: group._id }); if (inviterParty) { inviter = inviterParty.inviter; // If user was in a different party (when partying solo you can be invited to a new party) // make them leave that party before doing anything if (user.party._id) { - let userPreviousParty = await Group.getGroup({user, groupId: user.party._id}); + const userPreviousParty = await Group.getGroup({ user, groupId: user.party._id }); if (userPreviousParty.memberCount === 1 && user.party.quest.key) { throw new NotAuthorized(res.t('messageCannotLeaveWhileQuesting')); @@ -549,13 +554,13 @@ api.joinGroup = { isUserInvited = true; } } else if (group.type === 'guild') { - let hasInvitation = removeFromArray(user.invitations.guilds, { id: group._id }); + const hasInvitation = removeFromArray(user.invitations.guilds, { id: group._id }); if (hasInvitation) { isUserInvited = true; inviter = hasInvitation.inviter; } else { - isUserInvited = group.privacy === 'private' ? false : true; + isUserInvited = group.privacy !== 'private'; } } @@ -581,7 +586,7 @@ api.joinGroup = { if (inviter) { inviter = await User.findById(inviter).exec(); - let data = { + const data = { headerText: common.i18n.t('invitationAcceptedHeader', inviter.preferences.language), bodyText: common.i18n.t('invitationAcceptedBody', { groupName: group.name, @@ -604,32 +609,32 @@ api.joinGroup = { if (group.type === 'party' && inviter) { if (group.memberCount > 1) { promises.push(User.update({ - $or: [{'party._id': group._id}, {_id: user._id}], - 'achievements.partyUp': {$ne: true}, - }, {$set: {'achievements.partyUp': true}}, {multi: true}).exec()); + $or: [{ 'party._id': group._id }, { _id: user._id }], + 'achievements.partyUp': { $ne: true }, + }, { $set: { 'achievements.partyUp': true } }, { multi: true }).exec()); } if (group.memberCount > 3) { promises.push(User.update({ - $or: [{'party._id': group._id}, {_id: user._id}], - 'achievements.partyOn': {$ne: true}, - }, {$set: {'achievements.partyOn': true}}, {multi: true}).exec()); + $or: [{ 'party._id': group._id }, { _id: user._id }], + 'achievements.partyOn': { $ne: true }, + }, { $set: { 'achievements.partyOn': true } }, { multi: true }).exec()); } } promises = await Promise.all(promises); - if (group.hasNotCancelled()) { + if (group.hasNotCancelled()) { await payments.addSubToGroupUser(user, group); await group.updateGroupPlan(); } - let response = await Group.toJSONCleanChat(promises[0], user); - let leader = await User.findById(response.leader).select(nameFields).exec(); + const response = await Group.toJSONCleanChat(promises[0], user); + const leader = await User.findById(response.leader).select(nameFields).exec(); if (leader) { - response.leader = leader.toJSON({minimize: true}); + response.leader = leader.toJSON({ minimize: true }); } - let analyticsObject = { + const analyticsObject = { uuid: user._id, hitType: 'event', category: 'behavior', @@ -669,23 +674,23 @@ api.rejectGroupInvite = { url: '/groups/:groupId/reject-invite', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); // .isUUID(); can't be used because it would block 'habitrpg' or 'party' - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let groupId = req.params.groupId; + const { groupId } = req.params; let isUserInvited = false; - let hasPartyInvitation = removeFromArray(user.invitations.parties, { id: groupId }); + const hasPartyInvitation = removeFromArray(user.invitations.parties, { id: groupId }); if (hasPartyInvitation) { user.invitations.party = user.invitations.parties.length > 0 ? user.invitations.parties[user.invitations.parties.length - 1] : {}; user.markModified('invitations.party'); isUserInvited = true; } else { - let hasInvitation = removeFromArray(user.invitations.guilds, { id: groupId }); + const hasInvitation = removeFromArray(user.invitations.guilds, { id: groupId }); if (hasInvitation) { isUserInvited = true; @@ -744,17 +749,19 @@ api.leaveGroup = { url: '/groups/:groupId/leave', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); // When removing the user from challenges, should we keep the tasks? req.checkQuery('keep', apiError('keepOrRemoveAll')).optional().isIn(['keep-all', 'remove-all']); req.checkBody('keepChallenges', apiError('groupRemainOrLeaveChallenges')).optional().isIn(['remain-in-challenges', 'leave-challenges']); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let groupId = req.params.groupId; - let group = await Group.getGroup({user, groupId, fields: '-chat', requireMembership: true}); + const { groupId } = req.params; + const group = await Group.getGroup({ + user, groupId, fields: '-chat', requireMembership: true, + }); if (!group) { throw new NotFound(res.t('groupNotFound')); } @@ -775,11 +782,11 @@ api.leaveGroup = { await user.save(); if (group.type !== 'party') { - let guildIndex = user.guilds.indexOf(group._id); + const guildIndex = user.guilds.indexOf(group._id); if (guildIndex >= 0) user.guilds.splice(guildIndex, 1); } - let isMemberOfGroupPlan = await user.isMemberOfGroupPlan(); + const isMemberOfGroupPlan = await user.isMemberOfGroupPlan(); if (!isMemberOfGroupPlan) { await payments.cancelGroupSubscriptionForUser(user, group); } @@ -792,12 +799,12 @@ api.leaveGroup = { // Send an email to the removed user with an optional message from the leader function _sendMessageToRemoved (group, removedUser, message, isInGroup) { if (removedUser.preferences.emailNotifications.kickedGroup !== false) { - let subject = isInGroup ? `kicked-from-${group.type}` : `${group.type}-invite-rescinded`; + const subject = isInGroup ? `kicked-from-${group.type}` : `${group.type}-invite-rescinded`; sendTxnEmail(removedUser, subject, [ - {name: 'GROUP_NAME', content: group.name}, - {name: 'MESSAGE', content: message}, - {name: 'GUILDS_LINK', content: '/groups/discovery'}, - {name: 'PARTY_WANTED_GUILD', content: '/groups/guild/f2db2a7f-13c5-454d-b3ee-ea1f5089e601'}, + { name: 'GROUP_NAME', content: group.name }, + { name: 'MESSAGE', content: message }, + { name: 'GUILDS_LINK', content: '/groups/discovery' }, + { name: 'PARTY_WANTED_GUILD', content: '/groups/guild/f2db2a7f-13c5-454d-b3ee-ea1f5089e601' }, ]); } } @@ -831,19 +838,21 @@ api.removeGroupMember = { url: '/groups/:groupId/removeMember/:memberId', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); req.checkParams('memberId', res.t('userIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let optionalMembership = Boolean(user.contributor.admin); - let group = await Group.getGroup({user, groupId: req.params.groupId, optionalMembership, fields: '-chat'}); // Do not fetch chat + const optionalMembership = Boolean(user.contributor.admin); + const group = await Group.getGroup({ + user, groupId: req.params.groupId, optionalMembership, fields: '-chat', + }); // Do not fetch chat if (!group) throw new NotFound(res.t('groupNotFound')); - let uuid = req.params.memberId; + const uuid = req.params.memberId; if (group.leader !== user._id && group.type === 'party') throw new NotAuthorized(res.t('onlyLeaderCanRemoveMember')); if (group.leader !== user._id && !user.contributor.admin) throw new NotAuthorized(res.t('onlyLeaderCanRemoveMember')); @@ -852,7 +861,7 @@ api.removeGroupMember = { if (user._id === uuid) throw new NotAuthorized(res.t('memberCannotRemoveYourself')); - let member = await User.findOne({_id: uuid}).exec(); + const member = await User.findOne({ _id: uuid }).exec(); // We're removing the user from a guild or a party? is the user invited only? let isInGroup; @@ -863,9 +872,9 @@ api.removeGroupMember = { } let isInvited; - if (_.find(member.invitations.parties, {id: group._id})) { + if (_.find(member.invitations.parties, { id: group._id })) { isInvited = 'party'; - } else if (_.findIndex(member.invitations.guilds, {id: group._id}) !== -1) { + } else if (_.findIndex(member.invitations.guilds, { id: group._id }) !== -1) { isInvited = 'guild'; } @@ -907,7 +916,7 @@ api.removeGroupMember = { throw new NotFound(res.t('groupMemberNotFound')); } - let message = req.query.message || req.body.message; + const message = req.query.message || req.body.message; _sendMessageToRemoved(group, member, message, isInGroup); await Promise.all([ @@ -915,7 +924,7 @@ api.removeGroupMember = { group.save(), ]); - if (isInGroup && group.hasNotCancelled()) { + if (isInGroup && group.hasNotCancelled()) { await group.updateGroupPlan(true); await payments.cancelGroupSubscriptionForUser(member, group, true); } @@ -1010,7 +1019,7 @@ api.inviteToGroup = { url: '/groups/:groupId/invite', middlewares: [authWithHeaders()], async handler (req, res) { - const user = res.locals.user; + const { user } = res.locals; if (user.flags.chatRevoked) throw new NotAuthorized(res.t('chatPrivilegesRevoked')); @@ -1021,7 +1030,7 @@ api.inviteToGroup = { const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - const group = await Group.getGroup({user, groupId: req.params.groupId, fields: '-chat'}); + const group = await Group.getGroup({ user, groupId: req.params.groupId, fields: '-chat' }); if (!group) throw new NotFound(res.t('groupNotFound')); if (group.purchased && group.purchased.plan.customerId && user._id !== group.leader) throw new NotAuthorized(res.t('onlyGroupLeaderCanInviteToGroupPlan')); @@ -1041,13 +1050,13 @@ api.inviteToGroup = { const results = []; if (uuids) { - const uuidInvites = uuids.map((uuid) => inviteByUUID(uuid, group, user, req, res)); + const uuidInvites = uuids.map(uuid => inviteByUUID(uuid, group, user, req, res)); const uuidResults = await Promise.all(uuidInvites); results.push(...uuidResults); } if (emails) { - const emailInvites = emails.map((invite) => inviteByEmail(invite, group, user, req, res)); + const emailInvites = emails.map(invite => inviteByEmail(invite, group, user, req, res)); user.invitesSent += emails.length; await user.save(); const emailResults = await Promise.all(emailInvites); @@ -1055,12 +1064,12 @@ api.inviteToGroup = { } if (usernames) { - const usernameInvites = usernames.map((username) => inviteByUserName(username, group, user, req, res)); + const usernameInvites = usernames.map(username => inviteByUserName(username, group, user, req, res)); const usernameResults = await Promise.all(usernameInvites); results.push(...usernameResults); } - let analyticsObject = { + const analyticsObject = { uuid: user._id, hitType: 'event', category: 'behavior', @@ -1096,23 +1105,23 @@ api.addGroupManager = { url: '/groups/:groupId/add-manager', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let managerId = req.body.managerId; + const { user } = res.locals; + const { managerId } = req.body; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); // .isUUID(); can't be used because it would block 'habitrpg' or 'party' req.checkBody('managerId', apiError('managerIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let newManager = await User.findById(managerId, 'guilds party').exec(); - let groupFields = basicGroupFields.concat(' managers'); - let group = await Group.getGroup({user, groupId: req.params.groupId, fields: groupFields}); + const newManager = await User.findById(managerId, 'guilds party').exec(); + const groupFields = basicGroupFields.concat(' managers'); + const group = await Group.getGroup({ user, groupId: req.params.groupId, fields: groupFields }); if (!group) throw new NotFound(res.t('groupNotFound')); if (group.leader !== user._id) throw new NotAuthorized(res.t('messageGroupOnlyLeaderCanUpdate')); - let isMember = group.isMember(newManager); + const isMember = group.isMember(newManager); if (!isMember) throw new NotAuthorized(res.t('userMustBeMember')); group.managers[managerId] = true; @@ -1145,17 +1154,17 @@ api.removeGroupManager = { url: '/groups/:groupId/remove-manager', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let managerId = req.body.managerId; + const { user } = res.locals; + const { managerId } = req.body; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); // .isUUID(); can't be used because it would block 'habitrpg' or 'party' req.checkBody('managerId', apiError('managerIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let groupFields = basicGroupFields.concat(' managers'); - let group = await Group.getGroup({user, groupId: req.params.groupId, fields: groupFields}); + const groupFields = basicGroupFields.concat(' managers'); + const group = await Group.getGroup({ user, groupId: req.params.groupId, fields: groupFields }); if (!group) throw new NotFound(res.t('groupNotFound')); if (group.leader !== user._id) throw new NotAuthorized(res.t('messageGroupOnlyLeaderCanUpdate')); @@ -1166,8 +1175,8 @@ api.removeGroupManager = { group.markModified('managers'); await group.save(); - let manager = await User.findById(managerId, 'notifications').exec(); - let newNotifications = manager.notifications.filter((notification) => { + const manager = await User.findById(managerId, 'notifications').exec(); + const newNotifications = manager.notifications.filter(notification => { const isGroupTaskNotification = notification && notification.type && notification.type.indexOf('GROUP_TASK_') === 0; return !isGroupTaskNotification; @@ -1198,20 +1207,18 @@ api.getGroupPlans = { url: '/group-plans', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; const userGroups = user.getGroups(); const groups = await Group .find({ - _id: {$in: userGroups}, + _id: { $in: userGroups }, }) .select('leaderOnly leader purchased name managers') .exec(); - let groupPlans = groups.filter(group => { - return group.isSubscribed(); - }); + const groupPlans = groups.filter(group => group.isSubscribed()); res.respond(200, groupPlans); }, diff --git a/website/server/controllers/api-v3/hall.js b/website/server/controllers/api-v3/hall.js index 1c81cc25ac..e5ec6c079c 100644 --- a/website/server/controllers/api-v3/hall.js +++ b/website/server/controllers/api-v3/hall.js @@ -1,19 +1,19 @@ +import _ from 'lodash'; +import validator from 'validator'; import { authWithHeaders } from '../../middlewares/auth'; import { ensureAdmin } from '../../middlewares/ensureAccessRight'; import { model as User } from '../../models/user'; import { NotFound, } from '../../libs/errors'; -import _ from 'lodash'; import apiError from '../../libs/apiError'; -import validator from 'validator'; import { validateItemPath, castItemVal, } from '../../libs/items/utils'; -let api = {}; +const api = {}; /** * @api {get} /api/v3/hall/patrons Get all patrons @@ -69,17 +69,17 @@ api.getPatrons = { url: '/hall/patrons', middlewares: [authWithHeaders()], async handler (req, res) { - req.checkQuery('page').optional().isInt({min: 0}, apiError('queryPageInteger')); + req.checkQuery('page').optional().isInt({ min: 0 }, apiError('queryPageInteger')); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let page = req.query.page ? Number(req.query.page) : 0; + const page = req.query.page ? Number(req.query.page) : 0; const perPage = 50; - let patrons = await User + const patrons = await User .find({ - 'backer.tier': {$gt: 0}, + 'backer.tier': { $gt: 0 }, }) .select('contributor backer profile.name') .sort('-backer.tier') @@ -129,9 +129,9 @@ api.getHeroes = { url: '/hall/heroes', middlewares: [authWithHeaders()], async handler (req, res) { - let heroes = await User + const heroes = await User .find({ - 'contributor.level': {$gt: 0}, + 'contributor.level': { $gt: 0 }, }) .select('contributor backer profile.name') .sort('-contributor.level') @@ -174,13 +174,13 @@ api.getHero = { validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - const heroId = req.params.heroId; + const { heroId } = req.params; let query; if (validator.isUUID(heroId)) { - query = {_id: heroId}; + query = { _id: heroId }; } else { - query = {'auth.local.lowerCaseUsername': heroId.toLowerCase()}; + query = { 'auth.local.lowerCaseUsername': heroId.toLowerCase() }; } const hero = await User @@ -188,8 +188,8 @@ api.getHero = { .select(heroAdminFields) .exec(); - if (!hero) throw new NotFound(res.t('userWithIDNotFound', {userId: heroId})); - let heroRes = hero.toJSON({minimize: true}); + if (!hero) throw new NotFound(res.t('userWithIDNotFound', { userId: heroId })); + const heroRes = hero.toJSON({ minimize: true }); // supply to the possible absence of hero.contributor // if we didn't pass minimize: true it would have returned all fields as empty if (!heroRes.contributor) heroRes.contributor = {}; @@ -198,7 +198,9 @@ api.getHero = { }; // e.g., tier 5 gives 4 gems. Tier 8 = moderator. Tier 9 = staff -const gemsPerTier = {1: 3, 2: 3, 3: 3, 4: 4, 5: 4, 6: 4, 7: 4, 8: 0, 9: 0}; +const gemsPerTier = { + 1: 3, 2: 3, 3: 3, 4: 4, 5: 4, 6: 4, 7: 4, 8: 0, 9: 0, +}; /** * @api {put} /api/v3/hall/heroes/:heroId Update any user ("hero") @@ -240,22 +242,22 @@ api.updateHero = { url: '/hall/heroes/:heroId', middlewares: [authWithHeaders(), ensureAdmin], async handler (req, res) { - let heroId = req.params.heroId; - let updateData = req.body; + const { heroId } = req.params; + const updateData = req.body; req.checkParams('heroId', res.t('heroIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let hero = await User.findById(heroId).exec(); - if (!hero) throw new NotFound(res.t('userWithIDNotFound', {userId: heroId})); + const hero = await User.findById(heroId).exec(); + if (!hero) throw new NotFound(res.t('userWithIDNotFound', { userId: heroId })); if (updateData.balance) hero.balance = updateData.balance; // give them gems if they got an higher level let newTier = updateData.contributor && updateData.contributor.level; // tier = level in this context - let oldTier = hero.contributor && hero.contributor.level || 0; + const oldTier = hero.contributor && hero.contributor.level || 0; if (newTier > oldTier) { hero.flags.contributor = true; let tierDiff = newTier - oldTier; // can be 2+ tier increases at once @@ -291,9 +293,9 @@ api.updateHero = { if (updateData.flags && _.isBoolean(updateData.flags.chatRevoked)) hero.flags.chatRevoked = updateData.flags.chatRevoked; if (updateData.flags && _.isBoolean(updateData.flags.chatShadowMuted)) hero.flags.chatShadowMuted = updateData.flags.chatShadowMuted; - let savedHero = await hero.save(); - let heroJSON = savedHero.toJSON(); - let responseHero = {_id: heroJSON._id}; // only respond with important fields + const savedHero = await hero.save(); + const heroJSON = savedHero.toJSON(); + const responseHero = { _id: heroJSON._id }; // only respond with important fields heroAdminFields.split(' ').forEach(field => { _.set(responseHero, field, _.get(heroJSON, field)); }); diff --git a/website/server/controllers/api-v3/i18n.js b/website/server/controllers/api-v3/i18n.js index 63e6e54450..76a7d7ecd1 100644 --- a/website/server/controllers/api-v3/i18n.js +++ b/website/server/controllers/api-v3/i18n.js @@ -1,9 +1,9 @@ +import _ from 'lodash'; import { translations, momentLangs, availableLanguages, } from '../../libs/i18n'; -import _ from 'lodash'; const api = {}; @@ -32,7 +32,7 @@ api.geti18nBrowserScript = { method: 'GET', url: '/i18n/browser-script', async handler (req, res) { - const language = _.find(availableLanguages, {code: req.language}); + const language = _.find(availableLanguages, { code: req.language }); res.set({ 'Content-Type': 'application/javascript', diff --git a/website/server/controllers/api-v3/iap.js b/website/server/controllers/api-v3/iap.js index 0d07063f88..6512bf9dff 100644 --- a/website/server/controllers/api-v3/iap.js +++ b/website/server/controllers/api-v3/iap.js @@ -2,4 +2,5 @@ // to be found at /api/v3/iap instead of /iap. import iap from '../top-level/payments/iap'; + export default iap; diff --git a/website/server/controllers/api-v3/inbox.js b/website/server/controllers/api-v3/inbox.js index a6ad1d1410..0702fd0979 100644 --- a/website/server/controllers/api-v3/inbox.js +++ b/website/server/controllers/api-v3/inbox.js @@ -1,7 +1,7 @@ import { authWithHeaders } from '../../middlewares/auth'; import * as inboxLib from '../../libs/inbox'; -let api = {}; +const api = {}; /* NOTE most inbox routes are either in the user or members controller */ @@ -21,9 +21,9 @@ api.getInboxMessages = { url: '/inbox/messages', middlewares: [authWithHeaders()], async handler (req, res) { - const user = res.locals.user; - const page = req.query.page; - const conversation = req.query.conversation; + const { user } = res.locals; + const { page } = req.query; + const { conversation } = req.query; const userInbox = await inboxLib.getUserInbox(user, { page, conversation, diff --git a/website/server/controllers/api-v3/members.js b/website/server/controllers/api-v3/members.js index 2c90a12a47..d10fd2baa2 100644 --- a/website/server/controllers/api-v3/members.js +++ b/website/server/controllers/api-v3/members.js @@ -19,12 +19,12 @@ import { sendTxn as sendTxnEmail, } from '../../libs/email'; import { sendNotification as sendPushNotification } from '../../libs/pushNotifications'; -import common from '../../../../website/common/'; -import {sentMessage} from '../../libs/inbox'; +import common from '../../../common'; +import { sentMessage } from '../../libs/inbox'; -const achievements = common.achievements; +const { achievements } = common; -let api = {}; +const api = {}; /** * @api {get} /api/v3/members/:memberId Get a member profile @@ -100,22 +100,22 @@ api.getMember = { async handler (req, res) { req.checkParams('memberId', res.t('memberIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let memberId = req.params.memberId; + const { memberId } = req.params; - let member = await User + const member = await User .findById(memberId) .select(memberFields) .exec(); - if (!member) throw new NotFound(res.t('userWithIDNotFound', {userId: memberId})); + if (!member) throw new NotFound(res.t('userWithIDNotFound', { userId: memberId })); if (!member.flags.verifiedUsername) member.auth.local.username = null; // manually call toJSON with minimize: true so empty paths aren't returned - let memberToJSON = member.toJSON({minimize: true}); + const memberToJSON = member.toJSON({ minimize: true }); User.addComputedStatsToJSONObj(memberToJSON.stats, member); res.respond(200, memberToJSON); @@ -129,21 +129,21 @@ api.getMemberByUsername = { async handler (req, res) { req.checkParams('username', res.t('invalidReqParams')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; let username = req.params.username.toLowerCase(); if (username[0] === '@') username = username.slice(1, username.length); - let member = await User - .findOne({'auth.local.lowerCaseUsername': username, 'flags.verifiedUsername': true}) + const member = await User + .findOne({ 'auth.local.lowerCaseUsername': username, 'flags.verifiedUsername': true }) .select(memberFields) .exec(); if (!member) throw new NotFound(res.t('userNotFound')); // manually call toJSON with minimize: true so empty paths aren't returned - let memberToJSON = member.toJSON({minimize: true}); + const memberToJSON = member.toJSON({ minimize: true }); User.addComputedStatsToJSONObj(memberToJSON.stats, member); res.respond(200, memberToJSON); @@ -239,19 +239,19 @@ api.getMemberAchievements = { async handler (req, res) { req.checkParams('memberId', res.t('memberIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let memberId = req.params.memberId; + const { memberId } = req.params; - let member = await User + const member = await User .findById(memberId) .select(memberFields) .exec(); - if (!member) throw new NotFound(res.t('userWithIDNotFound', {userId: memberId})); + if (!member) throw new NotFound(res.t('userWithIDNotFound', { userId: memberId })); - let achievsObject = achievements.getAchievementsForProfile(member, req.language); + const achievsObject = achievements.getAchievementsForProfile(member, req.language); res.respond(200, achievsObject); }, @@ -274,13 +274,13 @@ function _getMembersForItem (type) { } req.checkQuery('lastId').optional().notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let groupId = req.params.groupId; - let challengeId = req.params.challengeId; - let lastId = req.query.lastId; - let user = res.locals.user; + const { groupId } = req.params; + const { challengeId } = req.params; + const { lastId } = req.query; + const { user } = res.locals; let challenge; let group; @@ -299,11 +299,11 @@ function _getMembersForItem (type) { if (!group || !challenge.canView(user, group)) throw new NotFound(res.t('challengeNotFound')); } else { - group = await Group.getGroup({user, groupId, fields: '_id type'}); + group = await Group.getGroup({ user, groupId, fields: '_id type' }); if (!group) throw new NotFound(res.t('groupNotFound')); } - let query = {}; + const query = {}; let fields = nameFields; let addComputedStats = false; // add computes stats to the member info when items and stats are available @@ -316,7 +316,7 @@ function _getMembersForItem (type) { } if (req.query.search) { - query['profile.name'] = {$regex: req.query.search}; + query['profile.name'] = { $regex: req.query.search }; } } else if (type === 'group-members') { if (group.type === 'guild') { @@ -357,7 +357,7 @@ function _getMembersForItem (type) { } } - if (lastId) query._id = {$gt: lastId}; + if (lastId) query._id = { $gt: lastId }; let limit = 30; @@ -366,9 +366,9 @@ function _getMembersForItem (type) { limit = 0; // no limit } - let members = await User + const members = await User .find(query) - .sort({_id: 1}) + .sort({ _id: 1 }) .limit(limit) .select(fields) .lean() @@ -541,26 +541,28 @@ api.getChallengeMemberProgress = { req.checkParams('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID(); req.checkParams('memberId', res.t('memberIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let user = res.locals.user; - let challengeId = req.params.challengeId; - let memberId = req.params.memberId; + const { user } = res.locals; + const { challengeId } = req.params; + const { memberId } = req.params; - let member = await User.findById(memberId).select(`${nameFields} challenges`).exec(); - if (!member) throw new NotFound(res.t('userWithIDNotFound', {userId: memberId})); + const member = await User.findById(memberId).select(`${nameFields} challenges`).exec(); + if (!member) throw new NotFound(res.t('userWithIDNotFound', { userId: memberId })); - let challenge = await Challenge.findById(challengeId).exec(); + const challenge = await Challenge.findById(challengeId).exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); // optionalMembership is set to true because even if you're not member of the group you may be able to access the challenge // for example if you've been booted from it, are the leader or a site admin - let group = await Group.getGroup({user, groupId: challenge.group, fields: '_id type privacy', optionalMembership: true}); + const group = await Group.getGroup({ + user, groupId: challenge.group, fields: '_id type privacy', optionalMembership: true, + }); if (!group || !challenge.canView(user, group)) throw new NotFound(res.t('challengeNotFound')); if (!challenge.isMember(member)) throw new NotFound(res.t('challengeMemberNotFound')); - let chalTasks = await Tasks.Task.find({ + const chalTasks = await Tasks.Task.find({ userId: memberId, 'challenge.id': challengeId, }) @@ -568,11 +570,11 @@ api.getChallengeMemberProgress = { .exec(); // manually call toJSON with minimize: true so empty paths aren't returned - let response = member.toJSON({minimize: true}); + const response = member.toJSON({ minimize: true }); delete response.challenges; response.tasks = chalTasks.map(chalTask => { chalTask.checklist = []; // Clear checklists as they are private - return chalTask.toJSON({minimize: true}); + return chalTask.toJSON({ minimize: true }); }); res.respond(200, response); }, @@ -597,15 +599,15 @@ api.getObjectionsToInteraction = { req.checkParams('toUserId', res.t('toUserIDRequired')).notEmpty().isUUID(); req.checkParams('interaction', res.t('interactionRequired')).notEmpty().isIn(KNOWN_INTERACTIONS); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let sender = res.locals.user; - let receiver = await User.findById(req.params.toUserId).exec(); - if (!receiver) throw new NotFound(res.t('userWithIDNotFound', {userId: req.params.toUserId})); + const sender = res.locals.user; + const receiver = await User.findById(req.params.toUserId).exec(); + if (!receiver) throw new NotFound(res.t('userWithIDNotFound', { userId: req.params.toUserId })); - let interaction = req.params.interaction; - let response = sender.getObjectionsToInteraction(interaction, receiver); + const { interaction } = req.params; + const response = sender.getObjectionsToInteraction(interaction, receiver); res.respond(200, response.map(res.t)); }, @@ -635,7 +637,7 @@ api.sendPrivateMessage = { if (validationErrors) throw validationErrors; const sender = res.locals.user; - const message = req.body.message; + const { message } = req.body; const receiver = await User.findById(req.body.toUserId).exec(); if (!receiver) throw new NotFound(res.t('userNotFound')); @@ -646,7 +648,7 @@ api.sendPrivateMessage = { const messageSent = await sentMessage(sender, receiver, message, res.t); - res.respond(200, {message: messageSent}); + res.respond(200, { message: messageSent }); }, }; @@ -671,18 +673,18 @@ api.transferGems = { req.checkBody('toUserId', res.t('toUserIDRequired')).notEmpty().isUUID(); req.checkBody('gemAmount', res.t('gemAmountRequired')).notEmpty().isInt(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let sender = res.locals.user; - let receiver = await User.findById(req.body.toUserId).exec(); + const sender = res.locals.user; + const receiver = await User.findById(req.body.toUserId).exec(); if (!receiver) throw new NotFound(res.t('userNotFound')); - let objections = sender.getObjectionsToInteraction('transfer-gems', receiver); + const objections = sender.getObjectionsToInteraction('transfer-gems', receiver); if (objections.length > 0) throw new NotAuthorized(res.t(objections[0])); - let gemAmount = req.body.gemAmount; - let amount = gemAmount / 4; + const { gemAmount } = req.body; + const amount = gemAmount / 4; if (amount <= 0 || sender.balance < amount) { throw new NotAuthorized(res.t('badAmountOfGemsToSend')); @@ -691,13 +693,13 @@ api.transferGems = { receiver.balance += amount; sender.balance -= amount; // @TODO necessary? Also saved when sending the inbox message - let promises = [receiver.save(), sender.save()]; + const promises = [receiver.save(), sender.save()]; await Promise.all(promises); // generate the message in both languages, so both users can understand it - let receiverLang = receiver.preferences.language; - let senderLang = sender.preferences.language; - let [receiverMsg, senderMsg] = [receiverLang, senderLang].map((lang) => { + const receiverLang = receiver.preferences.language; + const senderLang = sender.preferences.language; + const [receiverMsg, senderMsg] = [receiverLang, senderLang].map(lang => { let messageContent = res.t('privateMessageGiftGemsMessage', { receiverName: receiver.profile.name, senderName: sender.profile.name, @@ -716,21 +718,21 @@ api.transferGems = { receiverMsg, }); - let byUsername = getUserInfo(sender, ['name']).name; + const byUsername = getUserInfo(sender, ['name']).name; if (receiver.preferences.emailNotifications.giftedGems !== false) { sendTxnEmail(receiver, 'gifted-gems', [ - {name: 'GIFTER', content: byUsername}, - {name: 'X_GEMS_GIFTED', content: gemAmount}, + { name: 'GIFTER', content: byUsername }, + { name: 'X_GEMS_GIFTED', content: gemAmount }, ]); } if (receiver.preferences.pushNotifications.giftedGems !== false) { sendPushNotification(receiver, { title: res.t('giftedGems', receiverLang), - message: res.t('giftedGemsInfo', {amount: gemAmount, name: byUsername}, receiverLang), + message: res.t('giftedGemsInfo', { amount: gemAmount, name: byUsername }, receiverLang), identifier: 'giftedGems', - payload: {replyTo: sender._id}, + payload: { replyTo: sender._id }, }); } diff --git a/website/server/controllers/api-v3/modelsPaths.js b/website/server/controllers/api-v3/modelsPaths.js index 56ae235986..d98ad7841f 100644 --- a/website/server/controllers/api-v3/modelsPaths.js +++ b/website/server/controllers/api-v3/modelsPaths.js @@ -1,9 +1,9 @@ import mongoose from 'mongoose'; -let api = {}; +const api = {}; -let tasksModels = ['habit', 'daily', 'todo', 'reward']; -let allModels = ['user', 'tag', 'challenge', 'group'].concat(tasksModels); +const tasksModels = ['habit', 'daily', 'todo', 'reward']; +const allModels = ['user', 'tag', 'challenge', 'group'].concat(tasksModels); /** * @api {get} /api/v3/models/:model/paths Get all paths for the specified model @@ -36,10 +36,10 @@ api.getModelPaths = { async handler (req, res) { req.checkParams('model', res.t('modelNotFound')).notEmpty().isIn(allModels); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let model = req.params.model; + let { model } = req.params; // tasks models are lowercase, the others have the first letter uppercase (User, Group) if (tasksModels.indexOf(model) === -1) { model = model.charAt(0).toUpperCase() + model.slice(1); diff --git a/website/server/controllers/api-v3/news.js b/website/server/controllers/api-v3/news.js index 09e6c4e5bf..8196d3dac2 100644 --- a/website/server/controllers/api-v3/news.js +++ b/website/server/controllers/api-v3/news.js @@ -1,6 +1,6 @@ import { authWithHeaders } from '../../middlewares/auth'; -let api = {}; +const api = {}; // @TODO export this const, cannot export it from here because only routes are exported from controllers const LAST_ANNOUNCEMENT_TITLE = 'SPOOKY SPARKLES AND COSTUME CHALLENGE!'; @@ -65,13 +65,11 @@ api.tellMeLaterNews = { middlewares: [authWithHeaders()], url: '/news/tell-me-later', async handler (req, res) { - const user = res.locals.user; + const { user } = res.locals; user.flags.newStuff = false; - const existingNotificationIndex = user.notifications.findIndex(n => { - return n && n.type === 'NEW_STUFF'; - }); + const existingNotificationIndex = user.notifications.findIndex(n => n && n.type === 'NEW_STUFF'); if (existingNotificationIndex !== -1) user.notifications.splice(existingNotificationIndex, 1); user.addNotification('NEW_STUFF', { title: LAST_ANNOUNCEMENT_TITLE }, true); // seen by default diff --git a/website/server/controllers/api-v3/notifications.js b/website/server/controllers/api-v3/notifications.js index 45d92d0f77..f188ec66e3 100644 --- a/website/server/controllers/api-v3/notifications.js +++ b/website/server/controllers/api-v3/notifications.js @@ -9,7 +9,7 @@ import { model as UserNotification, } from '../../models/userNotification'; -let api = {}; +const api = {}; /** * @api {post} /api/v3/notifications/:notificationId/read Mark one notification as read @@ -25,16 +25,14 @@ api.readNotification = { url: '/notifications/:notificationId/read', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('notificationId', res.t('notificationIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - const index = user.notifications.findIndex(n => { - return n && n.id === req.params.notificationId; - }); + const index = user.notifications.findIndex(n => n && n.id === req.params.notificationId); if (index === -1) { throw new NotificationNotFound(req.language); @@ -67,18 +65,16 @@ api.readNotifications = { url: '/notifications/read', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkBody('notificationIds', res.t('notificationsRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let notificationsIds = req.body.notificationIds; - for (let notificationId of notificationsIds) { - const index = user.notifications.findIndex(n => { - return n && n.id === notificationId; - }); + const notificationsIds = req.body.notificationIds; + for (const notificationId of notificationsIds) { + const index = user.notifications.findIndex(n => n && n.id === notificationId); if (index === -1) { throw new NotificationNotFound(req.language); @@ -115,18 +111,16 @@ api.seeNotification = { url: '/notifications/:notificationId/see', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('notificationId', res.t('notificationIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - const notificationId = req.params.notificationId; + const { notificationId } = req.params; - const notification = user.notifications.find(n => { - return n && n.id === notificationId; - }); + const notification = user.notifications.find(n => n && n.id === notificationId); if (!notification) { throw new NotificationNotFound(req.language); @@ -164,19 +158,17 @@ api.seeNotifications = { url: '/notifications/see', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkBody('notificationIds', res.t('notificationsRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let notificationsIds = req.body.notificationIds; + const notificationsIds = req.body.notificationIds; - for (let notificationId of notificationsIds) { - const notification = user.notifications.find(n => { - return n && n.id === notificationId; - }); + for (const notificationId of notificationsIds) { + const notification = user.notifications.find(n => n && n.id === notificationId); if (!notification) { throw new NotificationNotFound(req.language); diff --git a/website/server/controllers/api-v3/pushNotifications.js b/website/server/controllers/api-v3/pushNotifications.js index 64400345d9..3ee0d9c592 100644 --- a/website/server/controllers/api-v3/pushNotifications.js +++ b/website/server/controllers/api-v3/pushNotifications.js @@ -4,7 +4,7 @@ import { } from '../../libs/errors'; import { model as PushDevice } from '../../models/pushDevice'; -let api = {}; +const api = {}; /** * @apiIgnore @@ -23,7 +23,7 @@ api.addPushDevice = { url: '/user/push-devices', middlewares: [authWithHeaders()], async handler (req, res) { - const user = res.locals.user; + const { user } = res.locals; req.checkBody('regId', res.t('regIdRequired')).notEmpty(); req.checkBody('type', res.t('typeRequired')).notEmpty().isIn(['ios', 'android']); @@ -31,7 +31,7 @@ api.addPushDevice = { const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - const pushDevices = user.pushDevices; + const { pushDevices } = user; const item = { regId: req.body.regId, @@ -73,20 +73,18 @@ api.removePushDevice = { url: '/user/push-devices/:regId', middlewares: [authWithHeaders()], async handler (req, res) { - const user = res.locals.user; + const { user } = res.locals; req.checkParams('regId', res.t('regIdRequired')).notEmpty(); const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - const regId = req.params.regId; + const { regId } = req.params; - const pushDevices = user.pushDevices; + const { pushDevices } = user; - const indexOfPushDevice = pushDevices.findIndex((element) => { - return element.regId === regId; - }); + const indexOfPushDevice = pushDevices.findIndex(element => element.regId === regId); if (indexOfPushDevice === -1) { throw new NotFound(res.t('pushDeviceNotFound')); diff --git a/website/server/controllers/api-v3/quests.js b/website/server/controllers/api-v3/quests.js index 0fd401354d..d882ebf697 100644 --- a/website/server/controllers/api-v3/quests.js +++ b/website/server/controllers/api-v3/quests.js @@ -21,7 +21,7 @@ import apiError from '../../libs/apiError'; const questScrolls = common.content.quests; -function canStartQuestAutomatically (group) { +function canStartQuestAutomatically (group) { // If all members are either true (accepted) or false (rejected) return true // If any member is null/undefined (undecided) return false return _.every(group.quest.members, _.isBoolean); @@ -37,7 +37,7 @@ function canStartQuestAutomatically (group) { * The quest leader can use this route. */ -let api = {}; +const api = {}; /** * @api {post} /api/v3/groups/:groupId/quests/invite/:questKey Invite users to a quest @@ -57,16 +57,16 @@ api.inviteToQuest = { url: '/groups/:groupId/quests/invite/:questKey', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let questKey = req.params.questKey; - let quest = questScrolls[questKey]; + const { user } = res.locals; + const { questKey } = req.params; + const quest = questScrolls[questKey]; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let group = await Group.getGroup({user, groupId: req.params.groupId, fields: basicGroupFields.concat(' quest chat')}); + const group = await Group.getGroup({ user, groupId: req.params.groupId, fields: basicGroupFields.concat(' quest chat') }); if (!group) throw new NotFound(res.t('groupNotFound')); if (group.type !== 'party') throw new NotAuthorized(res.t('guildQuestsNotSupported')); @@ -75,9 +75,9 @@ api.inviteToQuest = { if (user.stats.lvl < quest.lvl) throw new NotAuthorized(res.t('questLevelTooHigh', { level: quest.lvl })); if (group.quest.key) throw new NotAuthorized(res.t('questAlreadyUnderway')); - let members = await User.find({ + const members = await User.find({ 'party._id': group._id, - _id: {$ne: user._id}, + _id: { $ne: user._id }, }) .select('auth.facebook auth.google auth.local preferences.emailNotifications preferences.pushNotifications preferences.language profile.name pushDevices') .exec(); @@ -93,15 +93,15 @@ api.inviteToQuest = { await User.update({ 'party._id': group._id, - _id: {$ne: user._id}, + _id: { $ne: user._id }, }, { $set: { 'party.quest.RSVPNeeded': true, 'party.quest.key': questKey, }, - }, {multi: true}).exec(); + }, { multi: true }).exec(); - _.each(members, (member) => { + _.each(members, member => { group.quest.members[member._id] = null; }); @@ -109,7 +109,7 @@ api.inviteToQuest = { await group.startQuest(user); } - let [savedGroup] = await Promise.all([ + const [savedGroup] = await Promise.all([ group.save(), user.save(), ]); @@ -117,8 +117,8 @@ api.inviteToQuest = { res.respond(200, savedGroup.quest); // send out invites - let inviterVars = getUserInfo(user, ['name', 'email']); - let membersToEmail = members.filter(member => { + const inviterVars = getUserInfo(user, ['name', 'email']); + const membersToEmail = members.filter(member => { // send push notifications while filtering members before sending emails if (member.preferences.pushNotifications.invitedQuest !== false) { sendPushNotification( @@ -128,16 +128,16 @@ api.inviteToQuest = { message: res.t('questInvitationNotificationInfo', member.preferences.language), identifier: 'questInvitation', category: 'questInvitation', - } + }, ); } return member.preferences.emailNotifications.invitedQuest !== false; }); sendTxnEmail(membersToEmail, `invite-${quest.boss ? 'boss' : 'collection'}-quest`, [ - {name: 'QUEST_NAME', content: quest.text()}, - {name: 'INVITER', content: inviterVars.name}, - {name: 'PARTY_URL', content: '/party'}, + { name: 'QUEST_NAME', content: quest.text() }, + { name: 'INVITER', content: inviterVars.name }, + { name: 'PARTY_URL', content: '/party' }, ]); // track that the inviting user has accepted the quest @@ -170,14 +170,14 @@ api.acceptQuest = { url: '/groups/:groupId/quests/accept', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let group = await Group.getGroup({user, groupId: req.params.groupId, fields: basicGroupFields.concat(' quest chat')}); + const group = await Group.getGroup({ user, groupId: req.params.groupId, fields: basicGroupFields.concat(' quest chat') }); if (!group) throw new NotFound(res.t('groupNotFound')); if (group.type !== 'party') throw new NotAuthorized(res.t('guildQuestsNotSupported')); @@ -195,7 +195,7 @@ api.acceptQuest = { await group.startQuest(user); } - let savedGroup = await group.save(); + const savedGroup = await group.save(); res.respond(200, savedGroup.quest); @@ -229,14 +229,14 @@ api.rejectQuest = { url: '/groups/:groupId/quests/reject', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let group = await Group.getGroup({user, groupId: req.params.groupId, fields: basicGroupFields.concat(' quest chat')}); + const group = await Group.getGroup({ user, groupId: req.params.groupId, fields: basicGroupFields.concat(' quest chat') }); if (!group) throw new NotFound(res.t('groupNotFound')); if (group.type !== 'party') throw new NotAuthorized(res.t('guildQuestsNotSupported')); if (!group.quest.key) throw new NotFound(res.t('questInvitationDoesNotExist')); @@ -255,7 +255,7 @@ api.rejectQuest = { await group.startQuest(user); } - let savedGroup = await group.save(); + const savedGroup = await group.save(); res.respond(200, savedGroup.quest); @@ -292,14 +292,14 @@ api.forceStart = { url: '/groups/:groupId/quests/force-start', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let group = await Group.getGroup({user, groupId: req.params.groupId, fields: basicGroupFields.concat(' quest chat')}); + const group = await Group.getGroup({ user, groupId: req.params.groupId, fields: basicGroupFields.concat(' quest chat') }); if (!group) throw new NotFound(res.t('groupNotFound')); if (group.type !== 'party') throw new NotAuthorized(res.t('guildQuestsNotSupported')); @@ -311,7 +311,7 @@ api.forceStart = { await group.startQuest(user); - let [savedGroup] = await Promise.all([ + const [savedGroup] = await Promise.all([ group.save(), user.save(), ]); @@ -353,15 +353,15 @@ api.cancelQuest = { // Cancel a quest BEFORE it has begun (i.e., in the invitation stage) // Quest scroll has not yet left quest owner's inventory so no need to return it. // Do not wipe quest progress for members because they'll want it to be applied to the next quest that's started. - let user = res.locals.user; - let groupId = req.params.groupId; + const { user } = res.locals; + const { groupId } = req.params; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let group = await Group.getGroup({user, groupId, fields: basicGroupFields.concat(' quest')}); + const group = await Group.getGroup({ user, groupId, fields: basicGroupFields.concat(' quest') }); if (!group) throw new NotFound(res.t('groupNotFound')); if (group.type !== 'party') throw new NotAuthorized(res.t('guildQuestsNotSupported')); @@ -369,7 +369,7 @@ api.cancelQuest = { if (user._id !== group.leader && group.quest.leader !== user._id) throw new NotAuthorized(res.t('onlyLeaderCancelQuest')); if (group.quest.active) throw new NotAuthorized(res.t('cantCancelActiveQuest')); - let questName = questScrolls[group.quest.key].text('en'); + const questName = questScrolls[group.quest.key].text('en'); const newChatMessage = group.sendChat({ message: `\`${user.profile.name} cancelled the party quest ${questName}.\``, info: { @@ -382,13 +382,13 @@ api.cancelQuest = { group.quest = Group.cleanGroupQuest(); group.markModified('quest'); - let [savedGroup] = await Promise.all([ + const [savedGroup] = await Promise.all([ group.save(), newChatMessage.save(), User.update( - {'party._id': groupId}, + { 'party._id': groupId }, Group.cleanQuestParty(), - {multi: true} + { multi: true }, ).exec(), ]); @@ -417,24 +417,24 @@ api.abortQuest = { middlewares: [authWithHeaders()], async handler (req, res) { // Abort a quest AFTER it has begun - let user = res.locals.user; - let groupId = req.params.groupId; + const { user } = res.locals; + const { groupId } = req.params; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let group = await Group.getGroup({user, groupId, fields: basicGroupFields.concat(' quest chat')}); + const group = await Group.getGroup({ user, groupId, fields: basicGroupFields.concat(' quest chat') }); if (!group) throw new NotFound(res.t('groupNotFound')); if (group.type !== 'party') throw new NotAuthorized(res.t('guildQuestsNotSupported')); if (!group.quest.active) throw new NotFound(res.t('noActiveQuestToAbort')); if (user._id !== group.leader && user._id !== group.quest.leader) throw new NotAuthorized(res.t('onlyLeaderAbortQuest')); - let questName = questScrolls[group.quest.key].text('en'); + const questName = questScrolls[group.quest.key].text('en'); const newChatMessage = group.sendChat({ - message: `\`${common.i18n.t('chatQuestAborted', {username: user.profile.name, questName}, 'en')}\``, + message: `\`${common.i18n.t('chatQuestAborted', { username: user.profile.name, questName }, 'en')}\``, info: { type: 'quest_abort', user: user.profile.name, @@ -443,12 +443,12 @@ api.abortQuest = { }); await newChatMessage.save(); - let memberUpdates = User.update({ + const memberUpdates = User.update({ 'party._id': groupId, }, Group.cleanQuestParty(), - {multi: true}).exec(); + { multi: true }).exec(); - let questLeaderUpdate = User.update({ + const questLeaderUpdate = User.update({ _id: group.quest.leader, }, { $inc: { @@ -459,7 +459,7 @@ api.abortQuest = { group.quest = Group.cleanGroupQuest(); group.markModified('quest'); - let [groupSaved] = await Promise.all([group.save(), memberUpdates, questLeaderUpdate]); + const [groupSaved] = await Promise.all([group.save(), memberUpdates, questLeaderUpdate]); res.respond(200, groupSaved.quest); }, @@ -482,15 +482,15 @@ api.leaveQuest = { url: '/groups/:groupId/quests/leave', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let groupId = req.params.groupId; + const { user } = res.locals; + const { groupId } = req.params; req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let group = await Group.getGroup({user, groupId, fields: basicGroupFields.concat(' quest')}); + const group = await Group.getGroup({ user, groupId, fields: basicGroupFields.concat(' quest') }); if (!group) throw new NotFound(res.t('groupNotFound')); if (group.type !== 'party') throw new NotAuthorized(res.t('guildQuestsNotSupported')); @@ -504,7 +504,7 @@ api.leaveQuest = { user.party.quest = Group.cleanQuestUser(user.party.quest.progress); user.markModified('party.quest'); - let [savedGroup] = await Promise.all([ + const [savedGroup] = await Promise.all([ group.save(), user.save(), ]); diff --git a/website/server/controllers/api-v3/shops.js b/website/server/controllers/api-v3/shops.js index b4e3c6d568..f94129cb1f 100644 --- a/website/server/controllers/api-v3/shops.js +++ b/website/server/controllers/api-v3/shops.js @@ -1,9 +1,9 @@ import { authWithHeaders } from '../../middlewares/auth'; -import common from '../../../common/'; +import common from '../../../common'; -const shops = common.shops; +const { shops } = common; -let api = {}; +const api = {}; /** * @apiIgnore @@ -19,9 +19,9 @@ api.getMarketItems = { url: '/shops/market', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; - let resObject = shops.getMarketShop(user, req.language); + const resObject = shops.getMarketShop(user, req.language); res.respond(200, resObject); }, @@ -40,9 +40,9 @@ api.getMarketGear = { url: '/shops/market-gear', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; - let resObject = { + const resObject = { categories: shops.getMarketGearCategories(user, req.language), }; @@ -64,9 +64,9 @@ api.getQuestShopItems = { url: '/shops/quests', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; - let resObject = shops.getQuestShop(user, req.language); + const resObject = shops.getQuestShop(user, req.language); res.respond(200, resObject); }, @@ -86,9 +86,9 @@ api.getTimeTravelerShopItems = { url: '/shops/time-travelers', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; - let resObject = shops.getTimeTravelersShop(user, req.language); + const resObject = shops.getTimeTravelersShop(user, req.language); res.respond(200, resObject); }, @@ -108,9 +108,9 @@ api.getSeasonalShopItems = { url: '/shops/seasonal', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; - let resObject = shops.getSeasonalShop(user, req.language); + const resObject = shops.getSeasonalShop(user, req.language); res.respond(200, resObject); }, @@ -130,9 +130,9 @@ api.getBackgroundShopItems = { url: '/shops/backgrounds', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; - let resObject = { + const resObject = { identifier: 'backgroundShop', text: res.t('backgroundShop'), notes: res.t('backgroundShopText'), diff --git a/website/server/controllers/api-v3/status.js b/website/server/controllers/api-v3/status.js index ab2c764d5c..c957388d59 100644 --- a/website/server/controllers/api-v3/status.js +++ b/website/server/controllers/api-v3/status.js @@ -1,4 +1,4 @@ -let api = {}; +const api = {}; /** * @api {get} /api/v3/status Get Habitica's API status diff --git a/website/server/controllers/api-v3/tags.js b/website/server/controllers/api-v3/tags.js index 55ecc251e5..02665d2114 100644 --- a/website/server/controllers/api-v3/tags.js +++ b/website/server/controllers/api-v3/tags.js @@ -1,11 +1,11 @@ +import _ from 'lodash'; +import find from 'lodash/find'; import { authWithHeaders } from '../../middlewares/auth'; import { model as Tag } from '../../models/tag'; import * as Tasks from '../../models/task'; import { NotFound, } from '../../libs/errors'; -import _ from 'lodash'; -import find from 'lodash/find'; /** * @apiDefine TagNotFound @@ -18,7 +18,7 @@ import find from 'lodash/find'; */ -let api = {}; +const api = {}; /** * @api {post} /api/v3/tags Create a new tag @@ -40,13 +40,13 @@ api.createTag = { url: '/tags', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; user.tags.push(Tag.sanitize(req.body)); - let savedUser = await user.save(); + const savedUser = await user.save(); - let l = savedUser.tags.length; - let tag = savedUser.tags[l - 1]; + const l = savedUser.tags.length; + const tag = savedUser.tags[l - 1]; res.respond(201, tag); }, }; @@ -66,7 +66,7 @@ api.getTags = { url: '/tags', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; res.respond(200, user.tags); }, }; @@ -91,14 +91,14 @@ api.getTag = { url: '/tags/:tagId', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('tagId', res.t('tagIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let tag = _.find(user.tags, {id: req.params.tagId}); + const tag = _.find(user.tags, { id: req.params.tagId }); if (!tag) throw new NotFound(res.t('tagNotFound')); res.respond(200, tag); }, @@ -128,22 +128,22 @@ api.updateTag = { url: '/tags/:tagId', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('tagId', res.t('tagIdRequired')).notEmpty().isUUID(); - let tagId = req.params.tagId; + const { tagId } = req.params; - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let tag = _.find(user.tags, {id: tagId}); + const tag = _.find(user.tags, { id: tagId }); if (!tag) throw new NotFound(res.t('tagNotFound')); _.merge(tag, Tag.sanitize(req.body)); - let savedUser = await user.save(); - res.respond(200, _.find(savedUser.tags, {id: tagId})); + const savedUser = await user.save(); + res.respond(200, _.find(savedUser.tags, { id: tagId })); }, }; @@ -170,17 +170,15 @@ api.reorderTags = { url: '/reorder-tags', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkBody('to', res.t('toRequired')).notEmpty(); req.checkBody('tagId', res.t('tagIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let tagIndex = _.findIndex(user.tags, function findTag (tag) { - return tag.id === req.body.tagId; - }); + const tagIndex = _.findIndex(user.tags, tag => tag.id === req.body.tagId); if (tagIndex === -1) throw new NotFound(res.t('tagNotFound')); const removedItem = user.tags.splice(tagIndex, 1)[0]; @@ -212,16 +210,14 @@ api.deleteTag = { url: '/tags/:tagId', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('tagId', res.t('tagIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let tagFound = find(user.tags, (tag) => { - return tag.id === req.params.tagId; - }); + const tagFound = find(user.tags, tag => tag.id === req.params.tagId); if (!tagFound) throw new NotFound(res.t('tagNotFound')); await user.update({ @@ -240,7 +236,7 @@ api.deleteTag = { $pull: { tags: tagFound.id, }, - }, {multi: true}).exec(); + }, { multi: true }).exec(); res.respond(200, {}); }, diff --git a/website/server/controllers/api-v3/tasks.js b/website/server/controllers/api-v3/tasks.js index 4c93c66a4b..29fbaca4e4 100644 --- a/website/server/controllers/api-v3/tasks.js +++ b/website/server/controllers/api-v3/tasks.js @@ -1,3 +1,5 @@ +import _ from 'lodash'; +import moment from 'moment'; import { authWithHeaders } from '../../middlewares/auth'; import { taskActivityWebhook, @@ -21,15 +23,13 @@ import { setNextDue, } from '../../libs/taskManager'; import common from '../../../common'; -import _ from 'lodash'; import logger from '../../libs/logger'; -import moment from 'moment'; import apiError from '../../libs/apiError'; function canNotEditTasks (group, user, assignedUserId) { - let isNotGroupLeader = group.leader !== user._id; - let isManager = Boolean(group.managers[user._id]); - let userIsAssigningToSelf = Boolean(assignedUserId && user._id === assignedUserId); + const isNotGroupLeader = group.leader !== user._id; + const isManager = Boolean(group.managers[user._id]); + const userIsAssigningToSelf = Boolean(assignedUserId && user._id === assignedUserId); return isNotGroupLeader && !isManager && !userIsAssigningToSelf; } @@ -48,8 +48,8 @@ function canNotEditTasks (group, user, assignedUserId) { * @apiError (401) {NotAuthorized} There is no account that uses those credentials. */ -let api = {}; -let requiredGroupFields = '_id leader tasksOrder name'; +const api = {}; +const requiredGroupFields = '_id leader tasksOrder name'; /** * @api {post} /api/v3/tasks/user Create a new task belonging to the user @@ -160,12 +160,12 @@ api.createUserTasks = { url: '/tasks/user', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let tasks = await createTasks(req, res, {user}); + const { user } = res.locals; + const tasks = await createTasks(req, res, { user }); res.respond(201, tasks.length === 1 ? tasks[0] : tasks); - tasks.forEach((task) => { + tasks.forEach(task => { // Track when new users (first 7 days) create tasks if (moment().diff(user.auth.timestamps.created, 'days') < 7) { res.analytics.track('task create', { @@ -234,26 +234,26 @@ api.createChallengeTasks = { async handler (req, res) { req.checkParams('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID(); - let reqValidationErrors = req.validationErrors(); + const reqValidationErrors = req.validationErrors(); if (reqValidationErrors) throw reqValidationErrors; - let user = res.locals.user; - let challengeId = req.params.challengeId; + const { user } = res.locals; + const { challengeId } = req.params; - let challenge = await Challenge.findOne({_id: challengeId}).exec(); + const challenge = await Challenge.findOne({ _id: challengeId }).exec(); // If the challenge does not exist, or if it exists but user is not the leader -> throw error if (!challenge) throw new NotFound(res.t('challengeNotFound')); if (!challenge.canModify(user)) throw new NotAuthorized(res.t('onlyChalLeaderEditTasks')); - let tasks = await createTasks(req, res, {user, challenge}); + const tasks = await createTasks(req, res, { user, challenge }); res.respond(201, tasks.length === 1 ? tasks[0] : tasks); // If adding tasks to a challenge -> sync users if (challenge) challenge.addTasks(tasks); - tasks.forEach((task) => { + tasks.forEach(task => { res.analytics.track('task create', { uuid: user._id, hitType: 'event', @@ -289,15 +289,15 @@ api.getUserTasks = { userFieldsToInclude: ['tasksOrder'], })], async handler (req, res) { - let types = Tasks.tasksTypes.map(type => `${type}s`); + const types = Tasks.tasksTypes.map(type => `${type}s`); types.push('completedTodos', '_allCompletedTodos'); // _allCompletedTodos is currently in BETA and is likely to be removed in future req.checkQuery('type', res.t('invalidTasksTypeExtra')).optional().isIn(types); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - const user = res.locals.user; - const dueDate = req.query.dueDate; + const { user } = res.locals; + const { dueDate } = req.query; const tasks = await getTasks(req, res, { user, dueDate }); return res.respond(200, tasks); @@ -328,21 +328,21 @@ api.getChallengeTasks = { middlewares: [authWithHeaders()], async handler (req, res) { req.checkParams('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID(); - let types = Tasks.tasksTypes.map(type => `${type}s`); + const types = Tasks.tasksTypes.map(type => `${type}s`); req.checkQuery('type', res.t('invalidTasksType')).optional().isIn(types); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let user = res.locals.user; - let challengeId = req.params.challengeId; + const { user } = res.locals; + const { challengeId } = req.params; - let challenge = await Challenge.findOne({ + const challenge = await Challenge.findOne({ _id: challengeId, }).select('group leader tasksOrder').exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); - let group = await Group.getGroup({ + const group = await Group.getGroup({ user, groupId: challenge.group, fields: '_id type privacy', @@ -350,7 +350,7 @@ api.getChallengeTasks = { }); if (!group || !challenge.canView(user, group)) throw new NotFound(res.t('challengeNotFound')); - let tasks = await getTasks(req, res, {user, challenge}); + const tasks = await getTasks(req, res, { user, challenge }); return res.respond(200, tasks); }, }; @@ -377,14 +377,14 @@ api.getTask = { url: '/tasks/:taskId', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let taskId = req.params.taskId; - let task = await Tasks.Task.findByIdOrAlias(taskId, user._id); + const { user } = res.locals; + const { taskId } = req.params; + const task = await Tasks.Task.findByIdOrAlias(taskId, user._id); if (!task) { throw new NotFound(res.t('taskNotFound')); } else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights - let challenge = await Challenge.find({_id: task.challenge.id}).select('leader').exec(); + const challenge = await Challenge.find({ _id: task.challenge.id }).select('leader').exec(); if (!challenge || (user.challenges.indexOf(task.challenge.id) === -1 && challenge.leader !== user._id && !user.contributor.admin)) { // eslint-disable-line no-extra-parens throw new NotFound(res.t('taskNotFound')); } @@ -431,37 +431,37 @@ api.updateTask = { url: '/tasks/:taskId', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; let challenge; req.checkParams('taskId', apiError('taskIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let taskId = req.params.taskId; - let task = await Tasks.Task.findByIdOrAlias(taskId, user._id); + const { taskId } = req.params; + const task = await Tasks.Task.findByIdOrAlias(taskId, user._id); let group; if (!task) { throw new NotFound(res.t('taskNotFound')); } else if (task.group.id && !task.userId) { // @TODO: Abstract this access snippet - let fields = requiredGroupFields.concat(' managers'); - group = await Group.getGroup({user, groupId: task.group.id, fields}); + const fields = requiredGroupFields.concat(' managers'); + group = await Group.getGroup({ user, groupId: task.group.id, fields }); if (!group) throw new NotFound(res.t('groupNotFound')); if (canNotEditTasks(group, user)) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks')); } else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights - challenge = await Challenge.findOne({_id: task.challenge.id}).exec(); + challenge = await Challenge.findOne({ _id: task.challenge.id }).exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); if (!challenge.canModify(user)) throw new NotAuthorized(res.t('onlyChalLeaderEditTasks')); } else if (task.userId !== user._id) { // If the task is owned by a user make it's the current one throw new NotFound(res.t('taskNotFound')); } - let oldCheckList = task.checklist; + const oldCheckList = task.checklist; // we have to convert task to an object because otherwise things don't get merged correctly. Bad for performances? - let [updatedTaskObj] = common.ops.updateTask(task.toObject(), req); + const [updatedTaskObj] = common.ops.updateTask(task.toObject(), req); // Sanitize differently user tasks linked to a challenge let sanitizedObj; @@ -488,18 +488,16 @@ api.updateTask = { } setNextDue(task, user); - let savedTask = await task.save(); + const savedTask = await task.save(); if (group && task.group.id && task.group.assignedUsers.length > 0) { - let updateCheckListItems = _.remove(sanitizedObj.checklist, function getCheckListsToUpdate (checklist) { - let indexOld = _.findIndex(oldCheckList, function findIndex (check) { - return check.id === checklist.id; - }); + const updateCheckListItems = _.remove(sanitizedObj.checklist, checklist => { + const indexOld = _.findIndex(oldCheckList, check => check.id === checklist.id); if (indexOld !== -1) return checklist.text !== oldCheckList[indexOld].text; return false; // Only return changes. Adding and remove are handled differently }); - await group.updateTask(savedTask, {updateCheckListItems}); + await group.updateTask(savedTask, { updateCheckListItems }); } res.respond(200, savedTask); @@ -550,11 +548,11 @@ api.scoreTask = { const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - const user = res.locals.user; - const {taskId} = req.params; + const { user } = res.locals; + const { taskId } = req.params; - const task = await Tasks.Task.findByIdOrAlias(taskId, user._id, {userId: user._id}); - const direction = req.params.direction; + const task = await Tasks.Task.findByIdOrAlias(taskId, user._id, { userId: user._id }); + const { direction } = req.params; if (!task) throw new NotFound(res.t('taskNotFound')); @@ -567,10 +565,10 @@ api.scoreTask = { } if (task.group.approval.required && !task.group.approval.approved) { - let fields = requiredGroupFields.concat(' managers'); - let group = await Group.getGroup({user, groupId: task.group.id, fields}); + const fields = requiredGroupFields.concat(' managers'); + const group = await Group.getGroup({ user, groupId: task.group.id, fields }); - let managerIds = Object.keys(group.managers); + const managerIds = Object.keys(group.managers); managerIds.push(group.leader); if (managerIds.indexOf(user._id) !== -1) { @@ -585,11 +583,11 @@ api.scoreTask = { task.group.approval.requested = true; task.group.approval.requestedDate = new Date(); - let managers = await User.find({_id: managerIds}, 'notifications preferences').exec(); // Use this method so we can get access to notifications + const managers = await User.find({ _id: managerIds }, 'notifications preferences').exec(); // Use this method so we can get access to notifications // @TODO: we can use the User.pushNotification function because we need to ensure notifications are translated - let managerPromises = []; - managers.forEach((manager) => { + const managerPromises = []; + managers.forEach(manager => { manager.addNotification('GROUP_TASK_APPROVAL', { message: res.t('userHasRequestedTaskApproval', { user: user.profile.name, @@ -611,11 +609,11 @@ api.scoreTask = { } } - let wasCompleted = task.completed; + const wasCompleted = task.completed; - let [delta] = common.ops.scoreTask({task, user, direction}, req); + const [delta] = common.ops.scoreTask({ task, user, direction }, req); // Drop system (don't run on the client, as it would only be discarded since ops are sent to the API, not the results) - if (direction === 'up') common.fns.randomDrop(user, {task, delta}, req, res.analytics); + if (direction === 'up') common.fns.randomDrop(user, { task, delta }, req, res.analytics); // If a todo was completed or uncompleted move it in or out of the user.tasksOrder.todos list // TODO move to common code? @@ -638,7 +636,7 @@ api.scoreTask = { setNextDue(task, user); - let promises = [ + const promises = [ user.save(), task.save(), ]; @@ -661,12 +659,12 @@ api.scoreTask = { // Save results and handle request if (taskOrderPromise) promises.push(taskOrderPromise); - let results = await Promise.all(promises); + const results = await Promise.all(promises); - let savedUser = results[0]; + const savedUser = results[0]; - let userStats = savedUser.stats.toJSON(); - let resJsonData = _.assign({delta, _tmp: user._tmp}, userStats); + const userStats = savedUser.stats.toJSON(); + const resJsonData = _.assign({ delta, _tmp: user._tmp }, userStats); res.respond(200, resJsonData); taskScoredWebhook.send(user, { @@ -729,25 +727,25 @@ api.moveTask = { req.checkParams('taskId', apiError('taskIdRequired')).notEmpty(); req.checkParams('position', res.t('positionRequired')).notEmpty().isNumeric(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let user = res.locals.user; - let taskId = req.params.taskId; - let to = Number(req.params.position); + const { user } = res.locals; + const { taskId } = req.params; + const to = Number(req.params.position); - let task = await Tasks.Task.findByIdOrAlias(taskId, user._id, { userId: user._id }); + const task = await Tasks.Task.findByIdOrAlias(taskId, user._id, { userId: user._id }); if (!task) throw new NotFound(res.t('taskNotFound')); if (task.type === 'todo' && task.completed) throw new BadRequest(res.t('cantMoveCompletedTodo')); // In memory updates - let order = user.tasksOrder[`${task.type}s`]; + const order = user.tasksOrder[`${task.type}s`]; moveTask(order, task._id, to); // Server updates // Cannot send $pull and $push on same field in one single op - let pullQuery = { $pull: {} }; + const pullQuery = { $pull: {} }; pullQuery.$pull[`tasksOrder.${task.type}s`] = task.id; await user.update(pullQuery).exec(); @@ -755,7 +753,7 @@ api.moveTask = { let position = to; if (to === -1) position = [`tasksOrder.${task.type}s`].length - 1; - let updateQuery = { $push: {} }; + const updateQuery = { $push: {} }; updateQuery.$push[`tasksOrder.${task.type}s`] = { $each: [task._id], $position: position, @@ -795,26 +793,26 @@ api.addChecklistItem = { url: '/tasks/:taskId/checklist', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; let challenge; let group; req.checkParams('taskId', apiError('taskIdRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let taskId = req.params.taskId; - let task = await Tasks.Task.findByIdOrAlias(taskId, user._id); + const { taskId } = req.params; + const task = await Tasks.Task.findByIdOrAlias(taskId, user._id); if (!task) { throw new NotFound(res.t('taskNotFound')); } else if (task.group.id && !task.userId) { - let fields = requiredGroupFields.concat(' managers'); - group = await Group.getGroup({user, groupId: task.group.id, fields}); + const fields = requiredGroupFields.concat(' managers'); + group = await Group.getGroup({ user, groupId: task.group.id, fields }); if (canNotEditTasks(group, user)) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks')); } else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights - challenge = await Challenge.findOne({_id: task.challenge.id}).exec(); + challenge = await Challenge.findOne({ _id: task.challenge.id }).exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); if (!challenge.canModify(user)) throw new NotAuthorized(res.t('onlyChalLeaderEditTasks')); } else if (task.userId !== user._id) { // If the task is owned by a user make it's the current one @@ -823,16 +821,16 @@ api.addChecklistItem = { if (task.type !== 'daily' && task.type !== 'todo') throw new BadRequest(res.t('checklistOnlyDailyTodo')); - let newCheckListItem = Tasks.Task.sanitizeChecklist(req.body); + const newCheckListItem = Tasks.Task.sanitizeChecklist(req.body); task.checklist.push(newCheckListItem); - let savedTask = await task.save(); + const savedTask = await task.save(); newCheckListItem.id = savedTask.checklist[savedTask.checklist.length - 1].id; res.respond(200, savedTask); if (challenge) challenge.updateTask(savedTask); if (group && task.group.id && task.group.assignedUsers.length > 0) { - await group.updateTask(savedTask, {newCheckListItem}); + await group.updateTask(savedTask, { newCheckListItem }); } }, }; @@ -855,25 +853,25 @@ api.scoreCheckListItem = { url: '/tasks/:taskId/checklist/:itemId/score', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('taskId', apiError('taskIdRequired')).notEmpty(); req.checkParams('itemId', res.t('itemIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let taskId = req.params.taskId; - let task = await Tasks.Task.findByIdOrAlias(taskId, user._id, { userId: user._id }); + const { taskId } = req.params; + const task = await Tasks.Task.findByIdOrAlias(taskId, user._id, { userId: user._id }); if (!task) throw new NotFound(res.t('taskNotFound')); if (task.type !== 'daily' && task.type !== 'todo') throw new BadRequest(res.t('checklistOnlyDailyTodo')); - let item = _.find(task.checklist, {id: req.params.itemId}); + const item = _.find(task.checklist, { id: req.params.itemId }); if (!item) throw new NotFound(res.t('checklistItemNotFound')); item.completed = !item.completed; - let savedTask = await task.save(); + const savedTask = await task.save(); res.respond(200, savedTask); @@ -909,28 +907,28 @@ api.updateChecklistItem = { url: '/tasks/:taskId/checklist/:itemId', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; let challenge; let group; req.checkParams('taskId', apiError('taskIdRequired')).notEmpty(); req.checkParams('itemId', res.t('itemIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let taskId = req.params.taskId; - let task = await Tasks.Task.findByIdOrAlias(taskId, user._id); + const { taskId } = req.params; + const task = await Tasks.Task.findByIdOrAlias(taskId, user._id); if (!task) { throw new NotFound(res.t('taskNotFound')); } else if (task.group.id && !task.userId) { - let fields = requiredGroupFields.concat(' managers'); - group = await Group.getGroup({user, groupId: task.group.id, fields}); + const fields = requiredGroupFields.concat(' managers'); + group = await Group.getGroup({ user, groupId: task.group.id, fields }); if (!group) throw new NotFound(res.t('groupNotFound')); if (canNotEditTasks(group, user)) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks')); } else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights - challenge = await Challenge.findOne({_id: task.challenge.id}).exec(); + challenge = await Challenge.findOne({ _id: task.challenge.id }).exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); if (!challenge.canModify(user)) throw new NotAuthorized(res.t('onlyChalLeaderEditTasks')); } else if (task.userId !== user._id) { // If the task is owned by a user make it's the current one @@ -938,11 +936,11 @@ api.updateChecklistItem = { } if (task.type !== 'daily' && task.type !== 'todo') throw new BadRequest(res.t('checklistOnlyDailyTodo')); - let item = _.find(task.checklist, {id: req.params.itemId}); + const item = _.find(task.checklist, { id: req.params.itemId }); if (!item) throw new NotFound(res.t('checklistItemNotFound')); _.merge(item, Tasks.Task.sanitizeChecklist(req.body)); - let savedTask = await task.save(); + const savedTask = await task.save(); res.respond(200, savedTask); if (challenge) challenge.updateTask(savedTask); @@ -974,28 +972,28 @@ api.removeChecklistItem = { url: '/tasks/:taskId/checklist/:itemId', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; let challenge; let group; req.checkParams('taskId', apiError('taskIdRequired')).notEmpty(); req.checkParams('itemId', res.t('itemIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let taskId = req.params.taskId; - let task = await Tasks.Task.findByIdOrAlias(taskId, user._id); + const { taskId } = req.params; + const task = await Tasks.Task.findByIdOrAlias(taskId, user._id); if (!task) { throw new NotFound(res.t('taskNotFound')); } else if (task.group.id && !task.userId) { - let fields = requiredGroupFields.concat(' managers'); - group = await Group.getGroup({user, groupId: task.group.id, fields}); + const fields = requiredGroupFields.concat(' managers'); + group = await Group.getGroup({ user, groupId: task.group.id, fields }); if (!group) throw new NotFound(res.t('groupNotFound')); if (canNotEditTasks(group, user)) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks')); } else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights - challenge = await Challenge.findOne({_id: task.challenge.id}).exec(); + challenge = await Challenge.findOne({ _id: task.challenge.id }).exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); if (!challenge.canModify(user)) throw new NotAuthorized(res.t('onlyChalLeaderEditTasks')); } else if (task.userId !== user._id) { // If the task is owned by a user make it's the current one @@ -1003,14 +1001,14 @@ api.removeChecklistItem = { } if (task.type !== 'daily' && task.type !== 'todo') throw new BadRequest(res.t('checklistOnlyDailyTodo')); - let hasItem = removeFromArray(task.checklist, { id: req.params.itemId }); + const hasItem = removeFromArray(task.checklist, { id: req.params.itemId }); if (!hasItem) throw new NotFound(res.t('checklistItemNotFound')); - let savedTask = await task.save(); + const savedTask = await task.save(); res.respond(200, savedTask); if (challenge) challenge.updateTask(savedTask); if (group && task.group.id && task.group.assignedUsers.length > 0) { - await group.updateTask(savedTask, {removedCheckListItemId: req.params.itemId}); + await group.updateTask(savedTask, { removedCheckListItemId: req.params.itemId }); } }, }; @@ -1037,27 +1035,27 @@ api.addTagToTask = { url: '/tasks/:taskId/tags/:tagId', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('taskId', apiError('taskIdRequired')).notEmpty(); - let userTags = user.tags.map(tag => tag.id); + const userTags = user.tags.map(tag => tag.id); req.checkParams('tagId', res.t('tagIdRequired')).notEmpty().isUUID().isIn(userTags); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let taskId = req.params.taskId; - let task = await Tasks.Task.findByIdOrAlias(taskId, user._id, { userId: user._id }); + const { taskId } = req.params; + const task = await Tasks.Task.findByIdOrAlias(taskId, user._id, { userId: user._id }); if (!task) throw new NotFound(res.t('taskNotFound')); - let tagId = req.params.tagId; + const { tagId } = req.params; - let alreadyTagged = task.tags.indexOf(tagId) !== -1; + const alreadyTagged = task.tags.indexOf(tagId) !== -1; if (alreadyTagged) throw new BadRequest(res.t('alreadyTagged')); task.tags.push(tagId); - let savedTask = await task.save(); + const savedTask = await task.save(); res.respond(200, savedTask); }, }; @@ -1086,23 +1084,23 @@ api.removeTagFromTask = { url: '/tasks/:taskId/tags/:tagId', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.checkParams('taskId', apiError('taskIdRequired')).notEmpty(); req.checkParams('tagId', res.t('tagIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let taskId = req.params.taskId; - let task = await Tasks.Task.findByIdOrAlias(taskId, user._id, { userId: user._id }); + const { taskId } = req.params; + const task = await Tasks.Task.findByIdOrAlias(taskId, user._id, { userId: user._id }); if (!task) throw new NotFound(res.t('taskNotFound')); - let hasTag = removeFromArray(task.tags, req.params.tagId); + const hasTag = removeFromArray(task.tags, req.params.tagId); if (!hasTag) throw new NotFound(res.t('tagNotFound')); - let savedTask = await task.save(); + const savedTask = await task.save(); res.respond(200, savedTask); }, }; @@ -1134,21 +1132,19 @@ api.unlinkAllTasks = { req.checkParams('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID(); req.checkQuery('keep', apiError('keepOrRemoveAll')).notEmpty().isIn(['keep-all', 'remove-all']); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let user = res.locals.user; - let keep = req.query.keep; - let challengeId = req.params.challengeId; + const { user } = res.locals; + const { keep } = req.query; + const { challengeId } = req.params; - let tasks = await Tasks.Task.find({ + const tasks = await Tasks.Task.find({ 'challenge.id': challengeId, userId: user._id, }).exec(); - let validTasks = tasks.every(task => { - return task.challenge.broken; - }); + const validTasks = tasks.every(task => task.challenge.broken); if (!validTasks) throw new BadRequest(res.t('cantOnlyUnlinkChalTask')); @@ -1158,7 +1154,7 @@ api.unlinkAllTasks = { return task.save(); })); } else { // remove - let toSave = []; + const toSave = []; tasks.forEach(task => { if (task.type !== 'todo' || !task.completed) { // eslint-disable-line no-lonely-if @@ -1201,14 +1197,14 @@ api.unlinkOneTask = { req.checkParams('taskId', apiError('taskIdRequired')).notEmpty().isUUID(); req.checkQuery('keep', apiError('keepOrRemove')).notEmpty().isIn(['keep', 'remove']); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let user = res.locals.user; - let keep = req.query.keep; - let taskId = req.params.taskId; + const { user } = res.locals; + const { keep } = req.query; + const { taskId } = req.params; - let task = await Tasks.Task.findByIdOrAlias(taskId, user._id, { userId: user._id }); + const task = await Tasks.Task.findByIdOrAlias(taskId, user._id, { userId: user._id }); if (!task) throw new NotFound(res.t('taskNotFound')); if (!task.challenge.id) throw new BadRequest(res.t('cantOnlyUnlinkChalTask')); @@ -1248,7 +1244,7 @@ api.clearCompletedTodos = { url: '/tasks/clearCompletedTodos', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; // Clear completed todos // Do not delete completed todos from challenges or groups, unless the task is broken @@ -1259,14 +1255,14 @@ api.clearCompletedTodos = { $and: [ // exclude challenge and group tasks { $or: [ - {'challenge.id': {$exists: false}}, - {'challenge.broken': {$exists: true}}, + { 'challenge.id': { $exists: false } }, + { 'challenge.broken': { $exists: true } }, ], }, { $or: [ - {'group.id': {$exists: false}}, - {'group.broken': {$exists: true}}, + { 'group.id': { $exists: false } }, + { 'group.broken': { $exists: true } }, ], }, ], @@ -1299,23 +1295,23 @@ api.deleteTask = { url: '/tasks/:taskId', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; let challenge; - let taskId = req.params.taskId; - let task = await Tasks.Task.findByIdOrAlias(taskId, user._id); + const { taskId } = req.params; + const task = await Tasks.Task.findByIdOrAlias(taskId, user._id); if (!task) { throw new NotFound(res.t('taskNotFound')); } else if (task.group.id && !task.userId) { // @TODO: Abstract this access snippet - let fields = requiredGroupFields.concat(' managers'); - let group = await Group.getGroup({user, groupId: task.group.id, fields}); + const fields = requiredGroupFields.concat(' managers'); + const group = await Group.getGroup({ user, groupId: task.group.id, fields }); if (!group) throw new NotFound(res.t('groupNotFound')); if (canNotEditTasks(group, user)) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks')); await group.removeTask(task); } else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights - challenge = await Challenge.findOne({_id: task.challenge.id}).exec(); + challenge = await Challenge.findOne({ _id: task.challenge.id }).exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); if (!challenge.canModify(user)) throw new NotAuthorized(res.t('onlyChalLeaderEditTasks')); } else if (task.userId !== user._id) { // If the task is owned by a user make it's the current one @@ -1329,9 +1325,9 @@ api.deleteTask = { if (task.type !== 'todo' || !task.completed) { removeFromArray((challenge || user).tasksOrder[`${task.type}s`], taskId); - let pullQuery = {$pull: {}}; + const pullQuery = { $pull: {} }; pullQuery.$pull[`tasksOrder.${task.type}s`] = task._id; - let taskOrderUpdate = (challenge || user).update(pullQuery).exec(); + const taskOrderUpdate = (challenge || user).update(pullQuery).exec(); // Update the user version field manually, // it cannot be updated in the pre update hook diff --git a/website/server/controllers/api-v3/tasks/groups.js b/website/server/controllers/api-v3/tasks/groups.js index 2317b4b4ff..9b76855ba0 100644 --- a/website/server/controllers/api-v3/tasks/groups.js +++ b/website/server/controllers/api-v3/tasks/groups.js @@ -15,19 +15,19 @@ import { import { handleSharedCompletion } from '../../../libs/groupTasks'; import apiError from '../../../libs/apiError'; -let requiredGroupFields = '_id leader tasksOrder name'; +const requiredGroupFields = '_id leader tasksOrder name'; // @TODO: abstract to task lib -let types = Tasks.tasksTypes.map(type => `${type}s`); +const types = Tasks.tasksTypes.map(type => `${type}s`); types.push('completedTodos', '_allCompletedTodos'); // _allCompletedTodos is currently in BETA and is likely to be removed in future function canNotEditTasks (group, user, assignedUserId) { - let isNotGroupLeader = group.leader !== user._id; - let isManager = Boolean(group.managers[user._id]); - let userIsAssigningToSelf = Boolean(assignedUserId && user._id === assignedUserId); + const isNotGroupLeader = group.leader !== user._id; + const isManager = Boolean(group.managers[user._id]); + const userIsAssigningToSelf = Boolean(assignedUserId && user._id === assignedUserId); return isNotGroupLeader && !isManager && !userIsAssigningToSelf; } -let api = {}; +const api = {}; /** * @api {post} /api/v3/tasks/group/:groupId Create a new task belonging to a group @@ -46,22 +46,22 @@ api.createGroupTasks = { async handler (req, res) { req.checkParams('groupId', apiError('groupIdRequired')).notEmpty().isUUID(); - let reqValidationErrors = req.validationErrors(); + const reqValidationErrors = req.validationErrors(); if (reqValidationErrors) throw reqValidationErrors; - let user = res.locals.user; + const { user } = res.locals; - let fields = requiredGroupFields.concat(' managers'); - let group = await Group.getGroup({user, groupId: req.params.groupId, fields}); + const fields = requiredGroupFields.concat(' managers'); + const group = await Group.getGroup({ user, groupId: req.params.groupId, fields }); if (!group) throw new NotFound(res.t('groupNotFound')); if (canNotEditTasks(group, user)) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks')); - let tasks = await createTasks(req, res, {user, group}); + const tasks = await createTasks(req, res, { user, group }); res.respond(201, tasks.length === 1 ? tasks[0] : tasks); - tasks.forEach((task) => { + tasks.forEach(task => { res.analytics.track('task create', { uuid: user._id, hitType: 'event', @@ -91,15 +91,15 @@ api.getGroupTasks = { req.checkParams('groupId', apiError('groupIdRequired')).notEmpty().isUUID(); req.checkQuery('type', res.t('invalidTasksType')).optional().isIn(types); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let user = res.locals.user; + const { user } = res.locals; - let group = await Group.getGroup({user, groupId: req.params.groupId, fields: requiredGroupFields}); + const group = await Group.getGroup({ user, groupId: req.params.groupId, fields: requiredGroupFields }); if (!group) throw new NotFound(res.t('groupNotFound')); - let tasks = await getTasks(req, res, {user, group}); + const tasks = await getTasks(req, res, { user, group }); res.respond(200, tasks); }, }; @@ -124,17 +124,17 @@ api.groupMoveTask = { req.checkParams('taskId', apiError('taskIdRequired')).notEmpty(); req.checkParams('position', res.t('positionRequired')).notEmpty().isNumeric(); - let reqValidationErrors = req.validationErrors(); + const reqValidationErrors = req.validationErrors(); if (reqValidationErrors) throw reqValidationErrors; - let user = res.locals.user; + const { user } = res.locals; - let taskId = req.params.taskId; - let task = await Tasks.Task.findOne({ + const { taskId } = req.params; + const task = await Tasks.Task.findOne({ _id: taskId, }).exec(); - let to = Number(req.params.position); + const to = Number(req.params.position); if (!task) { throw new NotFound(res.t('taskNotFound')); @@ -142,12 +142,12 @@ api.groupMoveTask = { if (task.type === 'todo' && task.completed) throw new BadRequest(res.t('cantMoveCompletedTodo')); - let group = await Group.getGroup({user, groupId: task.group.id, fields: requiredGroupFields}); + const group = await Group.getGroup({ user, groupId: task.group.id, fields: requiredGroupFields }); if (!group) throw new NotFound(res.t('groupNotFound')); if (group.leader !== user._id) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks')); - let order = group.tasksOrder[`${task.type}s`]; + const order = group.tasksOrder[`${task.type}s`]; moveTask(order, task._id, to); @@ -175,15 +175,15 @@ api.assignTask = { req.checkParams('taskId', apiError('taskIdRequired')).notEmpty().isUUID(); req.checkParams('assignedUserId', res.t('userIdRequired')).notEmpty().isUUID(); - let reqValidationErrors = req.validationErrors(); + const reqValidationErrors = req.validationErrors(); if (reqValidationErrors) throw reqValidationErrors; - let user = res.locals.user; - let assignedUserId = req.params.assignedUserId; - let assignedUser = await User.findById(assignedUserId).exec(); + const { user } = res.locals; + const { assignedUserId } = req.params; + const assignedUser = await User.findById(assignedUserId).exec(); - let taskId = req.params.taskId; - let task = await Tasks.Task.findByIdOrAlias(taskId, user._id); + const { taskId } = req.params; + const task = await Tasks.Task.findByIdOrAlias(taskId, user._id); if (!task) { throw new NotFound(res.t('taskNotFound')); @@ -193,24 +193,24 @@ api.assignTask = { throw new NotAuthorized(res.t('onlyGroupTasksCanBeAssigned')); } - let groupFields = `${requiredGroupFields} chat managers`; - let group = await Group.getGroup({user, groupId: task.group.id, fields: groupFields}); + const groupFields = `${requiredGroupFields} chat managers`; + const group = await Group.getGroup({ user, groupId: task.group.id, fields: groupFields }); if (!group) throw new NotFound(res.t('groupNotFound')); if (canNotEditTasks(group, user, assignedUserId)) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks')); - let promises = []; + const promises = []; const taskText = task.text; const userName = user.profile.name; if (user._id === assignedUserId) { const managerIds = Object.keys(group.managers); managerIds.push(group.leader); - const managers = await User.find({_id: managerIds}, 'notifications preferences').exec(); - managers.forEach((manager) => { + const managers = await User.find({ _id: managerIds }, 'notifications preferences').exec(); + managers.forEach(manager => { if (manager._id === user._id) return; manager.addNotification('GROUP_TASK_CLAIMED', { - message: res.t('taskClaimed', {userName, taskText}, manager.preferences.language), + message: res.t('taskClaimed', { userName, taskText }, manager.preferences.language), groupId: group._id, taskId: task._id, }); @@ -218,7 +218,7 @@ api.assignTask = { }); } else { assignedUser.addNotification('GROUP_TASK_ASSIGNED', { - message: res.t('youHaveBeenAssignedTask', {managerName: userName, taskText}), + message: res.t('youHaveBeenAssignedTask', { managerName: userName, taskText }), taskId: task._id, }); } @@ -250,15 +250,15 @@ api.unassignTask = { req.checkParams('taskId', apiError('taskIdRequired')).notEmpty().isUUID(); req.checkParams('assignedUserId', res.t('userIdRequired')).notEmpty().isUUID(); - let reqValidationErrors = req.validationErrors(); + const reqValidationErrors = req.validationErrors(); if (reqValidationErrors) throw reqValidationErrors; - let user = res.locals.user; - let assignedUserId = req.params.assignedUserId; - let assignedUser = await User.findById(assignedUserId).exec(); + const { user } = res.locals; + const { assignedUserId } = req.params; + const assignedUser = await User.findById(assignedUserId).exec(); - let taskId = req.params.taskId; - let task = await Tasks.Task.findByIdOrAlias(taskId, user._id); + const { taskId } = req.params; + const task = await Tasks.Task.findByIdOrAlias(taskId, user._id); if (!task) { throw new NotFound(res.t('taskNotFound')); @@ -268,17 +268,15 @@ api.unassignTask = { throw new NotAuthorized(res.t('onlyGroupTasksCanBeAssigned')); } - let fields = requiredGroupFields.concat(' managers'); - let group = await Group.getGroup({user, groupId: task.group.id, fields}); + const fields = requiredGroupFields.concat(' managers'); + const group = await Group.getGroup({ user, groupId: task.group.id, fields }); if (!group) throw new NotFound(res.t('groupNotFound')); if (canNotEditTasks(group, user, assignedUserId)) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks')); await group.unlinkTask(task, assignedUser); - let notificationIndex = assignedUser.notifications.findIndex(function findNotification (notification) { - return notification && notification.data && notification.type === 'GROUP_TASK_ASSIGNED' && notification.data.taskId === task._id; - }); + const notificationIndex = assignedUser.notifications.findIndex(notification => notification && notification.data && notification.type === 'GROUP_TASK_ASSIGNED' && notification.data.taskId === task._id); if (notificationIndex !== -1) { assignedUser.notifications.splice(notificationIndex, 1); @@ -309,15 +307,15 @@ api.approveTask = { req.checkParams('taskId', apiError('taskIdRequired')).notEmpty().isUUID(); req.checkParams('userId', res.t('userIdRequired')).notEmpty().isUUID(); - let reqValidationErrors = req.validationErrors(); + const reqValidationErrors = req.validationErrors(); if (reqValidationErrors) throw reqValidationErrors; - let user = res.locals.user; - let assignedUserId = req.params.userId; - let assignedUser = await User.findById(assignedUserId).exec(); + const { user } = res.locals; + const assignedUserId = req.params.userId; + const assignedUser = await User.findById(assignedUserId).exec(); - let taskId = req.params.taskId; - let task = await Tasks.Task.findOne({ + const { taskId } = req.params; + const task = await Tasks.Task.findOne({ 'group.taskId': taskId, userId: assignedUserId, }).exec(); @@ -326,8 +324,8 @@ api.approveTask = { throw new NotFound(res.t('taskNotFound')); } - let fields = requiredGroupFields.concat(' managers'); - let group = await Group.getGroup({user, groupId: task.group.id, fields}); + const fields = requiredGroupFields.concat(' managers'); + const group = await Group.getGroup({ user, groupId: task.group.id, fields }); if (!group) throw new NotFound(res.t('groupNotFound')); if (canNotEditTasks(group, user)) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks')); @@ -343,24 +341,20 @@ api.approveTask = { // Get Managers const managerIds = Object.keys(group.managers); managerIds.push(group.leader); - const managers = await User.find({_id: managerIds}, 'notifications').exec(); // Use this method so we can get access to notifications + const managers = await User.find({ _id: managerIds }, 'notifications').exec(); // Use this method so we can get access to notifications // Get task direction const firstManagerNotifications = managers[0].notifications; - const firstNotificationIndex = firstManagerNotifications.findIndex((notification) => { - return notification && notification.data && notification.data.taskId === task._id && notification.type === 'GROUP_TASK_APPROVAL'; - }); + const firstNotificationIndex = firstManagerNotifications.findIndex(notification => notification && notification.data && notification.data.taskId === task._id && notification.type === 'GROUP_TASK_APPROVAL'); let direction = 'up'; if (firstManagerNotifications[firstNotificationIndex]) { direction = firstManagerNotifications[firstNotificationIndex].direction; } // Remove old notifications - let approvalPromises = []; - managers.forEach((manager) => { - let notificationIndex = manager.notifications.findIndex(function findNotification (notification) { - return notification && notification.data && notification.data.taskId === task._id && notification.type === 'GROUP_TASK_APPROVAL'; - }); + const approvalPromises = []; + managers.forEach(manager => { + const notificationIndex = manager.notifications.findIndex(notification => notification && notification.data && notification.data.taskId === task._id && notification.type === 'GROUP_TASK_APPROVAL'); if (notificationIndex !== -1) { manager.notifications.splice(notificationIndex, 1); @@ -370,12 +364,12 @@ api.approveTask = { // Add new notifications to user assignedUser.addNotification('GROUP_TASK_APPROVED', { - message: res.t('yourTaskHasBeenApproved', {taskText: task.text}), + message: res.t('yourTaskHasBeenApproved', { taskText: task.text }), groupId: group._id, }); assignedUser.addNotification('SCORED_TASK', { - message: res.t('yourTaskHasBeenApproved', {taskText: task.text}), + message: res.t('yourTaskHasBeenApproved', { taskText: task.text }), scoreTask: task, direction, }); @@ -410,13 +404,13 @@ api.taskNeedsWork = { req.checkParams('taskId', apiError('taskIdRequired')).notEmpty().isUUID(); req.checkParams('userId', res.t('userIdRequired')).notEmpty().isUUID(); - let reqValidationErrors = req.validationErrors(); + const reqValidationErrors = req.validationErrors(); if (reqValidationErrors) throw reqValidationErrors; - let user = res.locals.user; + const { user } = res.locals; - let assignedUserId = req.params.userId; - let taskId = req.params.taskId; + const assignedUserId = req.params.userId; + const { taskId } = req.params; const [assignedUser, task] = await Promise.all([ User.findById(assignedUserId).exec(), @@ -430,8 +424,8 @@ api.taskNeedsWork = { throw new NotFound(res.t('taskNotFound')); } - let fields = requiredGroupFields.concat(' managers'); - let group = await Group.getGroup({user, groupId: task.group.id, fields}); + const fields = requiredGroupFields.concat(' managers'); + const group = await Group.getGroup({ user, groupId: task.group.id, fields }); if (!group) throw new NotFound(res.t('groupNotFound')); if (canNotEditTasks(group, user)) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks')); @@ -443,15 +437,13 @@ api.taskNeedsWork = { // Get Managers const managerIds = Object.keys(group.managers); managerIds.push(group.leader); - const managers = await User.find({_id: managerIds}, 'notifications').exec(); // Use this method so we can get access to notifications + const managers = await User.find({ _id: managerIds }, 'notifications').exec(); // Use this method so we can get access to notifications const promises = []; // Remove old notifications - managers.forEach((manager) => { - let notificationIndex = manager.notifications.findIndex(function findNotification (notification) { - return notification && notification.data && notification.data.taskId === task._id && notification.type === 'GROUP_TASK_APPROVAL'; - }); + managers.forEach(manager => { + const notificationIndex = manager.notifications.findIndex(notification => notification && notification.data && notification.data.taskId === task._id && notification.type === 'GROUP_TASK_APPROVAL'); if (notificationIndex !== -1) { manager.notifications.splice(notificationIndex, 1); @@ -465,7 +457,7 @@ api.taskNeedsWork = { const taskText = task.text; const managerName = user.profile.name; - const message = res.t('taskNeedsWork', {taskText, managerName}, assignedUser.preferences.language); + const message = res.t('taskNeedsWork', { taskText, managerName }, assignedUser.preferences.language); assignedUser.addNotification('GROUP_TASK_NEEDS_WORK', { message, @@ -506,14 +498,14 @@ api.getGroupApprovals = { async handler (req, res) { req.checkParams('groupId', apiError('groupIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let user = res.locals.user; - let groupId = req.params.groupId; + const { user } = res.locals; + const { groupId } = req.params; - let fields = requiredGroupFields.concat(' managers'); - let group = await Group.getGroup({user, groupId, fields}); + const fields = requiredGroupFields.concat(' managers'); + const group = await Group.getGroup({ user, groupId, fields }); if (!group) throw new NotFound(res.t('groupNotFound')); let approvals; diff --git a/website/server/controllers/api-v3/user.js b/website/server/controllers/api-v3/user.js index 9277df007f..c2c2dae156 100644 --- a/website/server/controllers/api-v3/user.js +++ b/website/server/controllers/api-v3/user.js @@ -1,3 +1,6 @@ +import _ from 'lodash'; +import nconf from 'nconf'; +import get from 'lodash/get'; import { authWithHeaders } from '../../middlewares/auth'; import common from '../../../common'; import { @@ -9,7 +12,6 @@ import { model as Group, } from '../../models/group'; import * as Tasks from '../../models/task'; -import _ from 'lodash'; import * as passwordUtils from '../../libs/password'; import { userActivityWebhook, @@ -20,8 +22,6 @@ import { } from '../../libs/email'; import * as inboxLib from '../../libs/inbox'; import * as userLib from '../../libs/user'; -import nconf from 'nconf'; -import get from 'lodash/get'; const TECH_ASSISTANCE_EMAIL = nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL'); const DELETE_CONFIRMATION = 'DELETE'; @@ -31,7 +31,7 @@ const DELETE_CONFIRMATION = 'DELETE'; * @apiError (404) {NotFound} UserNotFound The specified user could not be found. */ -let api = {}; +const api = {}; /* NOTE this route has also an API v4 version */ @@ -118,7 +118,7 @@ api.getBuyList = { middlewares: [authWithHeaders()], url: '/user/inventory/buy', async handler (req, res) { - let list = _.cloneDeep(common.updateStore(res.locals.user)); + const list = _.cloneDeep(common.updateStore(res.locals.user)); // return text and notes strings _.each(list, item => { @@ -161,7 +161,7 @@ api.getInAppRewardsList = { middlewares: [authWithHeaders()], url: '/user/in-app-rewards', async handler (req, res) { - let list = common.inAppRewards(res.locals.user); + const list = common.inAppRewards(res.locals.user); // return text and notes strings _.each(list, item => { @@ -252,34 +252,32 @@ api.deleteUser = { middlewares: [authWithHeaders()], url: '/user', async handler (req, res) { - let user = res.locals.user; - let plan = user.purchased.plan; + const { user } = res.locals; + const { plan } = user.purchased; - let password = req.body.password; + const { password } = req.body; if (!password) throw new BadRequest(res.t('missingPassword')); if (user.auth.local.hashed_password && user.auth.local.email) { - let isValidPassword = await passwordUtils.compare(user, password); + const isValidPassword = await passwordUtils.compare(user, password); if (!isValidPassword) throw new NotAuthorized(res.t('wrongPassword')); } else if ((user.auth.facebook.id || user.auth.google.id) && password !== DELETE_CONFIRMATION) { - throw new NotAuthorized(res.t('incorrectDeletePhrase', {magicWord: 'DELETE'})); + throw new NotAuthorized(res.t('incorrectDeletePhrase', { magicWord: 'DELETE' })); } - let feedback = req.body.feedback; + const { feedback } = req.body; if (feedback && feedback.length > 10000) throw new BadRequest(`Account deletion feedback is limited to 10,000 characters. For lengthy feedback, email ${TECH_ASSISTANCE_EMAIL}.`); if (plan && plan.customerId && !plan.dateTerminated) { throw new NotAuthorized(res.t('cannotDeleteActiveAccount')); } - let types = ['party', 'guilds']; - let groupFields = basicGroupFields.concat(' leader memberCount purchased'); + const types = ['party', 'guilds']; + const groupFields = basicGroupFields.concat(' leader memberCount purchased'); - let groupsUserIsMemberOf = await Group.getGroups({user, types, groupFields}); + const groupsUserIsMemberOf = await Group.getGroups({ user, types, groupFields }); - let groupLeavePromises = groupsUserIsMemberOf.map((group) => { - return group.leave(user, 'remove-all'); - }); + const groupLeavePromises = groupsUserIsMemberOf.map(group => group.leave(user, 'remove-all')); await Promise.all(groupLeavePromises); @@ -290,13 +288,13 @@ api.deleteUser = { await user.remove(); if (feedback) { - sendTxn({email: TECH_ASSISTANCE_EMAIL}, 'admin-feedback', [ - {name: 'PROFILE_NAME', content: user.profile.name}, - {name: 'USERNAME', content: user.auth.local.username}, - {name: 'UUID', content: user._id}, - {name: 'EMAIL', content: getUserInfo(user, ['email']).email}, - {name: 'FEEDBACK_SOURCE', content: 'from deletion form'}, - {name: 'FEEDBACK', content: feedback}, + sendTxn({ email: TECH_ASSISTANCE_EMAIL }, 'admin-feedback', [ + { name: 'PROFILE_NAME', content: user.profile.name }, + { name: 'USERNAME', content: user.auth.local.username }, + { name: 'UUID', content: user._id }, + { name: 'EMAIL', content: getUserInfo(user, ['email']).email }, + { name: 'FEEDBACK_SOURCE', content: 'from deletion form' }, + { name: 'FEEDBACK', content: feedback }, ]); } @@ -333,13 +331,13 @@ function _cleanChecklist (task) { * * @apiSuccess {Object} data.user * @apiSuccess {Object} data.tasks - **/ + * */ api.getUserAnonymized = { method: 'GET', middlewares: [authWithHeaders()], url: '/user/anonymized', async handler (req, res) { - let user = await res.locals.user.toJSONWithInbox(); + const user = await res.locals.user.toJSONWithInbox(); user.stats.toNextLevel = common.tnl(user.stats.lvl); user.stats.maxHealth = common.maxHealth; user.stats.maxMP = common.statsComputed(res.locals.user).maxMP; @@ -361,25 +359,25 @@ api.getUserAnonymized = { delete user.achievements.challenges; delete user.notifications; - _.forEach(user.inbox.messages, (msg) => { + _.forEach(user.inbox.messages, msg => { msg.text = 'inbox message text'; }); - _.forEach(user.tags, (tag) => { + _.forEach(user.tags, tag => { tag.name = 'tag'; tag.challenge = 'challenge'; }); - let query = { + const query = { userId: user._id, $or: [ { type: 'todo', completed: false }, { type: { $in: ['habit', 'daily', 'reward'] } }, ], }; - let tasks = await Tasks.Task.find(query).exec(); + const tasks = await Tasks.Task.find(query).exec(); - _.forEach(tasks, (task) => { + _.forEach(tasks, task => { task.text = 'task text'; task.notes = 'task notes'; if (task.type === 'todo' || task.type === 'daily') { @@ -411,8 +409,8 @@ api.sleep = { middlewares: [authWithHeaders()], url: '/user/sleep', async handler (req, res) { - let user = res.locals.user; - let sleepRes = common.ops.sleep(user, req, res.analytics); + const { user } = res.locals; + const sleepRes = common.ops.sleep(user, req, res.analytics); await user.save(); res.respond(200, ...sleepRes); }, @@ -455,11 +453,11 @@ api.buy = { middlewares: [authWithHeaders()], url: '/user/buy/:key', async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; let buyRes; // @TODO: Remove this when mobile passes type in body - let type = req.params.key; + const type = req.params.key; if (buySpecialKeys.indexOf(type) !== -1) { req.type = 'special'; } else if (buyKnownKeys.indexOf(type) === -1) { @@ -519,8 +517,8 @@ api.buyGear = { middlewares: [authWithHeaders()], url: '/user/buy-gear/:key', async handler (req, res) { - let user = res.locals.user; - let buyGearRes = common.ops.buy(user, req, res.analytics); + const { user } = res.locals; + const buyGearRes = common.ops.buy(user, req, res.analytics); await user.save(); res.respond(200, ...buyGearRes); }, @@ -559,10 +557,10 @@ api.buyArmoire = { middlewares: [authWithHeaders()], url: '/user/buy-armoire', async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.type = 'armoire'; req.params.key = 'armoire'; - let buyArmoireResponse = common.ops.buy(user, req, res.analytics); + const buyArmoireResponse = common.ops.buy(user, req, res.analytics); await user.save(); res.respond(200, ...buyArmoireResponse); }, @@ -599,10 +597,10 @@ api.buyHealthPotion = { middlewares: [authWithHeaders()], url: '/user/buy-health-potion', async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.type = 'potion'; req.params.key = 'potion'; - let buyHealthPotionResponse = common.ops.buy(user, req, res.analytics); + const buyHealthPotionResponse = common.ops.buy(user, req, res.analytics); await user.save(); res.respond(200, ...buyHealthPotionResponse); }, @@ -641,9 +639,9 @@ api.buyMysterySet = { middlewares: [authWithHeaders()], url: '/user/buy-mystery-set/:key', async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.type = 'mystery'; - let buyMysterySetRes = common.ops.buy(user, req, res.analytics); + const buyMysterySetRes = common.ops.buy(user, req, res.analytics); await user.save(); res.respond(200, ...buyMysterySetRes); }, @@ -684,9 +682,9 @@ api.buyQuest = { middlewares: [authWithHeaders()], url: '/user/buy-quest/:key', async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.type = 'quest'; - let buyQuestRes = common.ops.buy(user, req, res.analytics); + const buyQuestRes = common.ops.buy(user, req, res.analytics); await user.save(); res.respond(200, ...buyQuestRes); }, @@ -724,9 +722,9 @@ api.buySpecialSpell = { middlewares: [authWithHeaders()], url: '/user/buy-special-spell/:key', async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; req.type = 'special'; - let buySpecialSpellRes = common.ops.buy(user, req); + const buySpecialSpellRes = common.ops.buy(user, req); await user.save(); res.respond(200, ...buySpecialSpellRes); }, @@ -768,8 +766,8 @@ api.hatch = { middlewares: [authWithHeaders()], url: '/user/hatch/:egg/:hatchingPotion', async handler (req, res) { - let user = res.locals.user; - let hatchRes = common.ops.hatch(user, req); + const { user } = res.locals; + const hatchRes = common.ops.hatch(user, req); await user.save(); @@ -820,8 +818,8 @@ api.equip = { middlewares: [authWithHeaders()], url: '/user/equip/:type/:key', async handler (req, res) { - let user = res.locals.user; - let equipRes = common.ops.equip(user, req); + const { user } = res.locals; + const equipRes = common.ops.equip(user, req); await user.save(); res.respond(200, ...equipRes); }, @@ -855,8 +853,8 @@ api.feed = { middlewares: [authWithHeaders()], url: '/user/feed/:pet/:food', async handler (req, res) { - let user = res.locals.user; - let feedRes = common.ops.feed(user, req); + const { user } = res.locals; + const feedRes = common.ops.feed(user, req); await user.save(); @@ -899,8 +897,8 @@ api.changeClass = { middlewares: [authWithHeaders()], url: '/user/change-class', async handler (req, res) { - let user = res.locals.user; - let changeClassRes = common.ops.changeClass(user, req, res.analytics); + const { user } = res.locals; + const changeClassRes = common.ops.changeClass(user, req, res.analytics); await user.save(); res.respond(200, ...changeClassRes); }, @@ -920,8 +918,8 @@ api.disableClasses = { middlewares: [authWithHeaders()], url: '/user/disable-classes', async handler (req, res) { - let user = res.locals.user; - let disableClassesRes = common.ops.disableClasses(user, req); + const { user } = res.locals; + const disableClassesRes = common.ops.disableClasses(user, req); await user.save(); res.respond(200, ...disableClassesRes); }, @@ -952,7 +950,7 @@ api.purchase = { middlewares: [authWithHeaders()], url: '/user/purchase/:type/:key', async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; const type = get(req.params, 'type'); const key = get(req.params, 'key'); @@ -969,7 +967,7 @@ api.purchase = { if (req.body.quantity) quantity = req.body.quantity; req.quantity = quantity; - let purchaseRes = common.ops.buy(user, req, res.analytics); + const purchaseRes = common.ops.buy(user, req, res.analytics); await user.save(); res.respond(200, ...purchaseRes); }, @@ -1002,10 +1000,10 @@ api.userPurchaseHourglass = { middlewares: [authWithHeaders()], url: '/user/purchase-hourglass/:type/:key', async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; const quantity = req.body.quantity || 1; if (quantity < 1 || !Number.isInteger(quantity)) throw new BadRequest(res.t('invalidQuantity'), req.language); - let purchaseHourglassRes = common.ops.buy(user, req, res.analytics, {quantity, hourglass: true}); + const purchaseHourglassRes = common.ops.buy(user, req, res.analytics, { quantity, hourglass: true }); await user.save(); res.respond(200, ...purchaseHourglassRes); }, @@ -1056,8 +1054,8 @@ api.readCard = { middlewares: [authWithHeaders()], url: '/user/read-card/:cardType', async handler (req, res) { - let user = res.locals.user; - let readCardRes = common.ops.readCard(user, req); + const { user } = res.locals; + const readCardRes = common.ops.readCard(user, req); await user.save(); res.respond(200, ...readCardRes); }, @@ -1098,8 +1096,8 @@ api.userOpenMysteryItem = { middlewares: [authWithHeaders()], url: '/user/open-mystery-item', async handler (req, res) { - let user = res.locals.user; - let openMysteryItemRes = common.ops.openMysteryItem(user, req, res.analytics); + const { user } = res.locals; + const openMysteryItemRes = common.ops.openMysteryItem(user, req, res.analytics); await user.save(); res.respond(200, ...openMysteryItemRes); }, @@ -1130,8 +1128,8 @@ api.userReleasePets = { middlewares: [authWithHeaders()], url: '/user/release-pets', async handler (req, res) { - let user = res.locals.user; - let releasePetsRes = common.ops.releasePets(user, req, res.analytics); + const { user } = res.locals; + const releasePetsRes = common.ops.releasePets(user, req, res.analytics); await user.save(); res.respond(200, ...releasePetsRes); }, @@ -1179,8 +1177,8 @@ api.userReleaseBoth = { middlewares: [authWithHeaders()], url: '/user/release-both', async handler (req, res) { - let user = res.locals.user; - let releaseBothRes = common.ops.releaseBoth(user, req, res.analytics); + const { user } = res.locals; + const releaseBothRes = common.ops.releaseBoth(user, req, res.analytics); await user.save(); res.respond(200, ...releaseBothRes); }, @@ -1215,8 +1213,8 @@ api.userReleaseMounts = { middlewares: [authWithHeaders()], url: '/user/release-mounts', async handler (req, res) { - let user = res.locals.user; - let releaseMountsRes = common.ops.releaseMounts(user, req, res.analytics); + const { user } = res.locals; + const releaseMountsRes = common.ops.releaseMounts(user, req, res.analytics); await user.save(); res.respond(200, ...releaseMountsRes); }, @@ -1245,8 +1243,8 @@ api.userSell = { middlewares: [authWithHeaders()], url: '/user/sell/:type/:key', async handler (req, res) { - let user = res.locals.user; - let sellRes = common.ops.sell(user, req); + const { user } = res.locals; + const sellRes = common.ops.sell(user, req); await user.save(); res.respond(200, ...sellRes); }, @@ -1288,8 +1286,8 @@ api.userUnlock = { middlewares: [authWithHeaders()], url: '/user/unlock', async handler (req, res) { - let user = res.locals.user; - let unlockRes = common.ops.unlock(user, req, res.analytics); + const { user } = res.locals; + const unlockRes = common.ops.unlock(user, req, res.analytics); await user.save(); res.respond(200, ...unlockRes); }, @@ -1314,8 +1312,8 @@ api.userRevive = { middlewares: [authWithHeaders()], url: '/user/revive', async handler (req, res) { - let user = res.locals.user; - let reviveRes = common.ops.revive(user, req, res.analytics); + const { user } = res.locals; + const reviveRes = common.ops.revive(user, req, res.analytics); await user.save(); res.respond(200, ...reviveRes); }, @@ -1380,8 +1378,8 @@ api.blockUser = { middlewares: [authWithHeaders()], url: '/user/block/:uuid', async handler (req, res) { - let user = res.locals.user; - let blockUserRes = common.ops.blockUser(user, req); + const { user } = res.locals; + const blockUserRes = common.ops.blockUser(user, req); await user.save(); res.respond(200, ...blockUserRes); }, @@ -1422,11 +1420,11 @@ api.deleteMessage = { middlewares: [authWithHeaders()], url: '/user/messages/:id', async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; await inboxLib.deleteMessage(user, req.params.id); - res.respond(200, ...[await inboxLib.getUserInbox(user, {asArray: false})]); + res.respond(200, ...[await inboxLib.getUserInbox(user, { asArray: false })]); }, }; @@ -1447,7 +1445,7 @@ api.clearMessages = { middlewares: [authWithHeaders()], url: '/user/messages', async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; await inboxLib.clearPMs(user); @@ -1471,8 +1469,8 @@ api.markPmsRead = { middlewares: [authWithHeaders()], url: '/user/mark-pms-read', async handler (req, res) { - let user = res.locals.user; - let markPmsResponse = common.ops.markPmsRead(user); + const { user } = res.locals; + const markPmsResponse = common.ops.markPmsRead(user); await user.save(); res.respond(200, markPmsResponse); }, @@ -1570,8 +1568,8 @@ api.setCustomDayStart = { middlewares: [authWithHeaders()], url: '/user/custom-day-start', async handler (req, res) { - let user = res.locals.user; - let dayStart = req.body.dayStart; + const { user } = res.locals; + const { dayStart } = req.body; user.preferences.dayStart = dayStart; user.lastCron = new Date(); @@ -1608,15 +1606,15 @@ api.togglePinnedItem = { middlewares: [authWithHeaders()], url: '/user/toggle-pinned-item/:type/:path', async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; const path = get(req.params, 'path'); const type = get(req.params, 'type'); - common.ops.pinnedGearUtils.togglePinnedItem(user, {type, path}, req); + common.ops.pinnedGearUtils.togglePinnedItem(user, { type, path }, req); await user.save(); - let userJson = user.toJSON(); + const userJson = user.toJSON(); res.respond(200, { pinnedItems: userJson.pinnedItems, @@ -1648,28 +1646,28 @@ api.movePinnedItem = { req.checkParams('path', res.t('taskIdRequired')).notEmpty(); req.checkParams('position', res.t('positionRequired')).notEmpty().isNumeric(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let user = res.locals.user; - let path = req.params.path; - let position = Number(req.params.position); + const { user } = res.locals; + const { path } = req.params; + const position = Number(req.params.position); // If something has been added or removed from the inAppRewards, we need // to reset pinnedItemsOrder to have the correct length. Since inAppRewards // Uses the current pinnedItemsOrder to return these in the right order, // the new reset array will be in the right order before we do the swap - let currentPinnedItems = common.inAppRewards(user); + const currentPinnedItems = common.inAppRewards(user); if (user.pinnedItemsOrder.length !== currentPinnedItems.length) { user.pinnedItemsOrder = currentPinnedItems.map(item => item.path); } // Adjust the order - let currentIndex = user.pinnedItemsOrder.findIndex(item => item === path); - let currentPinnedItemPath = user.pinnedItemsOrder[currentIndex]; + const currentIndex = user.pinnedItemsOrder.findIndex(item => item === path); + const currentPinnedItemPath = user.pinnedItemsOrder[currentIndex]; if (currentIndex === -1) { - throw new BadRequest(res.t('wrongItemPath', {path}, req.language)); + throw new BadRequest(res.t('wrongItemPath', { path }, req.language)); } // Remove the one we will move @@ -1683,7 +1681,7 @@ api.movePinnedItem = { } await user.save(); - let userJson = user.toJSON(); + const userJson = user.toJSON(); res.respond(200, userJson.pinnedItemsOrder); }, diff --git a/website/server/controllers/api-v3/user/spells.js b/website/server/controllers/api-v3/user/spells.js index 4052e72876..7a22d81822 100644 --- a/website/server/controllers/api-v3/user/spells.js +++ b/website/server/controllers/api-v3/user/spells.js @@ -3,7 +3,7 @@ import { castSpell, } from '../../../libs/spells'; -let api = {}; +const api = {}; /* NOTE this route has also an API v4 version */ diff --git a/website/server/controllers/api-v3/user/stats.js b/website/server/controllers/api-v3/user/stats.js index 97e8af637a..dea525dbf0 100644 --- a/website/server/controllers/api-v3/user/stats.js +++ b/website/server/controllers/api-v3/user/stats.js @@ -2,7 +2,7 @@ import common from '../../../../common'; import { authWithHeaders } from '../../../middlewares/auth'; -let api = {}; +const api = {}; /** * @api {post} /api/v3/user/allocate Allocate a single Stat Point (previously called Attribute Point) @@ -30,8 +30,8 @@ api.allocate = { middlewares: [authWithHeaders()], url: '/user/allocate', async handler (req, res) { - let user = res.locals.user; - let allocateRes = common.ops.allocate(user, req); + const { user } = res.locals; + const allocateRes = common.ops.allocate(user, req); await user.save(); res.respond(200, ...allocateRes); }, @@ -70,8 +70,8 @@ api.allocateBulk = { middlewares: [authWithHeaders()], url: '/user/allocate-bulk', async handler (req, res) { - let user = res.locals.user; - let allocateRes = common.ops.allocateBulk(user, req); + const { user } = res.locals; + const allocateRes = common.ops.allocateBulk(user, req); await user.save(); res.respond(200, ...allocateRes); }, @@ -127,8 +127,8 @@ api.allocateNow = { middlewares: [authWithHeaders()], url: '/user/allocate-now', async handler (req, res) { - let user = res.locals.user; - let allocateNowRes = common.ops.allocateNow(user); + const { user } = res.locals; + const allocateNowRes = common.ops.allocateNow(user); await user.save(); res.respond(200, ...allocateNowRes); }, diff --git a/website/server/controllers/api-v3/webhook.js b/website/server/controllers/api-v3/webhook.js index 62079fd93b..54aa086876 100644 --- a/website/server/controllers/api-v3/webhook.js +++ b/website/server/controllers/api-v3/webhook.js @@ -3,7 +3,7 @@ import { model as Webhook } from '../../models/webhook'; import { removeFromArray } from '../../libs/collectionManipulators'; import { NotFound, BadRequest } from '../../libs/errors'; -let api = {}; +const api = {}; /** * @apiDefine Webhook Webhook @@ -76,10 +76,10 @@ api.addWebhook = { middlewares: [authWithHeaders()], url: '/user/webhook', async handler (req, res) { - let user = res.locals.user; - let webhook = new Webhook(req.body); + const { user } = res.locals; + const webhook = new Webhook(req.body); - let existingWebhook = user.webhooks.find(hook => hook.id === webhook.id); + const existingWebhook = user.webhooks.find(hook => hook.id === webhook.id); if (existingWebhook) { throw new BadRequest(res.t('webhookIdAlreadyTaken', { id: webhook.id })); @@ -107,7 +107,7 @@ api.getWebhook = { middlewares: [authWithHeaders()], url: '/user/webhook', async handler (req, res) { - const user = res.locals.user; + const { user } = res.locals; res.respond(200, user.webhooks); }, @@ -154,13 +154,15 @@ api.updateWebhook = { middlewares: [authWithHeaders()], url: '/user/webhook/:id', async handler (req, res) { - let user = res.locals.user; - let id = req.params.id; - let webhook = user.webhooks.find(hook => hook.id === id); - let { url, label, type, enabled, options } = req.body; + const { user } = res.locals; + const { id } = req.params; + const webhook = user.webhooks.find(hook => hook.id === id); + const { + url, label, type, enabled, options, + } = req.body; if (!webhook) { - throw new NotFound(res.t('noWebhookWithId', {id})); + throw new NotFound(res.t('noWebhookWithId', { id })); } if (url) { @@ -205,13 +207,13 @@ api.deleteWebhook = { middlewares: [authWithHeaders()], url: '/user/webhook/:id', async handler (req, res) { - let user = res.locals.user; - let id = req.params.id; + const { user } = res.locals; + const { id } = req.params; - let webhook = user.webhooks.find(hook => hook.id === id); + const webhook = user.webhooks.find(hook => hook.id === id); if (!webhook) { - throw new NotFound(res.t('noWebhookWithId', {id})); + throw new NotFound(res.t('noWebhookWithId', { id })); } removeFromArray(user.webhooks, webhook); diff --git a/website/server/controllers/api-v3/world.js b/website/server/controllers/api-v3/world.js index 635ae05826..0444670df8 100644 --- a/website/server/controllers/api-v3/world.js +++ b/website/server/controllers/api-v3/world.js @@ -3,10 +3,10 @@ import { TAVERN_ID as tavernId, } from '../../models/group'; -let api = {}; +const api = {}; async function getWorldBoss () { - let tavern = await Group + const tavern = await Group .findById(tavernId) .select('quest.progress quest.key quest.active quest.extra') .exec(); @@ -34,7 +34,7 @@ api.getWorldState = { method: 'GET', url: '/world-state', async handler (req, res) { - let worldState = {}; + const worldState = {}; worldState.worldBoss = await getWorldBoss(); worldState.npcImageSuffix = 'spring'; diff --git a/website/server/controllers/api-v4/auth.js b/website/server/controllers/api-v4/auth.js index cc2b269862..87a0dd5351 100644 --- a/website/server/controllers/api-v4/auth.js +++ b/website/server/controllers/api-v4/auth.js @@ -3,7 +3,7 @@ import { } from '../../middlewares/auth'; import * as authLib from '../../libs/auth'; import { model as User } from '../../models/user'; -import {verifyUsername} from '../../libs/user/validation'; +import { verifyUsername } from '../../libs/user/validation'; const api = {}; @@ -16,14 +16,14 @@ api.verifyUsername = { async handler (req, res) { req.checkBody({ username: { - notEmpty: {errorMessage: res.t('missingUsername')}, + notEmpty: { errorMessage: res.t('missingUsername') }, }, }); const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - const user = res.locals.user; + const { user } = res.locals; const chosenUsername = req.body.username; const issues = verifyUsername(chosenUsername, res); @@ -31,10 +31,10 @@ api.verifyUsername = { if (issues.length < 1) { const existingUser = await User.findOne({ 'auth.local.lowerCaseUsername': chosenUsername.toLowerCase(), - }, {auth: 1}).exec(); + }, { auth: 1 }).exec(); if (existingUser) { - if (!user || existingUser._id !== user._id) issues.push(res.t('usernameTaken')); + if (!user || existingUser._id !== user._id) issues.push(res.t('usernameTaken')); } } diff --git a/website/server/controllers/api-v4/coupon.js b/website/server/controllers/api-v4/coupon.js index 7b28948af0..819c0f407c 100644 --- a/website/server/controllers/api-v4/coupon.js +++ b/website/server/controllers/api-v4/coupon.js @@ -25,10 +25,10 @@ api.enterCouponCode = { url: '/coupons/enter/:code', middlewares: [authWithHeaders()], async handler (req, res) { - const user = res.locals.user; + const { user } = res.locals; await couponsLib.enterCode(req, res, user); res.respond(200, user); }, }; -export default api; \ No newline at end of file +export default api; diff --git a/website/server/controllers/api-v4/inbox.js b/website/server/controllers/api-v4/inbox.js index d0f0f00455..26b1ea33c2 100644 --- a/website/server/controllers/api-v4/inbox.js +++ b/website/server/controllers/api-v4/inbox.js @@ -37,8 +37,8 @@ api.deleteMessage = { const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - const messageId = req.params.messageId; - const user = res.locals.user; + const { messageId } = req.params; + const { user } = res.locals; const deleted = await inboxLib.deleteMessage(user, messageId); if (!deleted) throw new NotFound(res.t('messageGroupChatNotFound')); @@ -64,7 +64,7 @@ api.clearMessages = { middlewares: [authWithHeaders()], url: '/inbox/clear', async handler (req, res) { - const user = res.locals.user; + const { user } = res.locals; await inboxLib.clearPMs(user); @@ -99,7 +99,7 @@ api.conversations = { middlewares: [authWithHeaders()], url: '/inbox/conversations', async handler (req, res) { - const user = res.locals.user; + const { user } = res.locals; const result = await inboxLib.listConversations(user); @@ -123,9 +123,9 @@ api.getInboxMessages = { url: '/inbox/paged-messages', middlewares: [authWithHeaders()], async handler (req, res) { - const user = res.locals.user; - const page = req.query.page; - const conversation = req.query.conversation; + const { user } = res.locals; + const { page } = req.query; + const { conversation } = req.query; const userInbox = await inboxLib.getUserInbox(user, { page, conversation, mapProps: true, diff --git a/website/server/controllers/api-v4/members.js b/website/server/controllers/api-v4/members.js index d4bd021135..bfdb4c4963 100644 --- a/website/server/controllers/api-v4/members.js +++ b/website/server/controllers/api-v4/members.js @@ -1,7 +1,7 @@ import { authWithHeaders } from '../../middlewares/auth'; import { chatReporterFactory } from '../../libs/chatReporting/chatReporterFactory'; -let api = {}; +const api = {}; /** * @api {post} /api/v4/members/flag-private-message/:messageId Flag a private message diff --git a/website/server/controllers/api-v4/user.js b/website/server/controllers/api-v4/user.js index 966f000cec..8349c4e94a 100644 --- a/website/server/controllers/api-v4/user.js +++ b/website/server/controllers/api-v4/user.js @@ -216,7 +216,7 @@ api.verifyDisplayName = { async handler (req, res) { req.checkBody({ displayName: { - notEmpty: {errorMessage: res.t('messageMissingDisplayName')}, + notEmpty: { errorMessage: res.t('messageMissingDisplayName') }, }, }); diff --git a/website/server/controllers/api-v4/user/spells.js b/website/server/controllers/api-v4/user/spells.js index 59022e88cb..f8628e4f8d 100644 --- a/website/server/controllers/api-v4/user/spells.js +++ b/website/server/controllers/api-v4/user/spells.js @@ -3,7 +3,7 @@ import { castSpell, } from '../../../libs/spells'; -let api = {}; +const api = {}; /* * NOTE most spells routes are still in the v3 controller diff --git a/website/server/controllers/top-level/auth.js b/website/server/controllers/top-level/auth.js index d153020e35..ae8e08b87f 100644 --- a/website/server/controllers/top-level/auth.js +++ b/website/server/controllers/top-level/auth.js @@ -1,6 +1,6 @@ import { validatePasswordResetCodeAndFindUser } from '../../libs/password'; -let api = {}; +const api = {}; // Internal authentication routes @@ -10,7 +10,7 @@ api.resetPasswordSetNewOne = { url: '/static/user/auth/local/reset-password-set-new-one', runCron: false, async handler (req, res) { - const code = req.query.code; + const { code } = req.query; const user = await validatePasswordResetCodeAndFindUser(code); const isValidCode = Boolean(user); diff --git a/website/server/controllers/top-level/dataexport.js b/website/server/controllers/top-level/dataexport.js index d343c6faf4..a1e97d5547 100644 --- a/website/server/controllers/top-level/dataexport.js +++ b/website/server/controllers/top-level/dataexport.js @@ -1,18 +1,18 @@ -import { authWithSession } from '../../middlewares/auth'; -import { model as User } from '../../models/user'; -import * as inboxLib from '../../libs/inbox'; -import * as Tasks from '../../models/task'; -import { - NotFound, -} from '../../libs/errors'; import _ from 'lodash'; -import csvStringify from '../../libs/csvStringify'; import moment from 'moment'; import * as js2xml from 'js2xmlparser'; import Pageres from 'pageres'; import nconf from 'nconf'; import got from 'got'; import md from 'habitica-markdown'; +import csvStringify from '../../libs/csvStringify'; +import { + NotFound, +} from '../../libs/errors'; +import * as Tasks from '../../models/task'; +import * as inboxLib from '../../libs/inbox'; +import { model as User } from '../../models/user'; +import { authWithSession } from '../../middlewares/auth'; import { S3, } from '../../libs/aws'; @@ -21,7 +21,7 @@ const S3_BUCKET = nconf.get('S3_BUCKET'); const BASE_URL = nconf.get('BASE_URL'); -let api = {}; +const api = {}; /** * @apiDefine DataExport Data Export @@ -49,14 +49,14 @@ api.exportUserHistory = { url: '/export/history.csv', middlewares: [authWithSession], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; - let tasks = await Tasks.Task.find({ + const tasks = await Tasks.Task.find({ userId: user._id, - type: {$in: ['habit', 'daily']}, + type: { $in: ['habit', 'daily'] }, }).exec(); - let output = [ + const output = [ ['Task Name', 'Task ID', 'Task Type', 'Date', 'Value'], ]; @@ -77,7 +77,7 @@ api.exportUserHistory = { 'Content-disposition': 'attachment; filename=habitica-tasks-history.csv', }); - let csvRes = await csvStringify(output); + const csvRes = await csvStringify(output); res.status(200).send(csvRes); }, }; @@ -85,7 +85,7 @@ api.exportUserHistory = { // Convert user to json and attach tasks divided by type and inbox messages // at user.tasks[`${taskType}s`] (user.tasks.{dailys/habits/...}) async function _getUserDataForExport (user, xmlMode = false) { - let userData = user.toJSON(); + const userData = user.toJSON(); userData.tasks = {}; userData.inbox.messages = {}; @@ -119,19 +119,15 @@ async function _getUserDataForExport (user, xmlMode = false) { .value(); // _id gets parsed as an bytearray => which gets casted to a chararray => "weird chars" - userData.unpinnedItems = userData.unpinnedItems.map(i => { - return { - path: i.path, - type: i.type, - }; - }); + userData.unpinnedItems = userData.unpinnedItems.map(i => ({ + path: i.path, + type: i.type, + })); - userData.pinnedItems = userData.pinnedItems.map(i => { - return { - path: i.path, - type: i.type, - }; - }); + userData.pinnedItems = userData.pinnedItems.map(i => ({ + path: i.path, + type: i.type, + })); } return userData; @@ -149,13 +145,13 @@ api.exportUserDataJson = { url: '/export/userdata.json', middlewares: [authWithSession], async handler (req, res) { - let userData = await _getUserDataForExport(res.locals.user); + const userData = await _getUserDataForExport(res.locals.user); res.set({ 'Content-Type': 'application/json', 'Content-disposition': 'attachment; filename=habitica-user-data.json', }); - let jsonRes = JSON.stringify(userData); + const jsonRes = JSON.stringify(userData); res.status(200).send(jsonRes); }, @@ -173,7 +169,7 @@ api.exportUserDataXml = { url: '/export/userdata.xml', middlewares: [authWithSession], async handler (req, res) { - let userData = await _getUserDataForExport(res.locals.user, true); + const userData = await _getUserDataForExport(res.locals.user, true); res.set({ 'Content-Type': 'text/xml', @@ -207,19 +203,19 @@ api.exportUserAvatarHtml = { async handler (req, res) { req.checkParams('memberId', res.t('memberIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let memberId = req.params.memberId; - let member = await User + const { memberId } = req.params; + const member = await User .findById(memberId) .select('stats profile items achievements preferences backer contributor') .exec(); - if (!member) throw new NotFound(res.t('userWithIDNotFound', {userId: memberId})); + if (!member) throw new NotFound(res.t('userWithIDNotFound', { userId: memberId })); res.render('avatar-static', { title: member.profile.name, - env: _.defaults({user: member}, res.locals.habitrpg), + env: _.defaults({ user: member }, res.locals.habitrpg), }); }, }; @@ -239,13 +235,13 @@ api.exportUserAvatarPng = { async handler (req, res) { req.checkParams('memberId', res.t('memberIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let memberId = req.params.memberId; + const { memberId } = req.params; - let filename = `avatars/${memberId}.png`; - let s3url = `https://${S3_BUCKET}.s3.amazonaws.com/${filename}`; + const filename = `avatars/${memberId}.png`; + const s3url = `https://${S3_BUCKET}.s3.amazonaws.com/${filename}`; let response; try { @@ -269,17 +265,17 @@ api.exportUserAvatarPng = { }) .run(); - let s3upload = S3.upload({ + const s3upload = S3.upload({ Bucket: S3_BUCKET, Key: filename, ACL: 'public-read', StorageClass: 'REDUCED_REDUNDANCY', ContentType: 'image/png', - Expires: moment().add({minutes: 5}).toDate(), + Expires: moment().add({ minutes: 5 }).toDate(), Body: pageBuffer, }); - let s3res = await new Promise((resolve, reject) => { + const s3res = await new Promise((resolve, reject) => { s3upload.send((err, s3uploadRes) => { if (err) { reject(err); @@ -305,9 +301,9 @@ api.exportUserPrivateMessages = { url: '/export/inbox.html', middlewares: [authWithSession], async handler (req, res) { - const user = res.locals.user; + const { user } = res.locals; - const timezoneOffset = user.preferences.timezoneOffset; + const { timezoneOffset } = user.preferences; const dateFormat = user.preferences.dateFormat.toUpperCase(); const TO = res.t('to'); const FROM = res.t('from'); @@ -317,10 +313,10 @@ api.exportUserPrivateMessages = { let messages = ''; inbox.forEach((message, index) => { - let recipientLabel = message.sent ? TO : FROM; - let messageUser = message.user; - let timestamp = moment.utc(message.timestamp).zone(timezoneOffset).format(`${dateFormat} HH:mm:ss`); - let text = md.render(message.text); + const recipientLabel = message.sent ? TO : FROM; + const messageUser = message.user; + const timestamp = moment.utc(message.timestamp).zone(timezoneOffset).format(`${dateFormat} HH:mm:ss`); + const text = md.render(message.text); index = `(${index + 1}/${inbox.length})`; messages += `

diff --git a/website/server/controllers/top-level/email.js b/website/server/controllers/top-level/email.js index a4da992f28..43a7725b6a 100644 --- a/website/server/controllers/top-level/email.js +++ b/website/server/controllers/top-level/email.js @@ -5,7 +5,7 @@ import { NotFound, } from '../../libs/errors'; -let api = {}; +const api = {}; /** * @api {get} /email/unsubscribe Unsubscribe an email or user from email notifications @@ -27,28 +27,28 @@ api.unsubscribe = { async handler (req, res) { req.checkQuery({ code: { - notEmpty: {errorMessage: res.t('missingUnsubscriptionCode')}, + notEmpty: { errorMessage: res.t('missingUnsubscriptionCode') }, }, }); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let data = JSON.parse(decrypt(req.query.code)); + const data = JSON.parse(decrypt(req.query.code)); if (data._id) { - let userUpdated = await User.update( - {_id: data._id}, - { $set: {'preferences.emailNotifications.unsubscribeFromAll': true}} + const userUpdated = await User.update( + { _id: data._id }, + { $set: { 'preferences.emailNotifications.unsubscribeFromAll': true } }, ).exec(); if (userUpdated.nModified !== 1) throw new NotFound(res.t('userNotFound')); res.send(`

${res.t('unsubscribedSuccessfully')}

${res.t('unsubscribedTextUsers')}`); } else { - let unsubscribedEmail = await EmailUnsubscription.findOne({email: data.email.toLowerCase()}).exec(); - if (!unsubscribedEmail) await EmailUnsubscription.create({email: data.email.toLowerCase()}); + const unsubscribedEmail = await EmailUnsubscription.findOne({ email: data.email.toLowerCase() }).exec(); + if (!unsubscribedEmail) await EmailUnsubscription.create({ email: data.email.toLowerCase() }); - let okResponse = `

${res.t('unsubscribedSuccessfully')}

${res.t('unsubscribedTextOthers')}`; + const okResponse = `

${res.t('unsubscribedSuccessfully')}

${res.t('unsubscribedTextOthers')}`; res.send(okResponse); } }, diff --git a/website/server/controllers/top-level/pages.js b/website/server/controllers/top-level/pages.js index 063f413e01..2c3b9d7836 100644 --- a/website/server/controllers/top-level/pages.js +++ b/website/server/controllers/top-level/pages.js @@ -1,6 +1,6 @@ import { serveClient } from '../../libs/client'; -let api = {}; +const api = {}; // All requests to /new_app (except /new_app/static) should serve the new client in development // if (IS_PROD && IS_NEW_CLIENT_ENABLED) { diff --git a/website/server/controllers/top-level/payments/amazon.js b/website/server/controllers/top-level/payments/amazon.js index 74c4d3696b..64b3269d2b 100644 --- a/website/server/controllers/top-level/payments/amazon.js +++ b/website/server/controllers/top-level/payments/amazon.js @@ -7,7 +7,7 @@ import { } from '../../../middlewares/auth'; import shared from '../../../../common'; -let api = {}; +const api = {}; /** * @apiIgnore Payments are considered part of the private API @@ -16,13 +16,13 @@ let api = {}; * @apiGroup Payments * * @apiSuccess {Object} data Empty object - **/ + * */ api.verifyAccessToken = { method: 'POST', url: '/amazon/verifyAccessToken', middlewares: [authWithHeaders()], async handler (req, res) { - let accessToken = req.body.access_token; + const accessToken = req.body.access_token; if (!accessToken) throw new BadRequest('Missing req.body.access_token'); @@ -39,17 +39,17 @@ api.verifyAccessToken = { * @apiGroup Payments * * @apiSuccess {String} data.orderReferenceId The order reference id. - **/ + * */ api.createOrderReferenceId = { method: 'POST', url: '/amazon/createOrderReferenceId', middlewares: [authWithHeaders()], async handler (req, res) { - let billingAgreementId = req.body.billingAgreementId; + const { billingAgreementId } = req.body; if (!billingAgreementId) throw new BadRequest('Missing req.body.billingAgreementId'); - let response = await amzLib.createOrderReferenceId({ + const response = await amzLib.createOrderReferenceId({ Id: billingAgreementId, IdType: 'BillingAgreement', ConfirmNow: false, @@ -68,19 +68,21 @@ api.createOrderReferenceId = { * @apiGroup Payments * * @apiSuccess {Object} data Empty object - **/ + * */ api.checkout = { method: 'POST', url: '/amazon/checkout', middlewares: [authWithHeaders()], async handler (req, res) { - let gift = req.body.gift; - let user = res.locals.user; - let orderReferenceId = req.body.orderReferenceId; + const { gift } = req.body; + const { user } = res.locals; + const { orderReferenceId } = req.body; if (!orderReferenceId) throw new BadRequest('Missing req.body.orderReferenceId'); - await amzLib.checkout({gift, user, orderReferenceId, headers: req.headers}); + await amzLib.checkout({ + gift, user, orderReferenceId, headers: req.headers, + }); res.respond(200); }, @@ -93,17 +95,17 @@ api.checkout = { * @apiGroup Payments * * @apiSuccess {Object} data Empty object - **/ + * */ api.subscribe = { method: 'POST', url: '/amazon/subscribe', middlewares: [authWithHeaders()], async handler (req, res) { - let billingAgreementId = req.body.billingAgreementId; - let sub = req.body.subscription ? shared.content.subscriptionBlocks[req.body.subscription] : false; - let coupon = req.body.coupon; - let user = res.locals.user; - let groupId = req.body.groupId; + const { billingAgreementId } = req.body; + const sub = req.body.subscription ? shared.content.subscriptionBlocks[req.body.subscription] : false; + const { coupon } = req.body; + const { user } = res.locals; + const { groupId } = req.body; await amzLib.subscribe({ billingAgreementId, @@ -123,16 +125,16 @@ api.subscribe = { * @api {get} /amazon/subscribe/cancel Amazon Payments: subscribe cancel * @apiName AmazonSubscribe * @apiGroup Payments - **/ + * */ api.subscribeCancel = { method: 'GET', url: '/amazon/subscribe/cancel', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let groupId = req.query.groupId; + const { user } = res.locals; + const { groupId } = req.query; - await amzLib.cancelSubscription({user, groupId, headers: req.headers}); + await amzLib.cancelSubscription({ user, groupId, headers: req.headers }); if (req.query.noRedirect) { res.respond(200); diff --git a/website/server/controllers/top-level/payments/iap.js b/website/server/controllers/top-level/payments/iap.js index a490181ea0..1afe62e8b3 100644 --- a/website/server/controllers/top-level/payments/iap.js +++ b/website/server/controllers/top-level/payments/iap.js @@ -7,7 +7,7 @@ import { import googlePayments from '../../../libs/payments/google'; import applePayments from '../../../libs/payments/apple'; -let api = {}; +const api = {}; // TODO missing tests @@ -16,14 +16,14 @@ let api = {}; * @api {post} /iap/android/verify Android Verify IAP * @apiName IapAndroidVerify * @apiGroup Payments - **/ + * */ api.iapAndroidVerify = { method: 'POST', url: '/iap/android/verify', middlewares: [authWithHeaders()], async handler (req, res) { if (!req.body.transaction) throw new BadRequest(res.t('missingReceipt')); - let googleRes = await googlePayments.verifyGemPurchase({ + const googleRes = await googlePayments.verifyGemPurchase({ user: res.locals.user, receipt: req.body.transaction.receipt, signature: req.body.transaction.signature, @@ -39,7 +39,7 @@ api.iapAndroidVerify = { * @api {post} /iap/android/subscription Android Subscribe * @apiName IapAndroidSubscribe * @apiGroup Payments - **/ + * */ api.iapSubscriptionAndroid = { method: 'POST', url: '/iap/android/subscribe', @@ -57,7 +57,7 @@ api.iapSubscriptionAndroid = { * @api {post} /iap/android/norenew-subscribe Android non-renewing subscription IAP * @apiName iapSubscriptionAndroidNoRenew * @apiGroup Payments - **/ + * */ api.iapSubscriptionAndroidNoRenew = { method: 'POST', url: '/iap/android/norenew-subscribe', @@ -84,13 +84,13 @@ api.iapSubscriptionAndroidNoRenew = { * @api {get} /iap/android/subscribe/cancel Google Payments: subscribe cancel * @apiName AndroidSubscribeCancel * @apiGroup Payments - **/ + * */ api.iapCancelSubscriptionAndroid = { method: 'GET', url: '/iap/android/subscribe/cancel', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; await googlePayments.cancelSubscribe(user, req.headers); @@ -107,14 +107,14 @@ api.iapCancelSubscriptionAndroid = { * @api {post} /iap/ios/verify iOS Verify IAP * @apiName IapiOSVerify * @apiGroup Payments - **/ + * */ api.iapiOSVerify = { method: 'POST', url: '/iap/ios/verify', middlewares: [authWithHeaders()], async handler (req, res) { if (!req.body.transaction) throw new BadRequest(res.t('missingReceipt')); - let appleRes = await applePayments.verifyGemPurchase({ + const appleRes = await applePayments.verifyGemPurchase({ user: res.locals.user, receipt: req.body.transaction.receipt, gift: req.body.gift, @@ -129,7 +129,7 @@ api.iapiOSVerify = { * @api {post} /iap/android/subscription iOS Subscribe * @apiName IapiOSSubscribe * @apiGroup Payments - **/ + * */ api.iapSubscriptioniOS = { method: 'POST', url: '/iap/ios/subscribe', @@ -149,13 +149,13 @@ api.iapSubscriptioniOS = { * @api {get} /iap/android/subscribe/cancel Apple Payments: subscribe cancel * @apiName iOSSubscribeCancel * @apiGroup Payments - **/ + * */ api.iapCancelSubscriptioniOS = { method: 'GET', url: '/iap/ios/subscribe/cancel', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; await applePayments.cancelSubscribe(user, req.headers); @@ -172,7 +172,7 @@ api.iapCancelSubscriptioniOS = { * @api {post} /iap/ios/norenew-subscribe iOS Verify IAP * @apiName IapiOSVerify * @apiGroup Payments - **/ + * */ api.iapSubscriptioniOSNoRenew = { method: 'POST', url: '/iap/ios/norenew-subscribe', @@ -186,7 +186,8 @@ api.iapSubscriptioniOSNoRenew = { user: res.locals.user, receipt: req.body.transaction.receipt, gift: req.body.gift, - headers: req.headers}); + headers: req.headers, + }); res.respond(200); }, diff --git a/website/server/controllers/top-level/payments/paypal.js b/website/server/controllers/top-level/payments/paypal.js index 2088bfccb4..627ad415c1 100644 --- a/website/server/controllers/top-level/payments/paypal.js +++ b/website/server/controllers/top-level/payments/paypal.js @@ -10,23 +10,23 @@ import { } from '../../../libs/errors'; import apiError from '../../../libs/apiError'; -let api = {}; +const api = {}; /** * @apiIgnore Payments are considered part of the private API * @api {get} /paypal/checkout Paypal: checkout * @apiName PaypalCheckout * @apiGroup Payments - **/ + * */ api.checkout = { method: 'GET', url: '/paypal/checkout', middlewares: [authWithSession], async handler (req, res) { - let gift = req.query.gift ? JSON.parse(req.query.gift) : undefined; + const gift = req.query.gift ? JSON.parse(req.query.gift) : undefined; req.session.gift = req.query.gift; - let link = await paypalPayments.checkout({gift, user: res.locals.user}); + const link = await paypalPayments.checkout({ gift, user: res.locals.user }); if (req.query.noRedirect) { res.respond(200); @@ -41,22 +41,24 @@ api.checkout = { * @api {get} /paypal/checkout/success Paypal: checkout success * @apiName PaypalCheckoutSuccess * @apiGroup Payments - **/ + * */ api.checkoutSuccess = { method: 'GET', url: '/paypal/checkout/success', middlewares: [authWithSession], async handler (req, res) { - let paymentId = req.query.paymentId; - let customerId = req.query.PayerID; - let user = res.locals.user; - let gift = req.session.gift ? JSON.parse(req.session.gift) : undefined; + const { paymentId } = req.query; + const customerId = req.query.PayerID; + const { user } = res.locals; + const gift = req.session.gift ? JSON.parse(req.session.gift) : undefined; delete req.session.gift; if (!paymentId) throw new BadRequest(apiError('missingPaymentId')); if (!customerId) throw new BadRequest(apiError('missingCustomerId')); - await paypalPayments.checkoutSuccess({user, gift, paymentId, customerId}); + await paypalPayments.checkoutSuccess({ + user, gift, paymentId, customerId, + }); if (req.query.noRedirect) { res.respond(200); @@ -71,7 +73,7 @@ api.checkoutSuccess = { * @api {get} /paypal/subscribe Paypal: subscribe * @apiName PaypalSubscribe * @apiGroup Payments - **/ + * */ api.subscribe = { method: 'GET', url: '/paypal/subscribe', @@ -79,10 +81,10 @@ api.subscribe = { async handler (req, res) { if (!req.query.sub) throw new BadRequest(apiError('missingSubKey')); - let sub = shared.content.subscriptionBlocks[req.query.sub]; - let coupon = req.query.coupon; + const sub = shared.content.subscriptionBlocks[req.query.sub]; + const { coupon } = req.query; - let link = await paypalPayments.subscribe({sub, coupon}); + const link = await paypalPayments.subscribe({ sub, coupon }); req.session.paypalBlock = req.query.sub; req.session.groupId = req.query.groupId; @@ -100,24 +102,26 @@ api.subscribe = { * @api {get} /paypal/subscribe/success Paypal: subscribe success * @apiName PaypalSubscribeSuccess * @apiGroup Payments - **/ + * */ api.subscribeSuccess = { method: 'GET', url: '/paypal/subscribe/success', middlewares: [authWithSession], async handler (req, res) { - let user = res.locals.user; + const { user } = res.locals; if (!req.session.paypalBlock) throw new BadRequest(apiError('missingPaypalBlock')); - let block = shared.content.subscriptionBlocks[req.session.paypalBlock]; - let groupId = req.session.groupId; - let token = req.query.token; + const block = shared.content.subscriptionBlocks[req.session.paypalBlock]; + const { groupId } = req.session; + const { token } = req.query; delete req.session.paypalBlock; delete req.session.groupId; - await paypalPayments.subscribeSuccess({user, block, groupId, token, headers: req.headers}); + await paypalPayments.subscribeSuccess({ + user, block, groupId, token, headers: req.headers, + }); if (req.query.noRedirect) { res.respond(200); @@ -132,16 +136,16 @@ api.subscribeSuccess = { * @api {get} /paypal/subscribe/cancel Paypal: subscribe cancel * @apiName PaypalSubscribeCancel * @apiGroup Payments - **/ + * */ api.subscribeCancel = { method: 'GET', url: '/paypal/subscribe/cancel', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let groupId = req.query.groupId; + const { user } = res.locals; + const { groupId } = req.query; - await paypalPayments.subscribeCancel({user, groupId}); + await paypalPayments.subscribeCancel({ user, groupId }); if (req.query.noRedirect) { res.respond(200); @@ -159,7 +163,7 @@ api.subscribeCancel = { * @api {post} /paypal/ipn Paypal IPN * @apiName PaypalIpn * @apiGroup Payments - **/ + * */ api.ipn = { method: 'POST', url: '/paypal/ipn', diff --git a/website/server/controllers/top-level/payments/stripe.js b/website/server/controllers/top-level/payments/stripe.js index 4bcb45229c..9d8c63a8f6 100644 --- a/website/server/controllers/top-level/payments/stripe.js +++ b/website/server/controllers/top-level/payments/stripe.js @@ -4,7 +4,7 @@ import { } from '../../../middlewares/auth'; import stripePayments from '../../../libs/payments/stripe'; -let api = {}; +const api = {}; /** * @apiIgnore Payments are considered part of the private API @@ -19,21 +19,23 @@ let api = {}; * @apiParam {String} coupon Query parameter - coupon for the matching subscription, required only for certain subscriptions * * @apiSuccess {Object} data Empty object - **/ + * */ api.checkout = { method: 'POST', url: '/stripe/checkout', middlewares: [authWithHeaders()], async handler (req, res) { // @TODO: These quer params need to be changed to body - let token = req.body.id; - let user = res.locals.user; - let gift = req.query.gift ? JSON.parse(req.query.gift) : undefined; - let sub = req.query.sub ? shared.content.subscriptionBlocks[req.query.sub] : false; - let groupId = req.query.groupId; - let coupon = req.query.coupon; + const token = req.body.id; + const { user } = res.locals; + const gift = req.query.gift ? JSON.parse(req.query.gift) : undefined; + const sub = req.query.sub ? shared.content.subscriptionBlocks[req.query.sub] : false; + const { groupId } = req.query; + const { coupon } = req.query; - await stripePayments.checkout({token, user, gift, sub, groupId, coupon}); + await stripePayments.checkout({ + token, user, gift, sub, groupId, coupon, + }); res.respond(200, {}); }, @@ -48,17 +50,17 @@ api.checkout = { * @apiParam {String} id Body parameter - The token * * @apiSuccess {Object} data Empty object - **/ + * */ api.subscribeEdit = { method: 'POST', url: '/stripe/subscribe/edit', middlewares: [authWithHeaders()], async handler (req, res) { - let token = req.body.id; - let groupId = req.body.groupId; - let user = res.locals.user; + const token = req.body.id; + const { groupId } = req.body; + const { user } = res.locals; - await stripePayments.editSubscription({token, groupId, user}); + await stripePayments.editSubscription({ token, groupId, user }); res.respond(200, {}); }, @@ -69,16 +71,16 @@ api.subscribeEdit = { * @api {get} /stripe/subscribe/cancel Cancel Stripe subscription * @apiName StripeSubscribeCancel * @apiGroup Payments - **/ + * */ api.subscribeCancel = { method: 'GET', url: '/stripe/subscribe/cancel', middlewares: [authWithHeaders()], async handler (req, res) { - let user = res.locals.user; - let groupId = req.query.groupId; + const { user } = res.locals; + const { groupId } = req.query; - await stripePayments.cancelSubscription({user, groupId}); + await stripePayments.cancelSubscription({ user, groupId }); if (req.query.noRedirect) { res.respond(200); @@ -92,7 +94,7 @@ api.handleWebhooks = { method: 'POST', url: '/stripe/webhooks', async handler (req, res) { - await stripePayments.handleWebhooks({requestBody: req.body}); + await stripePayments.handleWebhooks({ requestBody: req.body }); return res.respond(200, {}); }, diff --git a/website/server/controllers/top-level/qrcodes.js b/website/server/controllers/top-level/qrcodes.js index 4df8b3c89f..588b5c69bd 100644 --- a/website/server/controllers/top-level/qrcodes.js +++ b/website/server/controllers/top-level/qrcodes.js @@ -1,4 +1,4 @@ -let api = {}; +const api = {}; api.redirectProfileQRCode = { method: 'GET', @@ -7,7 +7,7 @@ api.redirectProfileQRCode = { async handler (req, res) { req.checkParams('memberid', res.t('memberIdRequired')).notEmpty().isUUID(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; res.redirect(301, `/static/front/#?memberId=${req.params.memberid}`); diff --git a/website/server/index.js b/website/server/index.js index a902478905..d04fd9a3a9 100644 --- a/website/server/index.js +++ b/website/server/index.js @@ -1,4 +1,4 @@ -'use strict'; + /* eslint-disable global-require, no-process-env */ // Register babel hook so we can write the real entry file (server.js) in ES6 @@ -8,7 +8,6 @@ if (process.env.NODE_ENV !== 'production') { } // Initialize configuration BEFORE anything -const setupNconf = require('./libs/setupNconf').default; setupNconf(); const nconf = require('nconf'); @@ -16,6 +15,7 @@ const nconf = require('nconf'); require('./libs/gcpTraceAgent'); const cluster = require('cluster'); +const setupNconf = require('./libs/setupNconf').default; const logger = require('./libs/logger').default; const IS_PROD = nconf.get('IS_PROD'); @@ -29,8 +29,8 @@ if (CORES !== 0 && cluster.isMaster && (IS_DEV || IS_PROD)) { cluster.fork(); } - cluster.on('disconnect', function onWorkerDisconnect (worker) { - let w = cluster.fork(); // replace the dead worker + cluster.on('disconnect', worker => { + const w = cluster.fork(); // replace the dead worker logger.info('[%s] [master:%s] worker:%s disconnect! new worker:%s fork', new Date(), process.pid, worker.process.pid, w.process.pid); }); diff --git a/website/server/libs/analyticsService.js b/website/server/libs/analyticsService.js index 0f3b1f8ff7..5aed39e22e 100644 --- a/website/server/libs/analyticsService.js +++ b/website/server/libs/analyticsService.js @@ -26,19 +26,19 @@ const PLATFORM_MAP = Object.freeze({ let amplitude; if (AMPLITUDE_TOKEN) amplitude = new Amplitude(AMPLITUDE_TOKEN); -let ga = googleAnalytics(GA_TOKEN); +const ga = googleAnalytics(GA_TOKEN); const Content = common.content; function _lookUpItemName (itemKey) { if (!itemKey) return; - let gear = Content.gear.flat[itemKey]; - let egg = Content.eggs[itemKey]; - let food = Content.food[itemKey]; - let hatchingPotion = Content.hatchingPotions[itemKey]; - let quest = Content.quests[itemKey]; - let spell = Content.special[itemKey]; + const gear = Content.gear.flat[itemKey]; + const egg = Content.eggs[itemKey]; + const food = Content.food[itemKey]; + const hatchingPotion = Content.hatchingPotions[itemKey]; + const quest = Content.quests[itemKey]; + const spell = Content.special[itemKey]; let itemName; @@ -60,7 +60,7 @@ function _lookUpItemName (itemKey) { } function _formatUserData (user) { - let properties = {}; + const properties = {}; if (user.stats) { properties.Class = user.stats.class; @@ -124,8 +124,8 @@ function _formatUserAgentForAmplitude (platform, agentString) { return 'Unknown'; } - let agent = useragent.lookup(agentString).toJSON(); - let formattedAgent = {}; + const agent = useragent.lookup(agentString).toJSON(); + const formattedAgent = {}; if (platform === 'iOS' || platform === 'Android') { formattedAgent.name = agent.os.family; formattedAgent.version = `${agent.os.major}.${agent.os.minor}.${agent.os.patch}`; @@ -145,10 +145,10 @@ function _formatUUIDForAmplitude (uuid) { } function _formatDataForAmplitude (data) { - let event_properties = omit(data, AMPLITUDE_PROPERTIES_TO_SCRUB); - let platform = _formatPlatformForAmplitude(data.headers && data.headers['x-client']); - let agent = _formatUserAgentForAmplitude(platform, data.headers && data.headers['user-agent']); - let ampData = { + const event_properties = omit(data, AMPLITUDE_PROPERTIES_TO_SCRUB); + const platform = _formatPlatformForAmplitude(data.headers && data.headers['x-client']); + const agent = _formatUserAgentForAmplitude(platform, data.headers && data.headers['user-agent']); + const ampData = { user_id: _formatUUIDForAmplitude(data.uuid), platform, os_name: agent.name, @@ -160,7 +160,7 @@ function _formatDataForAmplitude (data) { ampData.user_properties = _formatUserData(data.user); } - let itemName = _lookUpItemName(data.itemKey); + const itemName = _lookUpItemName(data.itemKey); if (itemName) { ampData.event_properties.itemName = itemName; @@ -169,7 +169,7 @@ function _formatDataForAmplitude (data) { } function _sendDataToAmplitude (eventType, data) { - let amplitudeData = _formatDataForAmplitude(data); + const amplitudeData = _formatDataForAmplitude(data); amplitudeData.event_type = eventType; @@ -181,7 +181,7 @@ function _sendDataToAmplitude (eventType, data) { function _generateLabelForGoogleAnalytics (data) { let label; - each(GA_POSSIBLE_LABELS, (key) => { + each(GA_POSSIBLE_LABELS, key => { if (data[key]) { label = data[key]; return false; // exit each early @@ -194,7 +194,7 @@ function _generateLabelForGoogleAnalytics (data) { function _generateValueForGoogleAnalytics (data) { let value; - each(GA_POSSIBLE_VALUES, (key) => { + each(GA_POSSIBLE_VALUES, key => { if (data[key]) { value = data[key]; return false; // exit each early @@ -205,25 +205,25 @@ function _generateValueForGoogleAnalytics (data) { } function _sendDataToGoogle (eventType, data) { - let eventData = { + const eventData = { ec: data.gaCategory || data.category || 'behavior', ea: eventType, }; - let label = _generateLabelForGoogleAnalytics(data); + const label = _generateLabelForGoogleAnalytics(data); if (label) { eventData.el = label; } - let value = _generateValueForGoogleAnalytics(data); + const value = _generateValueForGoogleAnalytics(data); if (value) { eventData.ev = value; } const promise = new Promise((resolve, reject) => { - ga.event(eventData, (err) => { + ga.event(eventData, err => { if (err) return reject(err); resolve(); }); @@ -233,7 +233,7 @@ function _sendDataToGoogle (eventType, data) { } function _sendPurchaseDataToAmplitude (data) { - let amplitudeData = _formatDataForAmplitude(data); + const amplitudeData = _formatDataForAmplitude(data); amplitudeData.event_type = 'purchase'; amplitudeData.revenue = data.purchaseValue; @@ -244,17 +244,17 @@ function _sendPurchaseDataToAmplitude (data) { } function _sendPurchaseDataToGoogle (data) { - let label = data.paymentMethod; - let type = data.purchaseType; - let price = data.purchaseValue; - let qty = data.quantity; - let sku = data.sku; - let itemKey = data.itemPurchased; + const label = data.paymentMethod; + const type = data.purchaseType; + const price = data.purchaseValue; + const qty = data.quantity; + const { sku } = data; + const itemKey = data.itemPurchased; let variation = type; if (data.gift) variation += ' - Gift'; - let eventData = { + const eventData = { ec: 'commerce', ea: type, el: label, @@ -262,7 +262,7 @@ function _sendPurchaseDataToGoogle (data) { }; const eventPromise = new Promise((resolve, reject) => { - ga.event(eventData, (err) => { + ga.event(eventData, err => { if (err) return reject(err); resolve(); }); @@ -295,7 +295,7 @@ function _setOnce (dataToSetOnce, uuid) { // There's no error handling directly here because it's handled inside _sendDataTo{Amplitude|Google} async function track (eventType, data) { - let promises = [ + const promises = [ _sendDataToAmplitude(eventType, data), _sendDataToGoogle(eventType, data), ]; diff --git a/website/server/libs/apiError.js b/website/server/libs/apiError.js index ac0066636a..0121a0b907 100644 --- a/website/server/libs/apiError.js +++ b/website/server/libs/apiError.js @@ -11,7 +11,7 @@ export default function (msgKey, vars = {}) { if (!message) message = commonErrors[msgKey]; if (!message) throw new Error(`Error processing the API message "${msgKey}".`); - let clonedVars = vars ? _.clone(vars) : {}; + const clonedVars = vars ? _.clone(vars) : {}; // TODO cache the result of template() ? More memory usage, faster output return _.template(message)(clonedVars); diff --git a/website/server/libs/auth/index.js b/website/server/libs/auth/index.js index e18b6cf63f..a4423557db 100644 --- a/website/server/libs/auth/index.js +++ b/website/server/libs/auth/index.js @@ -1,17 +1,17 @@ +import moment from 'moment'; import { BadRequest, NotAuthorized, NotFound, -} from '../../libs/errors'; -import * as passwordUtils from '../../libs/password'; +} from '../errors'; +import * as passwordUtils from '../password'; import { model as User } from '../../models/user'; import { model as EmailUnsubscription } from '../../models/emailUnsubscription'; -import { sendTxn as sendTxnEmail } from '../../libs/email'; +import { sendTxn as sendTxnEmail } from '../email'; import common from '../../../common'; -import logger from '../../libs/logger'; -import { decrypt } from '../../libs/encryption'; +import logger from '../logger'; +import { decrypt } from '../encryption'; import { model as Group } from '../../models/group'; -import moment from 'moment'; import { loginSocial } from './social.js'; import { loginRes } from './utils'; import { verifyUsername } from '../user/validation'; @@ -24,23 +24,25 @@ async function _handleGroupInvitation (user, invite) { // wrapping the code in a try because we don't want it to prevent the user from signing up // that's why errors are not translated try { - let {sentAt, id: groupId, inviter} = JSON.parse(decrypt(invite)); + let { sentAt, id: groupId, inviter } = JSON.parse(decrypt(invite)); // check that the invite has not expired (after 7 days) if (sentAt && moment().subtract(7, 'days').isAfter(sentAt)) { - let err = new Error('Invite expired.'); + const err = new Error('Invite expired.'); err.privateData = invite; throw err; } - let group = await Group.getGroup({user, optionalMembership: true, groupId, fields: 'name type'}); + const group = await Group.getGroup({ + user, optionalMembership: true, groupId, fields: 'name type', + }); if (!group) throw new NotFound('Group not found.'); if (group.type === 'party') { - user.invitations.party = {id: group._id, name: group.name, inviter}; + user.invitations.party = { id: group._id, name: group.name, inviter }; user.invitations.parties.push(user.invitations.party); } else { - user.invitations.guilds.push({id: group._id, name: group.name, inviter}); + user.invitations.guilds.push({ id: group._id, name: group.name, inviter }); } // award the inviter with 'Invited a Friend' achievement @@ -64,9 +66,7 @@ function hasBackupAuth (user, networkToRemove) { return true; } - let hasAlternateNetwork = common.constants.SUPPORTED_SOCIAL_NETWORKS.find((network) => { - return network.key !== networkToRemove && user.auth[network.key].id; - }); + const hasAlternateNetwork = common.constants.SUPPORTED_SOCIAL_NETWORKS.find(network => network.key !== networkToRemove && user.auth[network.key].id); return hasAlternateNetwork; } @@ -79,22 +79,22 @@ async function registerLocal (req, res, { isV3 = false }) { notEmpty: true, errorMessage: res.t('missingUsername'), // TODO use the constants in the error message above - isLength: {options: {min: USERNAME_LENGTH_MIN, max: USERNAME_LENGTH_MAX}, errorMessage: res.t('usernameIssueLength')}, - matches: {options: /^[-_a-zA-Z0-9]+$/, errorMessage: res.t('usernameIssueInvalidCharacters')}, + isLength: { options: { min: USERNAME_LENGTH_MIN, max: USERNAME_LENGTH_MAX }, errorMessage: res.t('usernameIssueLength') }, + matches: { options: /^[-_a-zA-Z0-9]+$/, errorMessage: res.t('usernameIssueInvalidCharacters') }, }, email: { notEmpty: true, errorMessage: res.t('missingEmail'), - isEmail: {errorMessage: res.t('notAnEmail')}, + isEmail: { errorMessage: res.t('notAnEmail') }, }, password: { notEmpty: true, errorMessage: res.t('missingPassword'), - equals: {options: [req.body.confirmPassword], errorMessage: res.t('passwordConfirmationMatch')}, + equals: { options: [req.body.confirmPassword], errorMessage: res.t('passwordConfirmationMatch') }, }, }); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; const issues = verifyUsername(req.body.username, res); @@ -106,13 +106,15 @@ async function registerLocal (req, res, { isV3 = false }) { // So we can search for it in the database and then reject the choosen username if 1 or more results are found email = email.toLowerCase(); username = username.trim(); - let lowerCaseUsername = username.toLowerCase(); + const lowerCaseUsername = username.toLowerCase(); // Search for duplicates using lowercase version of username - let user = await User.findOne({$or: [ - {'auth.local.email': email}, - {'auth.local.lowerCaseUsername': lowerCaseUsername}, - ]}, {'auth.local': 1}).exec(); + const user = await User.findOne({ + $or: [ + { 'auth.local.email': email }, + { 'auth.local.lowerCaseUsername': lowerCaseUsername }, + ], + }, { 'auth.local': 1 }).exec(); if (user) { if (email === user.auth.local.email) throw new NotAuthorized(res.t('emailTaken')); @@ -124,7 +126,7 @@ async function registerLocal (req, res, { isV3 = false }) { } } - let hashed_password = await passwordUtils.bcryptHash(password); // eslint-disable-line camelcase + const hashed_password = await passwordUtils.bcryptHash(password); // eslint-disable-line camelcase let newUser = { auth: { local: { @@ -144,7 +146,7 @@ async function registerLocal (req, res, { isV3 = false }) { }; if (existingUser) { - let hasSocialAuth = common.constants.SUPPORTED_SOCIAL_NETWORKS.find(network => { + const hasSocialAuth = common.constants.SUPPORTED_SOCIAL_NETWORKS.find(network => { if (existingUser.auth.hasOwnProperty(network.key)) { return existingUser.auth[network.key].id; } @@ -162,7 +164,7 @@ async function registerLocal (req, res, { isV3 = false }) { await _handleGroupInvitation(newUser, req.query.groupInvite || req.query.partyInvite); } - let savedUser = await newUser.save(); + const savedUser = await newUser.save(); let userToJSON; if (isV3) { @@ -174,14 +176,14 @@ async function registerLocal (req, res, { isV3 = false }) { if (existingUser) { res.respond(200, userToJSON.auth.local); // We convert to toJSON to hide private fields } else { - let userJSON = userToJSON; + const userJSON = userToJSON; userJSON.newUser = true; res.respond(201, userJSON); } // Clean previous email preferences and send welcome email EmailUnsubscription - .remove({email: savedUser.auth.local.email}) + .remove({ email: savedUser.auth.local.email }) .then(() => { if (existingUser) return; if (newUser.registeredThrough === 'habitica-web') { diff --git a/website/server/libs/auth/social.js b/website/server/libs/auth/social.js index 24cc8582e7..b29030b48f 100644 --- a/website/server/libs/auth/social.js +++ b/website/server/libs/auth/social.js @@ -24,18 +24,16 @@ function _passportProfile (network, accessToken) { export async function loginSocial (req, res) { const existingUser = res.locals.user; const accessToken = req.body.authResponse.access_token; - const network = req.body.network; + const { network } = req.body; - const isSupportedNetwork = common.constants.SUPPORTED_SOCIAL_NETWORKS.find(supportedNetwork => { - return supportedNetwork.key === network; - }); + const isSupportedNetwork = common.constants.SUPPORTED_SOCIAL_NETWORKS.find(supportedNetwork => supportedNetwork.key === network); if (!isSupportedNetwork) throw new BadRequest(res.t('unsupportedNetwork')); const profile = await _passportProfile(network, accessToken); let user = await User.findOne({ [`auth.${network}.id`]: profile.id, - }, {_id: 1, apiToken: 1, auth: 1}).exec(); + }, { _id: 1, apiToken: 1, auth: 1 }).exec(); // User already signed up if (user) { @@ -85,7 +83,7 @@ export async function loginSocial (req, res) { // Clean previous email preferences if (savedUser.auth[network].emails && savedUser.auth[network].emails[0] && savedUser.auth[network].emails[0].value) { EmailUnsubscription - .remove({email: savedUser.auth[network].emails[0].value.toLowerCase()}) + .remove({ email: savedUser.auth[network].emails[0].value.toLowerCase() }) .exec() .then(() => { if (!existingUser) { diff --git a/website/server/libs/auth/utils.js b/website/server/libs/auth/utils.js index 14bd8062c9..beadb56bbe 100644 --- a/website/server/libs/auth/utils.js +++ b/website/server/libs/auth/utils.js @@ -7,12 +7,12 @@ const COMMUNITY_MANAGER_EMAIL = nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL'); const translator = shortid('0123456789abcdefghijklmnopqrstuvwxyz'); export function generateUsername () { - let newName = `hb-${translator.new()}`; + const newName = `hb-${translator.new()}`; return newName.substring(0, 20); } export function loginRes (user, req, res) { - if (user.auth.blocked) throw new NotAuthorized(res.t('accountSuspended', {communityManagerEmail: COMMUNITY_MANAGER_EMAIL, userId: user._id})); + if (user.auth.blocked) throw new NotAuthorized(res.t('accountSuspended', { communityManagerEmail: COMMUNITY_MANAGER_EMAIL, userId: user._id })); const responseData = { id: user._id, diff --git a/website/server/libs/bannedWords.js b/website/server/libs/bannedWords.js index ba0a7d8b4b..ec5701aa53 100644 --- a/website/server/libs/bannedWords.js +++ b/website/server/libs/bannedWords.js @@ -67,7 +67,7 @@ // DO NOT EDIT! See the comments at the top of this file. -let bannedWords = [ +const bannedWords = [ 'TESTPLACEHOLDERSWEARWORDHERE', 'TESTPLACEHOLDERSWEARWORDHERE1', diff --git a/website/server/libs/baseModel.js b/website/server/libs/baseModel.js index fe52e43130..e2a0e6700b 100644 --- a/website/server/libs/baseModel.js +++ b/website/server/libs/baseModel.js @@ -41,13 +41,13 @@ export default function baseModel (schema, options = {}) { }); } - let noSetFields = ['createdAt', 'updatedAt']; - let privateFields = ['__v']; + const noSetFields = ['createdAt', 'updatedAt']; + const privateFields = ['__v']; if (Array.isArray(options.noSet)) noSetFields.push(...options.noSet); // This method accepts an additional array of fields to be sanitized that can be passed at runtime schema.statics.sanitize = function sanitize (objToSanitize = {}, additionalFields = []) { - noSetFields.concat(additionalFields).forEach((fieldPath) => { + noSetFields.concat(additionalFields).forEach(fieldPath => { _.unset(objToSanitize, fieldPath); }); @@ -59,7 +59,7 @@ export default function baseModel (schema, options = {}) { if (!schema.options.toJSON) schema.options.toJSON = {}; schema.options.toJSON.transform = function transformToObject (doc, plainObj) { - privateFields.forEach((fieldPath) => { + privateFields.forEach(fieldPath => { _.unset(plainObj, fieldPath); }); diff --git a/website/server/libs/buildManifest.js b/website/server/libs/buildManifest.js index 8c222de185..158eeb4839 100644 --- a/website/server/libs/buildManifest.js +++ b/website/server/libs/buildManifest.js @@ -4,21 +4,21 @@ import nconf from 'nconf'; const MANIFEST_FILE_PATH = path.join(__dirname, '/../../client-old/manifest.json'); const BUILD_FOLDER_PATH = path.join(__dirname, '/../../build'); -let manifestFiles = require(MANIFEST_FILE_PATH); +const manifestFiles = require(MANIFEST_FILE_PATH); const IS_PROD = nconf.get('IS_PROD'); -let buildFiles = []; +const buildFiles = []; function _walk (folder) { - let files = fs.readdirSync(folder); + const files = fs.readdirSync(folder); - files.forEach((fileName) => { - let file = `${folder}/${fileName}`; + files.forEach(fileName => { + const file = `${folder}/${fileName}`; if (fs.statSync(file).isDirectory()) { _walk(file); } else { - let relFolder = path.relative(BUILD_FOLDER_PATH, folder); + const relFolder = path.relative(BUILD_FOLDER_PATH, folder); let original = fileName.replace(/-.{8}(\.[\d\w]+)$/, '$1'); // Match the hash part of the filename if (relFolder) { @@ -40,7 +40,7 @@ export function getBuildUrl (url) { } export function getManifestFiles (page, type) { - let files = manifestFiles[page]; + const files = manifestFiles[page]; if (!files) throw new Error(`Page "${page}" not found!`); @@ -56,13 +56,13 @@ export function getManifestFiles (page, type) { } } else { if (type !== 'js') { - files.css.forEach((file) => { + files.css.forEach(file => { htmlCode += ``; }); } if (type !== 'css') { - files.js.forEach((file) => { + files.js.forEach(file => { htmlCode += ``; }); } diff --git a/website/server/libs/challenges/index.js b/website/server/libs/challenges/index.js index 764cdf907b..dd3de27dce 100644 --- a/website/server/libs/challenges/index.js +++ b/website/server/libs/challenges/index.js @@ -29,10 +29,12 @@ export function getChallengeGroupResponse (group) { } export async function createChallenge (user, req, res) { - let groupId = req.body.group; - let prize = req.body.prize; + const groupId = req.body.group; + const { prize } = req.body; - let group = await Group.getGroup({user, groupId, fields: '-chat', mustBeMember: true}); + const group = await Group.getGroup({ + user, groupId, fields: '-chat', mustBeMember: true, + }); if (!group) throw new NotFound(res.t('groupNotFound')); if (!group.isMember(user)) throw new NotAuthorized(res.t('mustBeGroupMember')); @@ -45,8 +47,8 @@ export async function createChallenge (user, req, res) { } if (prize > 0) { - let groupBalance = group.balance && group.leader === user._id ? group.balance : 0; - let prizeCost = prize / 4; + const groupBalance = group.balance && group.leader === user._id ? group.balance : 0; + const prizeCost = prize / 4; if (prizeCost > user.balance + groupBalance) { throw new NotAuthorized(res.t('cantAfford')); @@ -57,7 +59,7 @@ export async function createChallenge (user, req, res) { group.balance -= prizeCost; } else if (groupBalance > 0) { // User pays remainder of prize cost after group - let remainder = prizeCost - group.balance; + const remainder = prizeCost - group.balance; group.balance = 0; user.balance -= remainder; } else { @@ -72,27 +74,27 @@ export async function createChallenge (user, req, res) { req.body.summary = req.body.name; } req.body.leader = user._id; - req.body.official = user.contributor.admin && req.body.official ? true : false; - let challenge = new Challenge(Challenge.sanitize(req.body)); + req.body.official = !!(user.contributor.admin && req.body.official); + const challenge = new Challenge(Challenge.sanitize(req.body)); // First validate challenge so we don't save group if it's invalid (only runs sync validators) - let challengeValidationErrors = challenge.validateSync(); + const challengeValidationErrors = challenge.validateSync(); if (challengeValidationErrors) throw challengeValidationErrors; - let results = await Promise.all([challenge.save({ + const results = await Promise.all([challenge.save({ validateBeforeSave: false, // already validated }), group.save(), user.save()]); - let savedChal = results[0]; + const savedChal = results[0]; - return {savedChal, group}; + return { savedChal, group }; } export function cleanUpTask (task) { - let cleansedTask = omit(task, TASK_KEYS_TO_REMOVE); + const cleansedTask = omit(task, TASK_KEYS_TO_REMOVE); // Copy checklists but reset to uncomplete and assign new id if (!cleansedTask.checklist) cleansedTask.checklist = []; - cleansedTask.checklist.forEach((item) => { + cleansedTask.checklist.forEach(item => { item.completed = false; item.id = uuid(); }); diff --git a/website/server/libs/chat.js b/website/server/libs/chat.js index a19e9b260c..1414386dfd 100644 --- a/website/server/libs/chat.js +++ b/website/server/libs/chat.js @@ -1,27 +1,26 @@ import { model as User } from '../models/user'; import { getUserInfo } from './email'; -import {sendNotification as sendPushNotification} from './pushNotifications'; +import { sendNotification as sendPushNotification } from './pushNotifications'; export async function getAuthorEmailFromMessage (message) { - let authorId = message.uuid; + const authorId = message.uuid; if (authorId === 'system') { return 'system'; } - let author = await User.findOne({_id: authorId}, {auth: 1}).exec(); + const author = await User.findOne({ _id: authorId }, { auth: 1 }).exec(); if (author) { return getUserInfo(author, ['email']).email; - } else { - return 'Author Account Deleted'; } + return 'Author Account Deleted'; } export async function sendChatPushNotifications (user, group, message, translate) { - let members = await User.find({ + const members = await User.find({ 'party._id': group._id, - _id: {$ne: user._id}, + _id: { $ne: user._id }, }) .select('preferences.pushNotifications preferences.language profile.name pushDevices') .exec(); @@ -30,13 +29,15 @@ export async function sendChatPushNotifications (user, group, message, translate sendPushNotification( member, { - title: translate('groupActivityNotificationTitle', {user: message.user, group: group.name}, member.preferences.language), + title: translate('groupActivityNotificationTitle', { user: message.user, group: group.name }, member.preferences.language), message: message.text, identifier: 'groupActivity', category: 'groupActivity', - payload: {groupID: group._id, type: group.type, groupName: group.name, message: message.text, timestamp: message.timestamp, senderName: message.user}, - } + payload: { + groupID: group._id, type: group.type, groupName: group.name, message: message.text, timestamp: message.timestamp, senderName: message.user, + }, + }, ); } }); -} \ No newline at end of file +} diff --git a/website/server/libs/chat/group-chat.js b/website/server/libs/chat/group-chat.js index d79b9b74c8..612201b574 100644 --- a/website/server/libs/chat/group-chat.js +++ b/website/server/libs/chat/group-chat.js @@ -1,6 +1,6 @@ +import _ from 'lodash'; import { chatModel as Chat } from '../../models/message'; import shared from '../../../common'; -import _ from 'lodash'; import { MAX_CHAT_COUNT, MAX_SUBBED_GROUP_CHAT_COUNT } from '../../models/group'; const questScrolls = shared.content.quests; @@ -9,7 +9,7 @@ const questScrolls = shared.content.quests; export async function getGroupChat (group) { const maxChatCount = group.isSubscribed() ? MAX_SUBBED_GROUP_CHAT_COUNT : MAX_CHAT_COUNT; - const groupChat = await Chat.find({groupId: group._id}) + const groupChat = await Chat.find({ groupId: group._id }) .limit(maxChatCount) .sort('-timestamp') .exec(); @@ -19,9 +19,7 @@ export async function getGroupChat (group) { const concatedGroupChat = groupChat.concat(currentGroupChat); group.chat = concatedGroupChat.reduce((previous, current) => { - const foundMessage = previous.find(message => { - return message.id === current.id; - }); + const foundMessage = previous.find(message => message.id === current.id); if (!foundMessage) previous.push(current); return previous; }, []); @@ -30,20 +28,22 @@ export async function getGroupChat (group) { export function translateMessage (lang, info) { let msg; let foundText = ''; - let spells = shared.content.spells; - let quests = shared.content.quests; + const { spells } = shared.content; + const { quests } = shared.content; switch (info.type) { case 'quest_start': - msg = shared.i18n.t('chatQuestStarted', {questName: questScrolls[info.quest].text(lang)}, lang); + msg = shared.i18n.t('chatQuestStarted', { questName: questScrolls[info.quest].text(lang) }, lang); break; case 'boss_damage': - msg = shared.i18n.t('chatBossDamage', {username: info.user, bossName: questScrolls[info.quest].boss.name(lang), userDamage: info.userDamage, bossDamage: info.bossDamage}, lang); + msg = shared.i18n.t('chatBossDamage', { + username: info.user, bossName: questScrolls[info.quest].boss.name(lang), userDamage: info.userDamage, bossDamage: info.bossDamage, + }, lang); break; case 'boss_dont_attack': - msg = shared.i18n.t('chatBossDontAttack', {username: info.user, bossName: questScrolls[info.quest].boss.name(lang), userDamage: info.userDamage}, lang); + msg = shared.i18n.t('chatBossDontAttack', { username: info.user, bossName: questScrolls[info.quest].boss.name(lang), userDamage: info.userDamage }, lang); break; case 'boss_rage': @@ -51,7 +51,7 @@ export function translateMessage (lang, info) { break; case 'boss_defeated': - msg = shared.i18n.t('chatBossDefeated', {bossName: questScrolls[info.quest].boss.name(lang)}, lang); + msg = shared.i18n.t('chatBossDefeated', { bossName: questScrolls[info.quest].boss.name(lang) }, lang); break; case 'user_found_items': @@ -59,7 +59,7 @@ export function translateMessage (lang, info) { m.push(`${v} ${questScrolls[info.quest].collect[k].text(lang)}`); return m; }, []).join(', '); - msg = shared.i18n.t('chatFindItems', {username: info.user, items: foundText}, lang); + msg = shared.i18n.t('chatFindItems', { username: info.user, items: foundText }, lang); break; case 'all_items_found': @@ -67,19 +67,19 @@ export function translateMessage (lang, info) { break; case 'spell_cast_party': - msg = shared.i18n.t('chatCastSpellParty', {username: info.user, spell: spells[info.class][info.spell].text(lang)}, lang); + msg = shared.i18n.t('chatCastSpellParty', { username: info.user, spell: spells[info.class][info.spell].text(lang) }, lang); break; case 'spell_cast_user': - msg = shared.i18n.t('chatCastSpellUser', {username: info.user, spell: spells[info.class][info.spell].text(lang), target: info.target}, lang); + msg = shared.i18n.t('chatCastSpellUser', { username: info.user, spell: spells[info.class][info.spell].text(lang), target: info.target }, lang); break; case 'quest_cancel': - msg = shared.i18n.t('chatQuestCancelled', {username: info.user, questName: questScrolls[info.quest].text(lang)}, lang); + msg = shared.i18n.t('chatQuestCancelled', { username: info.user, questName: questScrolls[info.quest].text(lang) }, lang); break; case 'quest_abort': - msg = shared.i18n.t('chatQuestAborted', {username: info.user, questName: questScrolls[info.quest].text(lang)}, lang); + msg = shared.i18n.t('chatQuestAborted', { username: info.user, questName: questScrolls[info.quest].text(lang) }, lang); break; case 'tavern_quest_completed': @@ -87,7 +87,7 @@ export function translateMessage (lang, info) { break; case 'tavern_boss_rage_tired': - msg = shared.i18n.t('tavernBossTired', {rageName: quests[info.quest].boss.rage.title(lang), bossName: quests[info.quest].boss.name(lang)}, lang); + msg = shared.i18n.t('tavernBossTired', { rageName: quests[info.quest].boss.rage.title(lang), bossName: quests[info.quest].boss.name(lang) }, lang); break; case 'tavern_boss_rage': @@ -99,7 +99,7 @@ export function translateMessage (lang, info) { break; case 'claim_task': - msg = shared.i18n.t('userIsClamingTask', {username: info.user, task: info.task}, lang); + msg = shared.i18n.t('userIsClamingTask', { username: info.user, task: info.task }, lang); break; } diff --git a/website/server/libs/chatReporting/chatReporter.js b/website/server/libs/chatReporting/chatReporter.js index d55a95c353..dd528eac83 100644 --- a/website/server/libs/chatReporting/chatReporter.js +++ b/website/server/libs/chatReporting/chatReporter.js @@ -16,31 +16,33 @@ export default class ChatReporter { const groupUrl = getGroupUrl(group); return [ - {name: 'MESSAGE_TIME', content: (new Date(message.timestamp)).toString()}, - {name: 'MESSAGE_TEXT', content: message.text}, + { name: 'MESSAGE_TIME', content: (new Date(message.timestamp)).toString() }, + { name: 'MESSAGE_TEXT', content: message.text }, - {name: 'REPORTER_DISPLAY_NAME', content: this.user.profile.name}, - {name: 'REPORTER_USERNAME', content: this.user.auth.local.username}, - {name: 'REPORTER_UUID', content: this.user._id}, - {name: 'REPORTER_EMAIL', content: reporterEmail}, - {name: 'REPORTER_MODAL_URL', content: `/static/front/#?memberId=${this.user._id}`}, + { name: 'REPORTER_DISPLAY_NAME', content: this.user.profile.name }, + { name: 'REPORTER_USERNAME', content: this.user.auth.local.username }, + { name: 'REPORTER_UUID', content: this.user._id }, + { name: 'REPORTER_EMAIL', content: reporterEmail }, + { name: 'REPORTER_MODAL_URL', content: `/static/front/#?memberId=${this.user._id}` }, ...authorVariables, - {name: 'GROUP_NAME', content: group.name}, - {name: 'GROUP_TYPE', content: group.type}, - {name: 'GROUP_ID', content: group._id}, - {name: 'GROUP_URL', content: groupUrl || 'N/A'}, + { name: 'GROUP_NAME', content: group.name }, + { name: 'GROUP_TYPE', content: group.type }, + { name: 'GROUP_ID', content: group._id }, + { name: 'GROUP_URL', content: groupUrl || 'N/A' }, ]; } - createGenericAuthorVariables (prefix, {user, username, uuid, email}) { + createGenericAuthorVariables (prefix, { + user, username, uuid, email, + }) { return [ - {name: `${prefix}_DISPLAY_NAME`, content: user}, - {name: `${prefix}_USERNAME`, content: username}, - {name: `${prefix}_UUID`, content: uuid}, - {name: `${prefix}_EMAIL`, content: email}, - {name: `${prefix}_MODAL_URL`, content: `/static/front/#?memberId=${uuid}`}, + { name: `${prefix}_DISPLAY_NAME`, content: user }, + { name: `${prefix}_USERNAME`, content: username }, + { name: `${prefix}_UUID`, content: uuid }, + { name: `${prefix}_EMAIL`, content: email }, + { name: `${prefix}_MODAL_URL`, content: `/static/front/#?memberId=${uuid}` }, ]; } diff --git a/website/server/libs/chatReporting/chatReporterFactory.js b/website/server/libs/chatReporting/chatReporterFactory.js index 50ac7e9e89..d6f6441d33 100644 --- a/website/server/libs/chatReporting/chatReporterFactory.js +++ b/website/server/libs/chatReporting/chatReporterFactory.js @@ -4,7 +4,7 @@ import InboxChatReporter from './inboxChatReporter'; export function chatReporterFactory (type, req, res) { if (type === 'Group') { return new GroupChatReporter(req, res); - } else if (type === 'Inbox') { + } if (type === 'Inbox') { return new InboxChatReporter(req, res); } } diff --git a/website/server/libs/chatReporting/groupChatReporter.js b/website/server/libs/chatReporting/groupChatReporter.js index ab7e23f7fc..ac6c199f7d 100644 --- a/website/server/libs/chatReporting/groupChatReporter.js +++ b/website/server/libs/chatReporting/groupChatReporter.js @@ -13,9 +13,7 @@ import { chatModel as Chat } from '../../models/message'; import apiError from '../apiError'; const COMMUNITY_MANAGER_EMAIL = nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL'); -const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map((email) => { - return { email, canSend: true }; -}); +const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map(email => ({ email, canSend: true })); const USER_AGE_FOR_FLAGGING = 3; // accounts less than this many days old don't increment flagCount export default class GroupChatReporter extends ChatReporter { @@ -30,29 +28,29 @@ export default class GroupChatReporter extends ChatReporter { this.req.checkParams('groupId', apiError('groupIdRequired')).notEmpty(); this.req.checkParams('chatId', apiError('chatIdRequired')).notEmpty(); - let validationErrors = this.req.validationErrors(); + const validationErrors = this.req.validationErrors(); if (validationErrors) throw validationErrors; - let group = await Group.getGroup({ + const group = await Group.getGroup({ user: this.user, groupId: this.groupId, optionalMembership: this.user.contributor.admin, }); if (!group) throw new NotFound(this.res.t('groupNotFound')); - const message = await Chat.findOne({_id: this.req.params.chatId}).exec(); + const message = await Chat.findOne({ _id: this.req.params.chatId }).exec(); if (!message) throw new NotFound(this.res.t('messageGroupChatNotFound')); - if (message.uuid === 'system') throw new BadRequest(this.res.t('messageCannotFlagSystemMessages', {communityManagerEmail: COMMUNITY_MANAGER_EMAIL})); + if (message.uuid === 'system') throw new BadRequest(this.res.t('messageCannotFlagSystemMessages', { communityManagerEmail: COMMUNITY_MANAGER_EMAIL })); const userComment = this.req.body.comment; - return {message, group, userComment}; + return { message, group, userComment }; } async notify (group, message, userComment, automatedComment = '') { let emailVariables = await this.getMessageVariables(group, message); emailVariables = emailVariables.concat([ - {name: 'REPORTER_COMMENT', content: userComment || ''}, + { name: 'REPORTER_COMMENT', content: userComment || '' }, ]); sendTxn(FLAG_REPORT_EMAILS, 'flag-report-to-mods-with-comments', emailVariables); @@ -88,7 +86,7 @@ export default class GroupChatReporter extends ChatReporter { } async flag () { - let {message, group, userComment} = await this.validate(); + const { message, group, userComment } = await this.validate(); let increaseFlagCount = true; let automatedComment = ''; diff --git a/website/server/libs/chatReporting/inboxChatReporter.js b/website/server/libs/chatReporting/inboxChatReporter.js index 059d200db2..73a3a1f5de 100644 --- a/website/server/libs/chatReporting/inboxChatReporter.js +++ b/website/server/libs/chatReporting/inboxChatReporter.js @@ -5,16 +5,14 @@ import ChatReporter from './chatReporter'; import { BadRequest, } from '../errors'; -import { getUserInfo, sendTxn} from '../email'; +import { getUserInfo, sendTxn } from '../email'; import * as slack from '../slack'; import apiError from '../apiError'; import * as inboxLib from '../inbox'; -import {getAuthorEmailFromMessage} from '../chat'; +import { getAuthorEmailFromMessage } from '../chat'; -const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map((email) => { - return { email, canSend: true }; -}); +const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map(email => ({ email, canSend: true })); export default class InboxChatReporter extends ChatReporter { constructor (req, res) { @@ -27,11 +25,11 @@ export default class InboxChatReporter extends ChatReporter { async validate () { this.req.checkParams('messageId', apiError('messageIdRequired')).notEmpty(); - let validationErrors = this.req.validationErrors(); + const validationErrors = this.req.validationErrors(); if (validationErrors) throw validationErrors; if (this.user.contributor.admin && this.req.query.userId) { - this.inboxUser = await User.findOne({_id: this.req.query.userId}); + this.inboxUser = await User.findOne({ _id: this.req.query.userId }); } const message = await inboxLib.getUserInboxMessage(this.inboxUser, this.req.params.messageId); @@ -39,7 +37,7 @@ export default class InboxChatReporter extends ChatReporter { const userComment = this.req.body.comment; - return {message, userComment}; + return { message, userComment }; } async notify (message, userComment) { @@ -51,7 +49,7 @@ export default class InboxChatReporter extends ChatReporter { let emailVariables = await this.getMessageVariables(group, message); emailVariables = emailVariables.concat([ - {name: 'REPORTER_COMMENT', content: userComment || ''}, + { name: 'REPORTER_COMMENT', content: userComment || '' }, ]); sendTxn(FLAG_REPORT_EMAILS, 'flag-report-to-mods-with-comments', emailVariables); @@ -120,7 +118,7 @@ export default class InboxChatReporter extends ChatReporter { } async flag () { - let {message, userComment} = await this.validate(); + const { message, userComment } = await this.validate(); await this.flagInboxMessage(message); await this.notify(message, userComment); await this.markMessageAsReported(message); diff --git a/website/server/libs/client.js b/website/server/libs/client.js index 12aa476c95..a9d1b25f8c 100644 --- a/website/server/libs/client.js +++ b/website/server/libs/client.js @@ -1,5 +1,5 @@ const ROOT = `${__dirname}/../../../`; export function serveClient (expressRes) { - return expressRes.sendFile('./dist-client/index.html', {root: ROOT}); -} \ No newline at end of file + return expressRes.sendFile('./dist-client/index.html', { root: ROOT }); +} diff --git a/website/server/libs/collectionManipulators.js b/website/server/libs/collectionManipulators.js index 5f6c4477d2..bcd1c527f1 100644 --- a/website/server/libs/collectionManipulators.js +++ b/website/server/libs/collectionManipulators.js @@ -11,7 +11,7 @@ export function removeFromArray (array, element) { } if (elementIndex !== -1) { - let removedElement = array[elementIndex]; + const removedElement = array[elementIndex]; array.splice(elementIndex, 1); return removedElement; } diff --git a/website/server/libs/coupons/index.js b/website/server/libs/coupons/index.js index e32c469962..5509ee9634 100644 --- a/website/server/libs/coupons/index.js +++ b/website/server/libs/coupons/index.js @@ -3,8 +3,8 @@ import { model as Coupon } from '../../models/coupon'; export async function enterCode (req, res, user) { req.checkParams('code', res.t('couponCodeRequired')).notEmpty(); - let validationErrors = req.validationErrors(); + const validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; await Coupon.apply(user, req, req.params.code); -} \ No newline at end of file +} diff --git a/website/server/libs/cron.js b/website/server/libs/cron.js index 2e01b1a917..b0380b62ab 100644 --- a/website/server/libs/cron.js +++ b/website/server/libs/cron.js @@ -1,37 +1,37 @@ import moment from 'moment'; -import { model as User } from '../models/user'; -import common from '../../common/'; -import { preenUserHistory } from '../libs/preening'; -import sleep from '../libs/sleep'; import _ from 'lodash'; import cloneDeep from 'lodash/cloneDeep'; import nconf from 'nconf'; +import { model as User } from '../models/user'; +import common from '../../common'; +import { preenUserHistory } from './preening'; +import sleep from './sleep'; const CRON_SAFE_MODE = nconf.get('CRON_SAFE_MODE') === 'true'; const CRON_SEMI_SAFE_MODE = nconf.get('CRON_SEMI_SAFE_MODE') === 'true'; -const MAX_INCENTIVES = common.constants.MAX_INCENTIVES; -const shouldDo = common.shouldDo; -const scoreTask = common.ops.scoreTask; -const i18n = common.i18n; -const loginIncentives = common.content.loginIncentives; +const { MAX_INCENTIVES } = common.constants; +const { shouldDo } = common; +const { scoreTask } = common.ops; +const { i18n } = common; +const { loginIncentives } = common.content; // const maxPMs = 200; function setIsDueNextDue (task, user, now) { - let optionsForShouldDo = cloneDeep(user.preferences.toObject()); + const optionsForShouldDo = cloneDeep(user.preferences.toObject()); task.isDue = common.shouldDo(now, task, optionsForShouldDo); optionsForShouldDo.nextDue = true; - let nextDue = common.shouldDo(now, task, optionsForShouldDo); + const nextDue = common.shouldDo(now, task, optionsForShouldDo); if (nextDue && nextDue.length > 0) { task.nextDue = nextDue; } } export async function recoverCron (status, locals) { - let {user} = locals; + const { user } = locals; await sleep(0.3); - let reloadedUser = await User.findOne({_id: user._id}).exec(); + const reloadedUser = await User.findOne({ _id: user._id }).exec(); if (!reloadedUser) { throw new Error(`User ${user._id} not found while recovering.`); @@ -49,7 +49,7 @@ export async function recoverCron (status, locals) { } } -let CLEAR_BUFFS = { +const CLEAR_BUFFS = { str: 0, int: 0, per: 0, @@ -60,16 +60,18 @@ let CLEAR_BUFFS = { function grantEndOfTheMonthPerks (user, now) { const SUBSCRIPTION_BASIC_BLOCK_LENGTH = 3; // multi-month subscriptions are for multiples of 3 months - let plan = user.purchased.plan; - let subscriptionEndDate = moment(plan.dateTerminated).isBefore() ? moment(plan.dateTerminated).startOf('month') : moment(now).startOf('month'); - let dateUpdatedMoment = moment(plan.dateUpdated).startOf('month'); - let elapsedMonths = moment(subscriptionEndDate).diff(dateUpdatedMoment, 'months'); + const { plan } = user.purchased; + const subscriptionEndDate = moment(plan.dateTerminated).isBefore() ? moment(plan.dateTerminated).startOf('month') : moment(now).startOf('month'); + const dateUpdatedMoment = moment(plan.dateUpdated).startOf('month'); + const elapsedMonths = moment(subscriptionEndDate).diff(dateUpdatedMoment, 'months'); if (elapsedMonths > 0) { plan.dateUpdated = now; // For every month, inc their "consecutive months" counter. Give perks based on consecutive blocks // If they already got perks for those blocks (eg, 6mo subscription, subscription gifts, etc) - then dec the offset until it hits 0 - _.defaults(plan.consecutive, {count: 0, offset: 0, trinkets: 0, gemCapExtra: 0}); + _.defaults(plan.consecutive, { + count: 0, offset: 0, trinkets: 0, gemCapExtra: 0, + }); let planMonthsLength = 1; // 1 for one-month recurring or gift subscriptions; later set to 3 for 3-month recurring, etc. @@ -90,8 +92,8 @@ function grantEndOfTheMonthPerks (user, now) { if (plan.consecutive.offset < 0) { if (plan.planId) { // NB gift subscriptions don't have a planID (which doesn't matter because we don't need to reapply perks for them and by this point they should have expired anyway) - let planIdRegExp = new RegExp('_([0-9]+)mo'); // e.g., matches 'google_6mo' / 'basic_12mo' and captures '6' / '12' - let match = plan.planId.match(planIdRegExp); + const planIdRegExp = new RegExp('_([0-9]+)mo'); // e.g., matches 'google_6mo' / 'basic_12mo' and captures '6' / '12' + const match = plan.planId.match(planIdRegExp); if (match !== null && match[0] !== null) { planMonthsLength = match[1]; // 3 for 3-month recurring subscription, etc } @@ -120,7 +122,7 @@ function grantEndOfTheMonthPerks (user, now) { } function removeTerminatedSubscription (user) { - let plan = user.purchased.plan; + const { plan } = user.purchased; _.merge(plan, { planId: null, @@ -145,7 +147,7 @@ function resetHabitCounters (user, tasksByType, now, daysMissed) { if (resetWeekly === true && resetMonthly === true) { break; } - let thatDay = moment(now).zone(user.preferences.timezoneOffset + user.preferences.dayStart * 60).subtract({days: i}); + const thatDay = moment(now).zone(user.preferences.timezoneOffset + user.preferences.dayStart * 60).subtract({ days: i }); if (thatDay.day() === 1) { resetWeekly = true; } @@ -154,7 +156,7 @@ function resetHabitCounters (user, tasksByType, now, daysMissed) { } } - tasksByType.habits.forEach((task) => { + tasksByType.habits.forEach(task => { // reset counters if appropriate let reset = false; @@ -206,10 +208,10 @@ function awardLoginIncentives (user) { if (notif && notif.type === 'LOGIN_INCENTIVE') user.notifications.splice(index, 1); }); - let notificationData = {}; + const notificationData = {}; notificationData.message = i18n.t('checkinEarned', user.preferences.language); - let loginIncentive = loginIncentives[user.loginIncentives]; + const loginIncentive = loginIncentives[user.loginIncentives]; if (loginIncentive.rewardKey) { loginIncentive.assignReward(user); @@ -218,11 +220,11 @@ function awardLoginIncentives (user) { // @TODO: Abstract this logic and share it across the server and client let count = 0; - for (let reward of loginIncentive.reward) { + for (const reward of loginIncentive.reward) { if (reward.text) { notificationData.rewardText += reward.text(user.preferences.language); if (reward.key === 'RoyalPurple') { - notificationData.rewardText = i18n.t('potion', {potionType: notificationData.rewardText}, user.preferences.language); + notificationData.rewardText = i18n.t('potion', { potionType: notificationData.rewardText }, user.preferences.language); } } else if (loginIncentive.rewardKey[0] === 'background_blue') { notificationData.rewardText = i18n.t('incentiveBackgrounds', user.preferences.language); @@ -248,11 +250,13 @@ function awardLoginIncentives (user) { // Perform various beginning-of-day reset actions. export function cron (options = {}) { - let {user, tasksByType, analytics, now = new Date(), daysMissed, timezoneOffsetFromUserPrefs} = options; - let _progress = {down: 0, up: 0, collectedItems: 0}; + const { + user, tasksByType, analytics, now = new Date(), daysMissed, timezoneOffsetFromUserPrefs, + } = options; + let _progress = { down: 0, up: 0, collectedItems: 0 }; // Record pre-cron values of HP and MP to show notifications later - let beforeCronStats = _.pick(user.stats, ['hp', 'mp']); + const beforeCronStats = _.pick(user.stats, ['hp', 'mp']); user.preferences.timezoneOffsetAtLastCron = timezoneOffsetFromUserPrefs; // User is only allowed a certain number of drops a day. This resets the count. @@ -262,7 +266,7 @@ export function cron (options = {}) { let perfect = true; // Reset Gold-to-Gems cap if it's the start of the month - let dateUpdatedFalse = !moment(user.purchased.plan.dateUpdated).startOf('month').isSame(moment().startOf('month')) || !user.purchased.plan.dateUpdated; + const dateUpdatedFalse = !moment(user.purchased.plan.dateUpdated).startOf('month').isSame(moment().startOf('month')) || !user.purchased.plan.dateUpdated; if (user.purchased && user.purchased.plan && dateUpdatedFalse) { user.purchased.plan.gemsBought = 0; @@ -273,15 +277,15 @@ export function cron (options = {}) { grantEndOfTheMonthPerks(user, now); } - let plan = user.purchased.plan; - let userHasTerminatedSubscription = plan.dateTerminated && moment(plan.dateTerminated).isBefore(new Date()); + const { plan } = user.purchased; + const userHasTerminatedSubscription = plan.dateTerminated && moment(plan.dateTerminated).isBefore(new Date()); if (!CRON_SAFE_MODE && userHasTerminatedSubscription) removeTerminatedSubscription(user); // Login Incentives user.loginIncentives++; awardLoginIncentives(user); - let multiDaysCountAsOneDay = true; + const multiDaysCountAsOneDay = true; // If the user does not log in for two or more days, cron (mostly) acts as if it were only one day. // When site-wide difficulty settings are introduced, this can be a user preference option. @@ -308,9 +312,9 @@ export function cron (options = {}) { let atLeastOneDailyDue = false; // were any dailies due? if (!user.party.quest.progress.down) user.party.quest.progress.down = 0; - tasksByType.dailys.forEach((task) => { + tasksByType.dailys.forEach(task => { if (task.group.assignedDate && moment(task.group.assignedDate).isAfter(user.auth.timestamps.updated)) return; - let completed = task.completed; + const { completed } = task; // Deduct points for missed Daily tasks let EvadeTask = 0; let scheduleMisses = daysMissed; @@ -318,7 +322,7 @@ export function cron (options = {}) { if (completed) { dailyChecked += 1; if (!atLeastOneDailyDue) { // only bother checking until the first thing is found - let thatDay = moment(now).subtract({days: daysMissed}); + const thatDay = moment(now).subtract({ days: daysMissed }); atLeastOneDailyDue = shouldDo(thatDay.toDate(), task, user.preferences); } } else { @@ -326,7 +330,7 @@ export function cron (options = {}) { scheduleMisses = 0; for (let i = 0; i < daysMissed; i++) { - let thatDay = moment(now).subtract({days: i + 1}); + const thatDay = moment(now).subtract({ days: i + 1 }); if (shouldDo(thatDay.toDate(), task, user.preferences)) { atLeastOneDailyDue = true; @@ -347,7 +351,7 @@ export function cron (options = {}) { perfect = false; if (task.checklist && task.checklist.length > 0) { // Partially completed checklists dock fewer mana points - let fractionChecked = _.reduce(task.checklist, (m, i) => m + (i.completed ? 1 : 0), 0) / task.checklist.length; + const fractionChecked = _.reduce(task.checklist, (m, i) => m + (i.completed ? 1 : 0), 0) / task.checklist.length; dailyDueUnchecked += 1 - fractionChecked; dailyChecked += fractionChecked; } else { @@ -355,7 +359,7 @@ export function cron (options = {}) { } if (!user.preferences.sleep) { - let delta = scoreTask({ + const delta = scoreTask({ user, task, direction: 'down', @@ -402,11 +406,11 @@ export function cron (options = {}) { resetHabitCounters(user, tasksByType, now, daysMissed); - tasksByType.habits.forEach((task) => { + tasksByType.habits.forEach(task => { // slowly reset value to 0 for "onlies" (Habits with + or - but not both) // move singleton Habits towards yellow. if (task.up === false || task.down === false) { - task.value = Math.abs(task.value) < 0.1 ? 0 : task.value = task.value / 2; + task.value = Math.abs(task.value) < 0.1 ? 0 : task.value /= 2; } if (task.group && task.group.approval && task.group.approval.approved) { task.group.approval.approved = false; @@ -417,7 +421,7 @@ export function cron (options = {}) { }); // Finished tallying - user.history.todos.push({date: now, value: todoTally}); + user.history.todos.push({ date: now, value: todoTally }); // tally experience let expTally = user.stats.exp; @@ -427,14 +431,10 @@ export function cron (options = {}) { expTally += common.tnl(lvl); } - user.history.exp.push({date: now, value: expTally}); + user.history.exp.push({ date: now, value: expTally }); // Remove any remaining completed todos from the list of active todos - user.tasksOrder.todos = user.tasksOrder.todos.filter(taskOrderId => { - return _.some(tasksByType.todos, taskType => { - return taskType._id === taskOrderId && taskType.completed === false; - }); - }); + user.tasksOrder.todos = user.tasksOrder.todos.filter(taskOrderId => _.some(tasksByType.todos, taskType => taskType._id === taskOrderId && taskType.completed === false)); // TODO also adjust tasksOrder arrays to remove deleted tasks of any kind (including rewards), ensure that all existing tasks are in the arrays, no tasks IDs are duplicated -- https://github.com/HabitRPG/habitica/issues/7645 // preen user history so that it doesn't become a performance problem @@ -443,7 +443,7 @@ export function cron (options = {}) { if (perfect && atLeastOneDailyDue) { user.achievements.perfect++; - let lvlDiv2 = Math.ceil(common.capByLevel(user.stats.lvl) / 2); + const lvlDiv2 = Math.ceil(common.capByLevel(user.stats.lvl) / 2); user.stats.buffs = { str: lvlDiv2, int: lvlDiv2, @@ -466,21 +466,20 @@ export function cron (options = {}) { // After all is said and done, progress up user's effect on quest, return those values & reset the user's if (!user.preferences.sleep) { - let progress = user.party.quest.progress; + const { progress } = user.party.quest; _progress = progress.toObject(); // clone the old progress object - _.merge(progress, {down: 0, up: 0, collectedItems: 0}); + _.merge(progress, { down: 0, up: 0, collectedItems: 0 }); } // Send notification for changes in HP and MP. // First remove a possible previous cron notification because // we don't want to flood the users with many cron notifications at once. - let oldCronNotif = user.notifications.find((notif, index) => { + const oldCronNotif = user.notifications.find((notif, index) => { if (notif && notif.type === 'CRON') { user.notifications.splice(index, 1); return true; - } else { - return false; } + return false; }); user.addNotification('CRON', { diff --git a/website/server/libs/csvStringify.js b/website/server/libs/csvStringify.js index 1b497032c6..78495b6a6b 100644 --- a/website/server/libs/csvStringify.js +++ b/website/server/libs/csvStringify.js @@ -1,10 +1,8 @@ import csvStringify from 'csv-stringify'; -export default (input) => { - return new Promise((resolve, reject) => { - csvStringify(input, (err, output) => { - if (err) return reject(err); - return resolve(output); - }); +export default input => new Promise((resolve, reject) => { + csvStringify(input, (err, output) => { + if (err) return reject(err); + return resolve(output); }); -}; +}); diff --git a/website/server/libs/email.js b/website/server/libs/email.js index 02e236fe60..122d48ac50 100644 --- a/website/server/libs/email.js +++ b/website/server/libs/email.js @@ -1,7 +1,7 @@ import nconf from 'nconf'; +import got from 'got'; import { TAVERN_ID } from '../models/group'; import { encrypt } from './encryption'; -import got from 'got'; import logger from './logger'; import common from '../../common'; @@ -16,7 +16,7 @@ const EMAIL_SERVER = { const BASE_URL = nconf.get('BASE_URL'); export function getUserInfo (user, fields = []) { - let info = {}; + const info = {}; if (fields.indexOf('name') !== -1) { info.name = user.auth && user.auth.local.username; @@ -65,51 +65,46 @@ export function sendTxn (mailingInfoArray, emailType, variables, personalVariabl mailingInfoArray = Array.isArray(mailingInfoArray) ? mailingInfoArray : [mailingInfoArray]; variables = [ - {name: 'BASE_URL', content: BASE_URL}, + { name: 'BASE_URL', content: BASE_URL }, ].concat(variables || []); // It's important to pass at least a user with its `preferences` as we need to check if he unsubscribed - mailingInfoArray = mailingInfoArray.map((mailingInfo) => { - return mailingInfo._id ? getUserInfo(mailingInfo, ['_id', 'email', 'name', 'canSend']) : mailingInfo; - }).filter((mailingInfo) => { + mailingInfoArray = mailingInfoArray.map(mailingInfo => (mailingInfo._id ? getUserInfo(mailingInfo, ['_id', 'email', 'name', 'canSend']) : mailingInfo)).filter(mailingInfo => // Always send reset-password emails // Don't check canSend for non registered users as already checked before - return mailingInfo.email && (!mailingInfo._id || mailingInfo.canSend || emailType === 'reset-password'); - }); + mailingInfo.email && (!mailingInfo._id || mailingInfo.canSend || emailType === 'reset-password')); // Personal variables are personal to each email recipient, if they are missing // we manually create a structure for them with RECIPIENT_NAME and RECIPIENT_UNSUB_URL // otherwise we just add RECIPIENT_NAME and RECIPIENT_UNSUB_URL to the existing personal variables if (!personalVariables || personalVariables.length === 0) { - personalVariables = mailingInfoArray.map((mailingInfo) => { - return { - rcpt: mailingInfo.email, - vars: [ - { - name: 'RECIPIENT_NAME', - content: mailingInfo.name, - }, - { - name: 'RECIPIENT_UNSUB_URL', - content: `/email/unsubscribe?code=${encrypt(JSON.stringify({ - _id: mailingInfo._id, - email: mailingInfo.email, - }))}`, - }, - ], - }; - }); + personalVariables = mailingInfoArray.map(mailingInfo => ({ + rcpt: mailingInfo.email, + vars: [ + { + name: 'RECIPIENT_NAME', + content: mailingInfo.name, + }, + { + name: 'RECIPIENT_UNSUB_URL', + content: `/email/unsubscribe?code=${encrypt(JSON.stringify({ + _id: mailingInfo._id, + email: mailingInfo.email, + }))}`, + }, + ], + })); } else { - let temporaryPersonalVariables = {}; + const temporaryPersonalVariables = {}; - mailingInfoArray.forEach((mailingInfo) => { + mailingInfoArray.forEach(mailingInfo => { temporaryPersonalVariables[mailingInfo.email] = { name: mailingInfo.name, _id: mailingInfo._id, }; }); - personalVariables.forEach((singlePersonalVariables) => { + personalVariables.forEach(singlePersonalVariables => { singlePersonalVariables.vars.push( { name: 'RECIPIENT_NAME', @@ -121,7 +116,7 @@ export function sendTxn (mailingInfoArray, emailType, variables, personalVariabl _id: temporaryPersonalVariables[singlePersonalVariables.rcpt]._id, email: singlePersonalVariables.rcpt, }))}`, - } + }, ); }); } @@ -141,9 +136,9 @@ export function sendTxn (mailingInfoArray, emailType, variables, personalVariabl options: { priority: 'high', attempts: 5, - backoff: {delay: 10 * 60 * 1000, type: 'fixed'}, + backoff: { delay: 10 * 60 * 1000, type: 'fixed' }, }, }, - }).catch((err) => logger.error(err)); + }).catch(err => logger.error(err)); } } diff --git a/website/server/libs/encryption.js b/website/server/libs/encryption.js index 36fff3f521..a9779044c9 100644 --- a/website/server/libs/encryption.js +++ b/website/server/libs/encryption.js @@ -9,7 +9,7 @@ const SESSION_SECRET_KEY = nconf.get('SESSION_SECRET_KEY'); const SESSION_SECRET_IV = nconf.get('SESSION_SECRET_IV'); const key = Buffer.from(SESSION_SECRET_KEY, 'hex'); -const iv = Buffer.from(SESSION_SECRET_IV, 'hex'); +const iv = Buffer.from(SESSION_SECRET_IV, 'hex'); export function encrypt (text) { const cipher = createCipheriv(algorithm, key, iv); diff --git a/website/server/libs/errors.js b/website/server/libs/errors.js index 1632f1a5e1..0bcc43f2a6 100644 --- a/website/server/libs/errors.js +++ b/website/server/libs/errors.js @@ -1,6 +1,6 @@ import common from '../../common'; -export const CustomError = common.errors.CustomError; +export const { CustomError } = common.errors; /** * @apiDefine NotAuthorized @@ -13,7 +13,7 @@ export const CustomError = common.errors.CustomError; * "message": "Not authorized." * } */ -export const NotAuthorized = common.errors.NotAuthorized; +export const { NotAuthorized } = common.errors; /** * @apiDefine BadRequest @@ -26,7 +26,7 @@ export const NotAuthorized = common.errors.NotAuthorized; * "message": "Bad request." * } */ -export const BadRequest = common.errors.BadRequest; +export const { BadRequest } = common.errors; /** * @apiDefine NotFound @@ -39,7 +39,7 @@ export const BadRequest = common.errors.BadRequest; * "message": "Not found." * } */ -export const NotFound = common.errors.NotFound; +export const { NotFound } = common.errors; /** diff --git a/website/server/libs/gcpTraceAgent.js b/website/server/libs/gcpTraceAgent.js index 5fdf0aa16e..95aec5b08b 100644 --- a/website/server/libs/gcpTraceAgent.js +++ b/website/server/libs/gcpTraceAgent.js @@ -9,4 +9,4 @@ if (IS_PROD && STACKDRIVER_TRACING_ENABLED) { tracer = require('@google-cloud/trace-agent').start(); // eslint-disable-line global-require } -export default tracer; \ No newline at end of file +export default tracer; diff --git a/website/server/libs/groupTasks.js b/website/server/libs/groupTasks.js index 30f6e328ec..1ffd08caf0 100644 --- a/website/server/libs/groupTasks.js +++ b/website/server/libs/groupTasks.js @@ -15,8 +15,8 @@ async function _deleteUnfinishedTasks (groupMemberTask) { await Tasks.Task.deleteMany({ 'group.taskId': groupMemberTask.group.taskId, $and: [ - {userId: {$exists: true}}, - {userId: {$ne: groupMemberTask.userId}}, + { userId: { $exists: true } }, + { userId: { $ne: groupMemberTask.userId } }, ], }).exec(); } @@ -41,7 +41,7 @@ async function _evaluateAllAssignedCompletion (masterTask) { } async function handleSharedCompletion (groupMemberTask) { - let masterTask = await Tasks.Task.findOne({ + const masterTask = await Tasks.Task.findOne({ _id: groupMemberTask.group.taskId, }).exec(); diff --git a/website/server/libs/i18n.js b/website/server/libs/i18n.js index 63d850f26a..5e4092d123 100644 --- a/website/server/libs/i18n.js +++ b/website/server/libs/i18n.js @@ -6,12 +6,12 @@ import shared from '../../common'; export const localePath = path.join(__dirname, '../../common/locales/'); // Store translations -export let translations = {}; +export const translations = {}; // Store MomentJS localization files -export let momentLangs = {}; +export const momentLangs = {}; // Handle differencies in language codes between MomentJS and /locales -let momentLangsMapping = { +const momentLangsMapping = { en: 'en-gb', en_GB: 'en-gb', // eslint-disable-line camelcase no: 'nn', @@ -21,7 +21,7 @@ let momentLangsMapping = { pt_BR: 'pt-br', // eslint-disable-line camelcase }; -export let approvedLanguages = [ +export const approvedLanguages = [ 'bg', 'cs', 'da', @@ -53,11 +53,11 @@ export let approvedLanguages = [ ]; function _loadTranslations (locale) { - let files = fs.readdirSync(path.join(localePath, locale)); + const files = fs.readdirSync(path.join(localePath, locale)); translations[locale] = {}; - files.forEach((file) => { + files.forEach(file => { if (path.extname(file) !== '.json') return; // We use require to load and parse a JSON file @@ -69,7 +69,7 @@ function _loadTranslations (locale) { _loadTranslations('en'); // Then load all other languages -approvedLanguages.forEach((file) => { +approvedLanguages.forEach(file => { if (file === 'en' || fs.statSync(path.join(localePath, file)).isDirectory() === false) return; _loadTranslations(file); @@ -80,24 +80,22 @@ approvedLanguages.forEach((file) => { // Add translations to shared shared.i18n.translations = translations; -export let langCodes = Object.keys(translations); +export const langCodes = Object.keys(translations); -export let availableLanguages = langCodes.map((langCode) => { - return { - code: langCode, - name: translations[langCode].languageName, - }; -}); +export const availableLanguages = langCodes.map(langCode => ({ + code: langCode, + name: translations[langCode].languageName, +})); -langCodes.forEach((code) => { - let lang = _.find(availableLanguages, {code}); +langCodes.forEach(code => { + const lang = _.find(availableLanguages, { code }); lang.momentLangCode = momentLangsMapping[code] || code; try { // MomentJS lang files are JS files that has to be executed in the browser so we load them as plain text files // We wrap everything in a try catch because the file might not exist - let f = fs.readFileSync(path.join(__dirname, `/../../../node_modules/moment/locale/${lang.momentLangCode}.js`), 'utf8'); + const f = fs.readFileSync(path.join(__dirname, `/../../../node_modules/moment/locale/${lang.momentLangCode}.js`), 'utf8'); momentLangs[code] = f; } catch (e) { // eslint-disable-lint no-empty @@ -107,10 +105,10 @@ langCodes.forEach((code) => { // Remove en_GB from langCodes checked by browser to avoid it being // used in place of plain original 'en' (it's an optional language that can be enabled only in setting) -export let defaultLangCodes = _.without(langCodes, 'en_GB'); +export const defaultLangCodes = _.without(langCodes, 'en_GB'); // A map of languages that have different versions and the relative versions -export let multipleVersionsLanguages = { +export const multipleVersionsLanguages = { es: { 'es-419': 'es_419', 'es-mx': 'es_419', diff --git a/website/server/libs/inbox/index.js b/website/server/libs/inbox/index.js index 9cb610cc5e..ff0ddfd2bc 100644 --- a/website/server/libs/inbox/index.js +++ b/website/server/libs/inbox/index.js @@ -1,6 +1,6 @@ -import {mapInboxMessage, inboxModel as Inbox} from '../../models/message'; -import {getUserInfo, sendTxn as sendTxnEmail} from '../email'; -import {sendNotification as sendPushNotification} from '../pushNotifications'; +import { mapInboxMessage, inboxModel as Inbox } from '../../models/message'; +import { getUserInfo, sendTxn as sendTxnEmail } from '../email'; +import { sendNotification as sendPushNotification } from '../pushNotifications'; const PM_PER_PAGE = 10; @@ -10,7 +10,7 @@ export async function sentMessage (sender, receiver, message, translate) { if (receiver.preferences.emailNotifications.newPM !== false) { sendTxnEmail(receiver, 'new-pm', [ - {name: 'SENDER', content: senderName}, + { name: 'SENDER', content: senderName }, ]); } @@ -18,19 +18,21 @@ export async function sentMessage (sender, receiver, message, translate) { sendPushNotification( receiver, { - title: translate('newPMNotificationTitle', {name: getUserInfo(sender, ['name']).name}, receiver.preferences.language), + title: translate('newPMNotificationTitle', { name: getUserInfo(sender, ['name']).name }, receiver.preferences.language), message, identifier: 'newPM', category: 'newPM', - payload: {replyTo: sender._id, senderName, message}, - } + payload: { replyTo: sender._id, senderName, message }, + }, ); } return messageSent; } -export async function getUserInbox (user, options = {asArray: true, page: 0, conversation: null, mapProps: false}) { +export async function getUserInbox (user, options = { + asArray: true, page: 0, conversation: null, mapProps: false, +}) { if (typeof options.asArray === 'undefined') { options.asArray = true; } @@ -39,7 +41,7 @@ export async function getUserInbox (user, options = {asArray: true, page: 0, con options.mapProps = false; } - const findObj = {ownerId: user._id}; + const findObj = { ownerId: user._id }; if (options.conversation) { findObj.uuid = options.conversation; @@ -47,7 +49,7 @@ export async function getUserInbox (user, options = {asArray: true, page: 0, con let query = Inbox .find(findObj) - .sort({timestamp: -1}); + .sort({ timestamp: -1 }); if (typeof options.page !== 'undefined') { query = query @@ -67,16 +69,15 @@ export async function getUserInbox (user, options = {asArray: true, page: 0, con if (options.asArray) { return messages; - } else { - const messagesObj = {}; - messages.forEach(msg => messagesObj[msg._id] = msg); - - return messagesObj; } + const messagesObj = {}; + messages.forEach(msg => messagesObj[msg._id] = msg); + + return messagesObj; } async function usersMapByConversations (owner, users) { - let query = Inbox + const query = Inbox .aggregate([ { $match: { @@ -87,8 +88,8 @@ async function usersMapByConversations (owner, users) { { $group: { _id: '$uuid', - userStyles: {$last: '$userStyles'}, - contributor: {$last: '$contributor'}, + userStyles: { $last: '$userStyles' }, + contributor: { $last: '$contributor' }, }, }, ]); @@ -106,7 +107,7 @@ async function usersMapByConversations (owner, users) { export async function listConversations (owner) { // group messages by user owned by logged-in user - let query = Inbox + const query = Inbox .aggregate([ { $match: { @@ -116,14 +117,14 @@ export async function listConversations (owner) { { $group: { _id: '$uuid', - user: {$last: '$user' }, - username: {$last: '$username' }, - timestamp: {$last: '$timestamp'}, - text: {$last: '$text'}, - count: {$sum: 1}, + user: { $last: '$user' }, + username: { $last: '$username' }, + timestamp: { $last: '$timestamp' }, + text: { $last: '$text' }, + count: { $sum: 1 }, }, }, - { $sort: {timestamp: -1}}, // sort by latest message + { $sort: { timestamp: -1 } }, // sort by latest message ]); const conversationsList = await query.exec(); @@ -133,7 +134,7 @@ export async function listConversations (owner) { // get user-info based on conversations const usersMap = await usersMapByConversations(owner, userIdList); - const conversations = conversationsList.map((res) => ({ + const conversations = conversationsList.map(res => ({ uuid: res._id, ...res, userStyles: usersMap[res._id].userStyles, @@ -144,13 +145,13 @@ export async function listConversations (owner) { } export async function getUserInboxMessage (user, messageId) { - return Inbox.findOne({ownerId: user._id, _id: messageId}).exec(); + return Inbox.findOne({ ownerId: user._id, _id: messageId }).exec(); } export async function deleteMessage (user, messageId) { - const message = await Inbox.findOne({_id: messageId, ownerId: user._id}).exec(); + const message = await Inbox.findOne({ _id: messageId, ownerId: user._id }).exec(); if (!message) return false; - await Inbox.remove({_id: message._id, ownerId: user._id}).exec(); + await Inbox.remove({ _id: message._id, ownerId: user._id }).exec(); return true; } @@ -160,6 +161,6 @@ export async function clearPMs (user) { await Promise.all([ user.save(), - Inbox.remove({ownerId: user._id}).exec(), + Inbox.remove({ ownerId: user._id }).exec(), ]); } diff --git a/website/server/libs/invites/index.js b/website/server/libs/invites/index.js index 2e0d2b37e0..7cc25dac58 100644 --- a/website/server/libs/invites/index.js +++ b/website/server/libs/invites/index.js @@ -27,8 +27,8 @@ function sendInvitePushNotification (userToInvite, groupLabel, group, publicGuil title: group.name, message: res.t(identifier, userToInvite.preferences.language), identifier, - payload: {groupID: group._id, publicGuild}, - } + payload: { groupID: group._id, publicGuild }, + }, ); } @@ -37,18 +37,18 @@ function sendInviteEmail (userToInvite, groupLabel, group, inviter) { const groupTemplate = group.type === 'guild' ? 'guild' : 'party'; const emailVars = [ - {name: 'INVITER', content: inviter.profile.name}, + { name: 'INVITER', content: inviter.profile.name }, ]; if (group.type === 'guild') { emailVars.push( - {name: 'GUILD_NAME', content: group.name}, - {name: 'GUILD_URL', content: '/groups/discovery'} + { name: 'GUILD_NAME', content: group.name }, + { name: 'GUILD_URL', content: '/groups/discovery' }, ); } else { emailVars.push( - {name: 'PARTY_NAME', content: group.name}, - {name: 'PARTY_URL', content: '/party'} + { name: 'PARTY_NAME', content: group.name }, + { name: 'PARTY_URL', content: '/party' }, ); } @@ -59,11 +59,11 @@ function inviteUserToGuild (userToInvite, group, inviter, publicGuild, res) { const uuid = userToInvite._id; if (_.includes(userToInvite.guilds, group._id)) { - throw new NotAuthorized(res.t('userAlreadyInGroup', { userId: uuid, username: userToInvite.profile.name})); + throw new NotAuthorized(res.t('userAlreadyInGroup', { userId: uuid, username: userToInvite.profile.name })); } - if (_.find(userToInvite.invitations.guilds, {id: group._id})) { - throw new NotAuthorized(res.t('userAlreadyInvitedToGroup', { userId: uuid, username: userToInvite.profile.name})); + if (_.find(userToInvite.invitations.guilds, { id: group._id })) { + throw new NotAuthorized(res.t('userAlreadyInvitedToGroup', { userId: uuid, username: userToInvite.profile.name })); } const guildInvite = { @@ -82,18 +82,18 @@ async function inviteUserToParty (userToInvite, group, inviter, res) { const uuid = userToInvite._id; // Do not add to invitations.parties array if the user is already invited to that party - if (_.find(userToInvite.invitations.parties, {id: group._id})) { - throw new NotAuthorized(res.t('userAlreadyPendingInvitation', { userId: uuid, username: userToInvite.profile.name})); + if (_.find(userToInvite.invitations.parties, { id: group._id })) { + throw new NotAuthorized(res.t('userAlreadyPendingInvitation', { userId: uuid, username: userToInvite.profile.name })); } if (userToInvite.party._id) { - let userParty = await Group.getGroup({user: userToInvite, groupId: 'party', fields: 'memberCount'}); + const userParty = await Group.getGroup({ user: userToInvite, groupId: 'party', fields: 'memberCount' }); // Allow user to be invited to a new party when they're partying solo - if (userParty && userParty.memberCount !== 1) throw new NotAuthorized(res.t('userAlreadyInAParty', { userId: uuid, username: userToInvite.profile.name})); + if (userParty && userParty.memberCount !== 1) throw new NotAuthorized(res.t('userAlreadyInAParty', { userId: uuid, username: userToInvite.profile.name })); } - let partyInvite = {id: group._id, name: group.name, inviter: inviter._id}; + const partyInvite = { id: group._id, name: group.name, inviter: inviter._id }; if (group.isSubscribed() && !group.hasNotCancelled()) partyInvite.cancelledPlan = true; userToInvite.invitations.parties.push(partyInvite); @@ -127,14 +127,14 @@ async function inviteByUUID (uuid, group, inviter, req, res) { const userToInvite = await User.findById(uuid).exec(); if (!userToInvite) { - throw new NotFound(res.t('userWithIDNotFound', {userId: uuid})); + throw new NotFound(res.t('userWithIDNotFound', { userId: uuid })); } else if (inviter._id === userToInvite._id) { throw new BadRequest(res.t('cannotInviteSelfToGroup')); } const objections = inviter.getObjectionsToInteraction('group-invitation', userToInvite); if (objections.length > 0) { - throw new NotAuthorized(res.t(objections[0], { userId: uuid, username: userToInvite.profile.name})); + throw new NotAuthorized(res.t(objections[0], { userId: uuid, username: userToInvite.profile.name })); } return await addInvitationToUser(userToInvite, group, inviter, res); @@ -145,12 +145,14 @@ async function inviteByEmail (invite, group, inviter, req, res) { if (!invite.email) throw new BadRequest(res.t('inviteMissingEmail')); - let userToContact = await User.findOne({$or: [ - {'auth.local.email': invite.email}, - {'auth.facebook.emails.value': invite.email}, - {'auth.google.emails.value': invite.email}, - ]}) - .select({_id: true, 'preferences.emailNotifications': true}) + const userToContact = await User.findOne({ + $or: [ + { 'auth.local.email': invite.email }, + { 'auth.facebook.emails.value': invite.email }, + { 'auth.google.emails.value': invite.email }, + ], + }) + .select({ _id: true, 'preferences.emailNotifications': true }) .exec(); if (userToContact) { @@ -168,20 +170,20 @@ async function inviteByEmail (invite, group, inviter, req, res) { sentAt: Date.now(), // so we can let it expire cancelledPlan, }); - let link = `/static/front?groupInvite=${encrypt(groupQueryString)}`; + const link = `/static/front?groupInvite=${encrypt(groupQueryString)}`; - let variables = [ - {name: 'LINK', content: link}, - {name: 'INVITER', content: req.body.inviter || inviter.profile.name}, + const variables = [ + { name: 'LINK', content: link }, + { name: 'INVITER', content: req.body.inviter || inviter.profile.name }, ]; if (group.type === 'guild') { - variables.push({name: 'GUILD_NAME', content: group.name}); + variables.push({ name: 'GUILD_NAME', content: group.name }); } // Check for the email address not to be unsubscribed - let userIsUnsubscribed = await EmailUnsubscription.findOne({email: invite.email}).exec(); - let groupLabel = group.type === 'guild' ? '-guild' : ''; + const userIsUnsubscribed = await EmailUnsubscription.findOne({ email: invite.email }).exec(); + const groupLabel = group.type === 'guild' ? '-guild' : ''; if (!userIsUnsubscribed) sendTxnEmail(invite, `invite-friend${groupLabel}`, variables); } @@ -191,7 +193,7 @@ async function inviteByEmail (invite, group, inviter, req, res) { async function inviteByUserName (username, group, inviter, req, res) { if (username.indexOf('@') === 0) username = username.slice(1, username.length); username = username.toLowerCase(); - const userToInvite = await User.findOne({'auth.local.lowerCaseUsername': username}).exec(); + const userToInvite = await User.findOne({ 'auth.local.lowerCaseUsername': username }).exec(); if (!userToInvite) { throw new NotFound(res.t('userWithUsernameNotFound', { username })); diff --git a/website/server/libs/items/utils.js b/website/server/libs/items/utils.js index cfa2b19bc2..05749bf94a 100644 --- a/website/server/libs/items/utils.js +++ b/website/server/libs/items/utils.js @@ -1,6 +1,6 @@ +import { last } from 'lodash'; import shared from '../../../common'; import { model as User } from '../../models/user'; -import { last } from 'lodash'; // Build a list of gear items owned by default const defaultOwnedGear = {}; @@ -14,7 +14,7 @@ Object.keys(shared.content.gear.flat).forEach(key => { export function getDefaultOwnedGear () { // Clone to avoid modifications to the original object - return Object.assign({}, defaultOwnedGear); + return { ...defaultOwnedGear }; } // When passed a path to an item in the user object it'll return true if @@ -62,18 +62,18 @@ export function validateItemPath (itemPath) { // will be converted to the number 5 export function castItemVal (itemPath, itemVal) { if ( - itemPath.indexOf('items.pets') === 0 || - itemPath.indexOf('items.eggs') === 0 || - itemPath.indexOf('items.hatchingPotions') === 0 || - itemPath.indexOf('items.food') === 0 || - itemPath.indexOf('items.quests') === 0 + itemPath.indexOf('items.pets') === 0 + || itemPath.indexOf('items.eggs') === 0 + || itemPath.indexOf('items.hatchingPotions') === 0 + || itemPath.indexOf('items.food') === 0 + || itemPath.indexOf('items.quests') === 0 ) { return Number(itemVal); } if ( - itemPath.indexOf('items.mounts') === 0 || - itemPath.indexOf('items.gear.owned') === 0 + itemPath.indexOf('items.mounts') === 0 + || itemPath.indexOf('items.gear.owned') === 0 ) { if (itemVal === 'true') return true; if (itemVal === 'false') return false; @@ -82,4 +82,4 @@ export function castItemVal (itemPath, itemVal) { } return itemVal; -} \ No newline at end of file +} diff --git a/website/server/libs/logger.js b/website/server/libs/logger.js index caf7661fed..fe5a4b1fa6 100644 --- a/website/server/libs/logger.js +++ b/website/server/libs/logger.js @@ -38,7 +38,7 @@ if (IS_PROD) { } // exports a public interface insteaf of accessing directly the logger module -let loggerInterface = { +const loggerInterface = { info (...args) { logger.info(...args); }, @@ -48,17 +48,17 @@ let loggerInterface = { // and an object of additional data to log alongside the error // If the first argument isn't an Error, it'll call logger.error with all the arguments supplied error (...args) { - let [err, errorData = {}, ...otherArgs] = args; + const [err, errorData = {}, ...otherArgs] = args; if (err instanceof Error) { // pass the error stack as the first parameter to logger.error - let stack = err.stack || err.message || err; + const stack = err.stack || err.message || err; if (_.isPlainObject(errorData) && !errorData.fullError) { // If the error object has interesting data (not only httpCode, message and name from the CustomError class) // add it to the logs if (err instanceof CustomError) { - let errWithoutCommonProps = _.omit(err, ['name', 'httpCode', 'message']); + const errWithoutCommonProps = _.omit(err, ['name', 'httpCode', 'message']); if (Object.keys(errWithoutCommonProps).length > 0) { errorData.fullError = errWithoutCommonProps; @@ -68,7 +68,7 @@ let loggerInterface = { } } - let loggerArgs = [stack, errorData, ...otherArgs]; + const loggerArgs = [stack, errorData, ...otherArgs]; // Treat 4xx errors that are handled as warnings, 5xx and uncaught errors as serious problems if (!errorData || !errorData.isHandledError || errorData.httpCode >= 500) { @@ -85,7 +85,7 @@ let loggerInterface = { // Logs unhandled promises errors // when no catch is attached to a promise a unhandledRejection event will be triggered // reason is the error, p the promise where it originated -process.on('unhandledRejection', function handlePromiseRejection (reason, p) { +process.on('unhandledRejection', (reason, p) => { loggerInterface.error(reason, 'unhandledPromiseRejection at', p); }); diff --git a/website/server/libs/password.js b/website/server/libs/password.js index 4dfcfc4162..54ffc38df0 100644 --- a/website/server/libs/password.js +++ b/website/server/libs/password.js @@ -1,8 +1,8 @@ // Utilities for working with passwords import crypto from 'crypto'; import bcrypt from 'bcrypt'; -import { decrypt } from './encryption'; import moment from 'moment'; +import { decrypt } from './encryption'; import { model as User } from '../models/user'; const BCRYPT_SALT_ROUNDS = 10; @@ -41,18 +41,17 @@ export function sha1MakeSalt (len = 10) { export async function compare (user, passwordToCheck) { if (!user || !passwordToCheck) throw new Error('user and passwordToCheck are required parameters.'); - let passwordHashMethod = user.auth.local.passwordHashMethod; - let passwordHash = user.auth.local.hashed_password; - let passwordSalt = user.auth.local.salt; // Only used for SHA1 + const { passwordHashMethod } = user.auth.local; + const passwordHash = user.auth.local.hashed_password; + const passwordSalt = user.auth.local.salt; // Only used for SHA1 if (passwordHashMethod === 'bcrypt') { return await bcryptCompare(passwordToCheck, passwordHash); // default to sha1 if the user has a salt but no passwordHashMethod - } else if (passwordHashMethod === 'sha1' || !passwordHashMethod && passwordSalt) { + } if (passwordHashMethod === 'sha1' || !passwordHashMethod && passwordSalt) { return passwordHash === sha1Encrypt(passwordToCheck, passwordSalt); - } else { - throw new Error('Invalid password hash method.'); } + throw new Error('Invalid password hash method.'); } // Convert an user to use bcrypt from sha1 for password hashing @@ -79,7 +78,7 @@ export async function validatePasswordResetCodeAndFindUser (code) { try { decryptedPasswordResetCode = JSON.parse(decrypt(code || 'invalid')); // also catches missing code userId = decryptedPasswordResetCode.userId; - let expiresAt = decryptedPasswordResetCode.expiresAt; + const { expiresAt } = decryptedPasswordResetCode; if (moment(expiresAt).isBefore(moment())) throw new Error(); } catch (err) { diff --git a/website/server/libs/payments/amazon.js b/website/server/libs/payments/amazon.js index 215b2dc1bb..0c5dec5260 100644 --- a/website/server/libs/payments/amazon.js +++ b/website/server/libs/payments/amazon.js @@ -21,10 +21,10 @@ import { model as Coupon } from '../../models/coupon'; // TODO better handling of errors -const i18n = common.i18n; +const { i18n } = common; const IS_SANDBOX = nconf.get('AMAZON_PAYMENTS_MODE') === 'sandbox'; -let amzPayment = amazonPayments.connect({ +const amzPayment = amazonPayments.connect({ environment: amazonPayments.Environment[IS_SANDBOX ? 'Sandbox' : 'Production'], sellerId: nconf.get('AMAZON_PAYMENTS_SELLER_ID'), mwsAccessKey: nconf.get('AMAZON_PAYMENTS_MWS_KEY'), @@ -32,7 +32,7 @@ let amzPayment = amazonPayments.connect({ clientId: nconf.get('AMAZON_PAYMENTS_CLIENT_ID'), }); -let api = {}; +const api = {}; api.constants = { CURRENCY_CODE: 'USD', @@ -93,7 +93,9 @@ api.authorize = function authorize (inputSet) { * @return undefined */ api.checkout = async function checkout (options = {}) { - let {gift, user, orderReferenceId, headers} = options; + const { + gift, user, orderReferenceId, headers, + } = options; let amount = 5; if (gift) { @@ -149,7 +151,7 @@ api.checkout = async function checkout (options = {}) { // execute payment let method = this.constants.METHOD_BUY_GEMS; - let data = { + const data = { user, paymentMethod: this.constants.PAYMENT_METHOD, headers, @@ -177,15 +179,19 @@ api.checkout = async function checkout (options = {}) { * @return undefined */ api.cancelSubscription = async function cancelSubscription (options = {}) { - let {user, groupId, headers, cancellationReason} = options; + const { + user, groupId, headers, cancellationReason, + } = options; let billingAgreementId; let planId; let lastBillingDate; if (groupId) { - let groupFields = basicGroupFields.concat(' purchased'); - let group = await Group.getGroup({user, groupId, populateLeader: false, groupFields}); + const groupFields = basicGroupFields.concat(' purchased'); + const group = await Group.getGroup({ + user, groupId, populateLeader: false, groupFields, + }); if (!group) { throw new NotFound(i18n.t('groupNotFound')); @@ -206,23 +212,21 @@ api.cancelSubscription = async function cancelSubscription (options = {}) { if (!billingAgreementId) throw new NotAuthorized(i18n.t('missingSubscription')); - let details = await this.getBillingAgreementDetails({ + const details = await this.getBillingAgreementDetails({ AmazonBillingAgreementId: billingAgreementId, - }).catch(function errorCatch (err) { - return err; - }); + }).catch(err => err); - let badBAStates = ['Canceled', 'Closed', 'Suspended']; - if (details && details.BillingAgreementDetails && details.BillingAgreementDetails.BillingAgreementStatus && - badBAStates.indexOf(details.BillingAgreementDetails.BillingAgreementStatus.State) === -1) { + const badBAStates = ['Canceled', 'Closed', 'Suspended']; + if (details && details.BillingAgreementDetails && details.BillingAgreementDetails.BillingAgreementStatus + && badBAStates.indexOf(details.BillingAgreementDetails.BillingAgreementStatus.State) === -1) { await this.closeBillingAgreement({ AmazonBillingAgreementId: billingAgreementId, }); } - let subscriptionBlock = common.content.subscriptionBlocks[planId]; - let subscriptionLength = subscriptionBlock.months * 30; + const subscriptionBlock = common.content.subscriptionBlocks[planId]; + const subscriptionLength = subscriptionBlock.months * 30; await payments.cancelSubscription({ user, @@ -247,7 +251,7 @@ api.cancelSubscription = async function cancelSubscription (options = {}) { * @return undefined */ api.subscribe = async function subscribe (options) { - let { + const { billingAgreementId, sub, coupon, @@ -261,17 +265,19 @@ api.subscribe = async function subscribe (options) { if (sub.discount) { // apply discount if (!coupon) throw new BadRequest(i18n.t('couponCodeRequired')); - let result = await Coupon.findOne({_id: cc.validate(coupon), event: sub.key}).exec(); + const result = await Coupon.findOne({ _id: cc.validate(coupon), event: sub.key }).exec(); if (!result) throw new NotAuthorized(i18n.t('invalidCoupon')); } let amount = sub.price; - let leaderCount = 1; - let priceOfSingleMember = 3; + const leaderCount = 1; + const priceOfSingleMember = 3; if (groupId) { const groupFields = basicGroupFields.concat(' purchased'); - const group = await Group.getGroup({user, groupId, populateLeader: false, groupFields}); + const group = await Group.getGroup({ + user, groupId, populateLeader: false, groupFields, + }); const membersCount = await group.getMemberCount(); amount = sub.price + (membersCount - leaderCount) * priceOfSingleMember; } @@ -322,7 +328,7 @@ api.subscribe = async function subscribe (options) { api.chargeForAdditionalGroupMember = async function chargeForAdditionalGroupMember (group) { // @TODO: Can we get this from the content plan? - let priceForNewMember = 3; + const priceForNewMember = 3; // @TODO: Prorate? diff --git a/website/server/libs/payments/apple.js b/website/server/libs/payments/apple.js index 3c85d512d7..7c581b83e8 100644 --- a/website/server/libs/payments/apple.js +++ b/website/server/libs/payments/apple.js @@ -1,3 +1,4 @@ +import moment from 'moment'; import shared from '../../../common'; import iap from '../inAppPurchases'; import payments from './payments'; @@ -6,10 +7,9 @@ import { BadRequest, } from '../errors'; import { model as IapPurchaseReceipt } from '../../models/iapPurchaseReceipt'; -import {model as User } from '../../models/user'; -import moment from 'moment'; +import { model as User } from '../../models/user'; -let api = {}; +const api = {}; api.constants = { PAYMENT_METHOD_APPLE: 'Apple', @@ -22,7 +22,9 @@ api.constants = { }; api.verifyGemPurchase = async function verifyGemPurchase (options) { - let {gift, user, receipt, headers} = options; + const { + gift, user, receipt, headers, + } = options; if (gift) { gift.member = await User.findById(gift.uuid).exec(); @@ -32,19 +34,19 @@ api.verifyGemPurchase = async function verifyGemPurchase (options) { if (!receiverCanGetGems) throw new NotAuthorized(shared.i18n.t('groupPolicyCannotGetGems', user.preferences.language)); await iap.setup(); - let appleRes = await iap.validate(iap.APPLE, receipt); - let isValidated = iap.isValidated(appleRes); + const appleRes = await iap.validate(iap.APPLE, receipt); + const isValidated = iap.isValidated(appleRes); if (!isValidated) throw new NotAuthorized(api.constants.RESPONSE_INVALID_RECEIPT); const purchaseDataList = iap.getPurchaseData(appleRes); if (purchaseDataList.length === 0) throw new NotAuthorized(api.constants.RESPONSE_NO_ITEM_PURCHASED); let correctReceipt = false; // Purchasing one item at a time (processing of await(s) below is sequential not parallel) - for (let index in purchaseDataList) { - let purchaseData = purchaseDataList[index]; - let token = purchaseData.transactionId; + for (const index in purchaseDataList) { + const purchaseData = purchaseDataList[index]; + const token = purchaseData.transactionId; - let existingReceipt = await IapPurchaseReceipt.findOne({ // eslint-disable-line no-await-in-loop + const existingReceipt = await IapPurchaseReceipt.findOne({ // eslint-disable-line no-await-in-loop _id: token, }).exec(); @@ -115,19 +117,19 @@ api.subscribe = async function subscribe (sku, user, receipt, headers, nextPayme if (!sub) throw new NotAuthorized(this.constants.RESPONSE_INVALID_ITEM); await iap.setup(); - let appleRes = await iap.validate(iap.APPLE, receipt); + const appleRes = await iap.validate(iap.APPLE, receipt); const isValidated = iap.isValidated(appleRes); if (!isValidated) throw new NotAuthorized(api.constants.RESPONSE_INVALID_RECEIPT); - let purchaseDataList = iap.getPurchaseData(appleRes); + const purchaseDataList = iap.getPurchaseData(appleRes); if (purchaseDataList.length === 0) throw new NotAuthorized(api.constants.RESPONSE_NO_ITEM_PURCHASED); let transactionId; - for (let index in purchaseDataList) { - let purchaseData = purchaseDataList[index]; + for (const index in purchaseDataList) { + const purchaseData = purchaseDataList[index]; - let dateTerminated = new Date(Number(purchaseData.expirationDate)); + const dateTerminated = new Date(Number(purchaseData.expirationDate)); if (purchaseData.productId === sku && dateTerminated > new Date()) { transactionId = purchaseData.transactionId; break; @@ -135,12 +137,12 @@ api.subscribe = async function subscribe (sku, user, receipt, headers, nextPayme } if (transactionId) { - let existingUser = await User.findOne({ + const existingUser = await User.findOne({ 'purchased.plan.customerId': transactionId, }).exec(); if (existingUser) throw new NotAuthorized(this.constants.RESPONSE_ALREADY_USED); - nextPaymentProcessing = nextPaymentProcessing ? nextPaymentProcessing : moment.utc().add({days: 2}); + nextPaymentProcessing = nextPaymentProcessing || moment.utc().add({ days: 2 }); await payments.createSubscription({ user, @@ -157,7 +159,9 @@ api.subscribe = async function subscribe (sku, user, receipt, headers, nextPayme }; api.noRenewSubscribe = async function noRenewSubscribe (options) { - let {sku, gift, user, receipt, headers} = options; + const { + sku, gift, user, receipt, headers, + } = options; if (!sku) throw new BadRequest(shared.i18n.t('missingSubscriptionCode')); @@ -180,19 +184,19 @@ api.noRenewSubscribe = async function noRenewSubscribe (options) { if (!sub) throw new NotAuthorized(this.constants.RESPONSE_INVALID_ITEM); await iap.setup(); - let appleRes = await iap.validate(iap.APPLE, receipt); + const appleRes = await iap.validate(iap.APPLE, receipt); const isValidated = iap.isValidated(appleRes); if (!isValidated) throw new NotAuthorized(api.constants.RESPONSE_INVALID_RECEIPT); - let purchaseDataList = iap.getPurchaseData(appleRes); + const purchaseDataList = iap.getPurchaseData(appleRes); if (purchaseDataList.length === 0) throw new NotAuthorized(api.constants.RESPONSE_NO_ITEM_PURCHASED); let transactionId; - for (let index in purchaseDataList) { - let purchaseData = purchaseDataList[index]; + for (const index in purchaseDataList) { + const purchaseData = purchaseDataList[index]; - let dateTerminated = new Date(Number(purchaseData.expirationDate)); + const dateTerminated = new Date(Number(purchaseData.expirationDate)); if (purchaseData.productId === sku && dateTerminated > new Date()) { transactionId = purchaseData.transactionId; break; @@ -200,7 +204,7 @@ api.noRenewSubscribe = async function noRenewSubscribe (options) { } if (transactionId) { - let existingReceipt = await IapPurchaseReceipt.findOne({ // eslint-disable-line no-await-in-loop + const existingReceipt = await IapPurchaseReceipt.findOne({ // eslint-disable-line no-await-in-loop _id: transactionId, }).exec(); if (existingReceipt) throw new NotAuthorized(this.constants.RESPONSE_ALREADY_USED); @@ -211,7 +215,7 @@ api.noRenewSubscribe = async function noRenewSubscribe (options) { // This should always be the buying user even for a gift. userId: user._id, }); - let data = { + const data = { user, paymentMethod: this.constants.PAYMENT_METHOD_APPLE, headers, @@ -233,7 +237,7 @@ api.noRenewSubscribe = async function noRenewSubscribe (options) { }; api.cancelSubscribe = async function cancelSubscribe (user, headers) { - let plan = user.purchased.plan; + const { plan } = user.purchased; if (plan.paymentMethod !== api.constants.PAYMENT_METHOD_APPLE) throw new NotAuthorized(shared.i18n.t('missingSubscription')); @@ -242,14 +246,14 @@ api.cancelSubscribe = async function cancelSubscribe (user, headers) { let dateTerminated; try { - let appleRes = await iap.validate(iap.APPLE, plan.additionalData); + const appleRes = await iap.validate(iap.APPLE, plan.additionalData); - let isValidated = iap.isValidated(appleRes); + const isValidated = iap.isValidated(appleRes); if (!isValidated) throw new NotAuthorized(this.constants.RESPONSE_INVALID_RECEIPT); - let purchases = iap.getPurchaseData(appleRes); + const purchases = iap.getPurchaseData(appleRes); if (purchases.length === 0) throw new NotAuthorized(this.constants.RESPONSE_INVALID_RECEIPT); - let subscriptionData = purchases[0]; + const subscriptionData = purchases[0]; dateTerminated = new Date(Number(subscriptionData.expirationDate)); if (dateTerminated > new Date()) throw new NotAuthorized(this.constants.RESPONSE_STILL_VALID); diff --git a/website/server/libs/payments/gems.js b/website/server/libs/payments/gems.js index 9ee9546e41..20be8bff83 100644 --- a/website/server/libs/payments/gems.js +++ b/website/server/libs/payments/gems.js @@ -27,13 +27,13 @@ async function buyGemGift (data) { const languages = [data.user.preferences.language, data.gift.member.preferences.language]; const senderMsg = getGiftMessage(data, byUsername, gemAmount, languages[0]); - const receiverMsg = getGiftMessage(data, byUsername, gemAmount, languages[1]); + const receiverMsg = getGiftMessage(data, byUsername, gemAmount, languages[1]); data.user.sendMessage(data.gift.member, { receiverMsg, senderMsg, save: false }); if (data.gift.member.preferences.emailNotifications.giftedGems !== false) { txnEmail(data.gift.member, 'gifted-gems', [ - {name: 'GIFTER', content: byUsername}, - {name: 'X_GEMS_GIFTED', content: gemAmount}, + { name: 'GIFTER', content: byUsername }, + { name: 'X_GEMS_GIFTED', content: gemAmount }, ]); } @@ -43,9 +43,9 @@ async function buyGemGift (data) { data.gift.member, { title: shared.i18n.t('giftedGems', languages[1]), - message: shared.i18n.t('giftedGemsInfo', {amount: gemAmount, name: byUsername}, languages[1]), + message: shared.i18n.t('giftedGemsInfo', { amount: gemAmount, name: byUsername }, languages[1]), identifier: 'giftedGems', - } + }, ); } diff --git a/website/server/libs/payments/google.js b/website/server/libs/payments/google.js index eac9b123fc..1d4a0f800e 100644 --- a/website/server/libs/payments/google.js +++ b/website/server/libs/payments/google.js @@ -1,3 +1,4 @@ +import moment from 'moment'; import shared from '../../../common'; import iap from '../inAppPurchases'; import payments from './payments'; @@ -6,10 +7,9 @@ import { BadRequest, } from '../errors'; import { model as IapPurchaseReceipt } from '../../models/iapPurchaseReceipt'; -import {model as User } from '../../models/user'; -import moment from 'moment'; +import { model as User } from '../../models/user'; -let api = {}; +const api = {}; api.constants = { PAYMENT_METHOD_GOOGLE: 'Google', @@ -21,7 +21,9 @@ api.constants = { }; api.verifyGemPurchase = async function verifyGemPurchase (options) { - let {gift, user, receipt, signature, headers} = options; + const { + gift, user, receipt, signature, headers, + } = options; if (gift) { gift.member = await User.findById(gift.uuid).exec(); @@ -32,20 +34,20 @@ api.verifyGemPurchase = async function verifyGemPurchase (options) { await iap.setup(); - let testObj = { + const testObj = { data: receipt, signature, }; - let googleRes = await iap.validate(iap.GOOGLE, testObj); + const googleRes = await iap.validate(iap.GOOGLE, testObj); - let isValidated = iap.isValidated(googleRes); + const isValidated = iap.isValidated(googleRes); if (!isValidated) throw new NotAuthorized(this.constants.RESPONSE_INVALID_RECEIPT); - let receiptObj = typeof testObj.data === 'string' ? JSON.parse(testObj.data) : testObj.data; // passed as a string - let token = receiptObj.token || receiptObj.purchaseToken; + const receiptObj = typeof testObj.data === 'string' ? JSON.parse(testObj.data) : testObj.data; // passed as a string + const token = receiptObj.token || receiptObj.purchaseToken; - let existingReceipt = await IapPurchaseReceipt.findOne({ + const existingReceipt = await IapPurchaseReceipt.findOne({ _id: token, }).exec(); if (existingReceipt) throw new NotAuthorized(this.constants.RESPONSE_ALREADY_USED); @@ -104,30 +106,30 @@ api.subscribe = async function subscribe (sku, user, receipt, signature, headers subCode = 'basic_12mo'; break; } - let sub = subCode ? shared.content.subscriptionBlocks[subCode] : false; + const sub = subCode ? shared.content.subscriptionBlocks[subCode] : false; if (!sub) throw new NotAuthorized(this.constants.RESPONSE_INVALID_ITEM); await iap.setup(); - let testObj = { + const testObj = { data: receipt, signature, }; - let receiptObj = typeof receipt === 'string' ? JSON.parse(receipt) : receipt; // passed as a string - let token = receiptObj.token || receiptObj.purchaseToken; + const receiptObj = typeof receipt === 'string' ? JSON.parse(receipt) : receipt; // passed as a string + const token = receiptObj.token || receiptObj.purchaseToken; - let existingUser = await User.findOne({ + const existingUser = await User.findOne({ 'purchased.plan.customerId': token, }).exec(); if (existingUser) throw new NotAuthorized(this.constants.RESPONSE_ALREADY_USED); - let googleRes = await iap.validate(iap.GOOGLE, testObj); + const googleRes = await iap.validate(iap.GOOGLE, testObj); - let isValidated = iap.isValidated(googleRes); + const isValidated = iap.isValidated(googleRes); if (!isValidated) throw new NotAuthorized(this.constants.RESPONSE_INVALID_RECEIPT); - nextPaymentProcessing = nextPaymentProcessing ? nextPaymentProcessing : moment.utc().add({days: 2}); + nextPaymentProcessing = nextPaymentProcessing || moment.utc().add({ days: 2 }); await payments.createSubscription({ user, @@ -141,7 +143,9 @@ api.subscribe = async function subscribe (sku, user, receipt, signature, headers }; api.noRenewSubscribe = async function noRenewSubscribe (options) { - let {sku, gift, user, receipt, signature, headers} = options; + const { + sku, gift, user, receipt, signature, headers, + } = options; if (!sku) throw new BadRequest(shared.i18n.t('missingSubscriptionCode')); let subCode; switch (sku) { @@ -158,20 +162,20 @@ api.noRenewSubscribe = async function noRenewSubscribe (options) { subCode = 'basic_12mo'; break; } - let sub = subCode ? shared.content.subscriptionBlocks[subCode] : false; + const sub = subCode ? shared.content.subscriptionBlocks[subCode] : false; if (!sub) throw new NotAuthorized(this.constants.RESPONSE_INVALID_ITEM); await iap.setup(); - let testObj = { + const testObj = { data: receipt, signature, }; - let receiptObj = typeof receipt === 'string' ? JSON.parse(receipt) : receipt; // passed as a string - let token = receiptObj.token || receiptObj.purchaseToken; + const receiptObj = typeof receipt === 'string' ? JSON.parse(receipt) : receipt; // passed as a string + const token = receiptObj.token || receiptObj.purchaseToken; - let existingReceipt = await IapPurchaseReceipt.findOne({ // eslint-disable-line no-await-in-loop + const existingReceipt = await IapPurchaseReceipt.findOne({ // eslint-disable-line no-await-in-loop _id: token, }).exec(); if (existingReceipt) throw new NotAuthorized(this.constants.RESPONSE_ALREADY_USED); @@ -183,12 +187,12 @@ api.noRenewSubscribe = async function noRenewSubscribe (options) { userId: user._id, }); - let googleRes = await iap.validate(iap.GOOGLE, testObj); + const googleRes = await iap.validate(iap.GOOGLE, testObj); - let isValidated = iap.isValidated(googleRes); + const isValidated = iap.isValidated(googleRes); if (!isValidated) throw new NotAuthorized(this.constants.RESPONSE_INVALID_RECEIPT); - let data = { + const data = { user, paymentMethod: this.constants.PAYMENT_METHOD_GOOGLE, headers, @@ -210,7 +214,7 @@ api.noRenewSubscribe = async function noRenewSubscribe (options) { api.cancelSubscribe = async function cancelSubscribe (user, headers) { - let plan = user.purchased.plan; + const { plan } = user.purchased; if (plan.paymentMethod !== api.constants.PAYMENT_METHOD_GOOGLE) throw new NotAuthorized(shared.i18n.t('missingSubscription')); @@ -219,14 +223,14 @@ api.cancelSubscribe = async function cancelSubscribe (user, headers) { let dateTerminated; try { - let googleRes = await iap.validate(iap.GOOGLE, plan.additionalData); + const googleRes = await iap.validate(iap.GOOGLE, plan.additionalData); - let isValidated = iap.isValidated(googleRes); + const isValidated = iap.isValidated(googleRes); if (!isValidated) throw new NotAuthorized(this.constants.RESPONSE_INVALID_RECEIPT); - let purchases = iap.getPurchaseData(googleRes); + const purchases = iap.getPurchaseData(googleRes); if (purchases.length === 0) throw new NotAuthorized(this.constants.RESPONSE_INVALID_RECEIPT); - let subscriptionData = purchases[0]; + const subscriptionData = purchases[0]; dateTerminated = new Date(Number(subscriptionData.expirationDate)); } catch (err) { // Status:410 means that the subsctiption isn't active anymore and we can safely delete it diff --git a/website/server/libs/payments/groupPayments.js b/website/server/libs/payments/groupPayments.js index 1bf8398f9a..b5aa0ce4bf 100644 --- a/website/server/libs/payments/groupPayments.js +++ b/website/server/libs/payments/groupPayments.js @@ -31,14 +31,12 @@ function _dateDiff (earlyDate, lateDate) { async function addSubscriptionToGroupUsers (group) { let members; if (group.type === 'guild') { - members = await User.find({guilds: group._id}).select('_id purchased items auth profile.name notifications').exec(); + members = await User.find({ guilds: group._id }).select('_id purchased items auth profile.name notifications').exec(); } else { - members = await User.find({'party._id': group._id}).select('_id purchased items auth profile.name notifications').exec(); + members = await User.find({ 'party._id': group._id }).select('_id purchased items auth profile.name notifications').exec(); } - let promises = members.map((member) => { - return this.addSubToGroupUser(member, group); - }); + const promises = members.map(member => this.addSubToGroupUser(member, group)); await Promise.all(promises); } @@ -63,12 +61,12 @@ async function addSubToGroupUser (member, group) { // When changing customerIdsToIgnore or paymentMethodsToIgnore, the code blocks below for // the `group-member-join` email template will probably need to be changed. - let customerIdsToIgnore = [this.constants.GROUP_PLAN_CUSTOMER_ID, this.constants.UNLIMITED_CUSTOMER_ID]; - let paymentMethodsToIgnore = [this.constants.GOOGLE_PAYMENT_METHOD, this.constants.IOS_PAYMENT_METHOD]; + const customerIdsToIgnore = [this.constants.GROUP_PLAN_CUSTOMER_ID, this.constants.UNLIMITED_CUSTOMER_ID]; + const paymentMethodsToIgnore = [this.constants.GOOGLE_PAYMENT_METHOD, this.constants.IOS_PAYMENT_METHOD]; let previousSubscriptionType = EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NONE; - let leader = await User.findById(group.leader).exec(); + const leader = await User.findById(group.leader).exec(); - let data = { + const data = { user: {}, sub: { key: 'group_plan_auto', @@ -96,20 +94,20 @@ async function addSubToGroupUser (member, group) { }, }; - let memberPlan = member.purchased.plan; + const memberPlan = member.purchased.plan; if (member.isSubscribed()) { - let customerHasCancelledGroupPlan = memberPlan.customerId === this.constants.GROUP_PLAN_CUSTOMER_ID && !member.hasNotCancelled(); - let ignorePaymentPlan = paymentMethodsToIgnore.indexOf(memberPlan.paymentMethod) !== -1; - let ignoreCustomerId = customerIdsToIgnore.indexOf(memberPlan.customerId) !== -1; + const customerHasCancelledGroupPlan = memberPlan.customerId === this.constants.GROUP_PLAN_CUSTOMER_ID && !member.hasNotCancelled(); + const ignorePaymentPlan = paymentMethodsToIgnore.indexOf(memberPlan.paymentMethod) !== -1; + const ignoreCustomerId = customerIdsToIgnore.indexOf(memberPlan.customerId) !== -1; if (ignorePaymentPlan) { - txnEmail({email: TECH_ASSISTANCE_EMAIL}, 'admin-user-subscription-details', [ - {name: 'PROFILE_NAME', content: member.profile.name}, - {name: 'UUID', content: member._id}, - {name: 'EMAIL', content: getUserInfo(member, ['email']).email}, - {name: 'PAYMENT_METHOD', content: memberPlan.paymentMethod}, - {name: 'PURCHASED_PLAN', content: JSON.stringify(memberPlan)}, - {name: 'ACTION_NEEDED', content: 'User has joined group plan and has been told to cancel their subscription then email us. Ensure they do that then give them free sub.'}, + txnEmail({ email: TECH_ASSISTANCE_EMAIL }, 'admin-user-subscription-details', [ + { name: 'PROFILE_NAME', content: member.profile.name }, + { name: 'UUID', content: member._id }, + { name: 'EMAIL', content: getUserInfo(member, ['email']).email }, + { name: 'PAYMENT_METHOD', content: memberPlan.paymentMethod }, + { name: 'PURCHASED_PLAN', content: JSON.stringify(memberPlan) }, + { name: 'ACTION_NEEDED', content: 'User has joined group plan and has been told to cancel their subscription then email us. Ensure they do that then give them free sub.' }, // TODO User won't get email instructions if they've opted out of all emails. See if we can make this email an exception and if not, report here whether they've opted out. ]); } @@ -131,19 +129,19 @@ async function addSubToGroupUser (member, group) { previousSubscriptionType = EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_UNKNOWN; } txnEmail(member, 'group-member-join', [ - {name: 'LEADER', content: leader.profile.name}, - {name: 'GROUP_NAME', content: group.name}, - {name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: previousSubscriptionType}, + { name: 'LEADER', content: leader.profile.name }, + { name: 'GROUP_NAME', content: group.name }, + { name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: previousSubscriptionType }, ]); return; } if (member.hasNotCancelled()) { - await member.cancelSubscription({cancellationReason: JOINED_GROUP_PLAN}); + await member.cancelSubscription({ cancellationReason: JOINED_GROUP_PLAN }); previousSubscriptionType = EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NORMAL; } - let today = new Date(); + const today = new Date(); plan = member.purchased.plan.toObject(); let extraMonths = Number(plan.extraMonths); if (plan.dateTerminated) extraMonths += _dateDiff(today, plan.dateTerminated); @@ -176,9 +174,9 @@ async function addSubToGroupUser (member, group) { await this.createSubscription(data); txnEmail(data.user, 'group-member-join', [ - {name: 'LEADER', content: leader.profile.name}, - {name: 'GROUP_NAME', content: group.name}, - {name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: previousSubscriptionType}, + { name: 'LEADER', content: leader.profile.name }, + { name: 'GROUP_NAME', content: group.name }, + { name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: previousSubscriptionType }, ]); } @@ -192,14 +190,12 @@ async function addSubToGroupUser (member, group) { async function cancelGroupUsersSubscription (group) { let members; if (group.type === 'guild') { - members = await User.find({guilds: group._id}).select('_id guilds purchased').exec(); + members = await User.find({ guilds: group._id }).select('_id guilds purchased').exec(); } else { - members = await User.find({'party._id': group._id}).select('_id guilds purchased').exec(); + members = await User.find({ 'party._id': group._id }).select('_id guilds purchased').exec(); } - let promises = members.map((member) => { - return this.cancelGroupSubscriptionForUser(member, group); - }); + const promises = members.map(member => this.cancelGroupSubscriptionForUser(member, group)); await Promise.all(promises); } @@ -207,31 +203,31 @@ async function cancelGroupUsersSubscription (group) { async function cancelGroupSubscriptionForUser (user, group, userWasRemoved = false) { if (user.purchased.plan.customerId !== this.constants.GROUP_PLAN_CUSTOMER_ID) return; - let userGroups = user.guilds.toObject(); + const userGroups = user.guilds.toObject(); if (user.party._id) userGroups.push(user.party._id); - let index = userGroups.indexOf(group._id); + const index = userGroups.indexOf(group._id); if (index >= 0) userGroups.splice(index, 1); - let groupPlansQuery = { + const groupPlansQuery = { // type: { $in: ['guild', 'party'] }, // privacy: 'private', - _id: {$in: userGroups}, + _id: { $in: userGroups }, 'purchased.plan.dateTerminated': { $type: 'null' }, }; - let groupFields = `${basicGroupFields} purchased`; - let userGroupPlans = await Group.find(groupPlansQuery).select(groupFields).exec(); + const groupFields = `${basicGroupFields} purchased`; + const userGroupPlans = await Group.find(groupPlansQuery).select(groupFields).exec(); - if (userGroupPlans.length === 0) { - let leader = await User.findById(group.leader).exec(); + if (userGroupPlans.length === 0) { + const leader = await User.findById(group.leader).exec(); const email = userWasRemoved ? 'group-member-removed' : 'group-member-cancel'; txnEmail(user, email, [ - {name: 'LEADER', content: leader.profile.name}, - {name: 'GROUP_NAME', content: group.name}, + { name: 'LEADER', content: leader.profile.name }, + { name: 'GROUP_NAME', content: group.name }, ]); - await this.cancelSubscription({user}); + await this.cancelSubscription({ user }); } } diff --git a/website/server/libs/payments/payments.js b/website/server/libs/payments/payments.js index 70f1f109d3..4f3ddf3f05 100644 --- a/website/server/libs/payments/payments.js +++ b/website/server/libs/payments/payments.js @@ -12,7 +12,7 @@ import { buyGems, } from './gems'; -let api = {}; +const api = {}; api.constants = { UNLIMITED_CUSTOMER_ID: 'habitrpg', // Users with the customerId have an unlimted free subscription diff --git a/website/server/libs/payments/paypal.js b/website/server/libs/payments/paypal.js index d1a445cdc4..edab26ec6f 100644 --- a/website/server/libs/payments/paypal.js +++ b/website/server/libs/payments/paypal.js @@ -3,11 +3,11 @@ import nconf from 'nconf'; import moment from 'moment'; import util from 'util'; import _ from 'lodash'; -import payments from './payments'; import ipn from 'paypal-ipn'; import paypal from 'paypal-rest-sdk'; -import shared from '../../../common'; import cc from 'coupon-code'; +import shared from '../../../common'; +import payments from './payments'; import { model as Coupon } from '../../models/coupon'; import { model as User } from '../../models/user'; import { @@ -22,12 +22,12 @@ import { const BASE_URL = nconf.get('BASE_URL'); -const i18n = shared.i18n; +const { i18n } = shared; // This is the plan.id for paypal subscriptions. You have to set up billing plans via their REST sdk (they don't have // a web interface for billing-plan creation), see ./paypalBillingSetup.js for how. After the billing plan is created // there, get it's plan.id and store it in config.json -_.each(shared.content.subscriptionBlocks, (block) => { +_.each(shared.content.subscriptionBlocks, block => { block.paypalKey = nconf.get(`PAYPAL_BILLING_PLANS_${block.key}`); }); @@ -37,12 +37,12 @@ paypal.configure({ client_secret: nconf.get('PAYPAL_CLIENT_SECRET'), }); -let experienceProfileId = nconf.get('PAYPAL_EXPERIENCE_PROFILE_ID'); +const experienceProfileId = nconf.get('PAYPAL_EXPERIENCE_PROFILE_ID'); // TODO better handling of errors // @TODO: Create constants -let api = {}; +const api = {}; api.constants = { // CURRENCY_CODE: 'USD', @@ -70,7 +70,7 @@ api.paypalBillingAgreementCancel = util.promisify(paypal.billingAgreement.cancel api.ipnVerifyAsync = util.promisify(ipn.verify.bind(ipn)); api.checkout = async function checkout (options = {}) { - let {gift, user} = options; + const { gift, user } = options; let amount = 5.00; let description = 'Habitica Gems'; @@ -99,7 +99,7 @@ api.checkout = async function checkout (options = {}) { } - let createPayment = { + const createPayment = { intent: 'sale', payer: { payment_method: this.constants.PAYMENT_METHOD }, redirect_urls: { @@ -128,16 +128,18 @@ api.checkout = async function checkout (options = {}) { createPayment.experience_profile_id = experienceProfileId; } - let result = await this.paypalPaymentCreate(createPayment); - let link = _.find(result.links, { rel: 'approval_url' }).href; + const result = await this.paypalPaymentCreate(createPayment); + const link = _.find(result.links, { rel: 'approval_url' }).href; return link; }; api.checkoutSuccess = async function checkoutSuccess (options = {}) { - let {user, gift, paymentId, customerId} = options; + const { + user, gift, paymentId, customerId, + } = options; let method = 'buyGems'; - let data = { + const data = { user, customerId, paymentMethod: this.constants.PAYMENT_METHOD, @@ -158,16 +160,16 @@ api.checkoutSuccess = async function checkoutSuccess (options = {}) { }; api.subscribe = async function subscribe (options = {}) { - let {sub, coupon} = options; + const { sub, coupon } = options; if (sub.discount) { if (!coupon) throw new BadRequest(i18n.t('couponCodeRequired')); - let couponResult = await Coupon.findOne({_id: cc.validate(coupon), event: sub.key}).exec(); + const couponResult = await Coupon.findOne({ _id: cc.validate(coupon), event: sub.key }).exec(); if (!couponResult) throw new NotAuthorized(i18n.t('invalidCoupon')); } - let billingPlanTitle = `Habitica Subscription ($${sub.price} every ${sub.months} months, recurring)`; - let billingAgreementAttributes = { + const billingPlanTitle = `Habitica Subscription ($${sub.price} every ${sub.months} months, recurring)`; + const billingAgreementAttributes = { name: billingPlanTitle, description: billingPlanTitle, start_date: moment().add({ minutes: 5 }).format(), @@ -178,15 +180,17 @@ api.subscribe = async function subscribe (options = {}) { payment_method: this.constants.PAYMENT_METHOD, }, }; - let billingAgreement = await this.paypalBillingAgreementCreate(billingAgreementAttributes); + const billingAgreement = await this.paypalBillingAgreementCreate(billingAgreementAttributes); - let link = _.find(billingAgreement.links, { rel: 'approval_url' }).href; + const link = _.find(billingAgreement.links, { rel: 'approval_url' }).href; return link; }; api.subscribeSuccess = async function subscribeSuccess (options = {}) { - let {user, groupId, block, headers, token} = options; - let result = await this.paypalBillingAgreementExecute(token, {}); + const { + user, groupId, block, headers, token, + } = options; + const result = await this.paypalBillingAgreementExecute(token, {}); await payments.createSubscription({ user, groupId, @@ -208,12 +212,14 @@ api.subscribeSuccess = async function subscribeSuccess (options = {}) { * @return undefined */ api.subscribeCancel = async function subscribeCancel (options = {}) { - let {groupId, user, cancellationReason} = options; + const { groupId, user, cancellationReason } = options; let customerId; if (groupId) { - let groupFields = basicGroupFields.concat(' purchased'); - let group = await Group.getGroup({user, groupId, populateLeader: false, groupFields}); + const groupFields = basicGroupFields.concat(' purchased'); + const group = await Group.getGroup({ + user, groupId, populateLeader: false, groupFields, + }); if (!group) { throw new NotFound(i18n.t('groupNotFound')); @@ -229,10 +235,10 @@ api.subscribeCancel = async function subscribeCancel (options = {}) { if (!customerId) throw new NotAuthorized(i18n.t('missingSubscription')); - let customer = await this.paypalBillingAgreementGet(customerId); + const customer = await this.paypalBillingAgreementGet(customerId); // @TODO: Handle error response - let nextBillingDate = customer.agreement_details.next_billing_date; + const nextBillingDate = customer.agreement_details.next_billing_date; if (customer.agreement_details.cycles_completed === '0') { // hasn't billed yet throw new BadRequest(i18n.t('planNotActive', { nextBillingDate })); } @@ -250,9 +256,9 @@ api.subscribeCancel = async function subscribeCancel (options = {}) { api.ipn = async function ipnApi (options = {}) { await this.ipnVerifyAsync(options); - let {txn_type, recurring_payment_id} = options; + const { txn_type, recurring_payment_id } = options; - let ipnAcceptableTypes = [ + const ipnAcceptableTypes = [ 'recurring_payment_profile_cancel', 'recurring_payment_failed', 'recurring_payment_expired', @@ -263,7 +269,7 @@ api.ipn = async function ipnApi (options = {}) { if (ipnAcceptableTypes.indexOf(txn_type) === -1) return; // @TODO: Should this request billing date? - let user = await User.findOne({ 'purchased.plan.customerId': recurring_payment_id }).exec(); + const user = await User.findOne({ 'purchased.plan.customerId': recurring_payment_id }).exec(); if (user) { // If the user has already cancelled the subscription, return // Otherwise the subscription would be cancelled twice resulting in the loss of subscription credits @@ -273,8 +279,8 @@ api.ipn = async function ipnApi (options = {}) { return; } - let groupFields = basicGroupFields.concat(' purchased'); - let group = await Group + const groupFields = basicGroupFields.concat(' purchased'); + const group = await Group .findOne({ 'purchased.plan.customerId': recurring_payment_id }) .select(groupFields) .exec(); diff --git a/website/server/libs/payments/stripe.js b/website/server/libs/payments/stripe.js index c8a2684f7c..d8ae53f7a0 100644 --- a/website/server/libs/payments/stripe.js +++ b/website/server/libs/payments/stripe.js @@ -17,11 +17,11 @@ import stripeConstants from './stripe/constants'; import { checkout } from './stripe/checkout'; import { getStripeApi, setStripeApi } from './stripe/api'; -const i18n = shared.i18n; +const { i18n } = shared; -let api = {}; +const api = {}; -api.constants = Object.assign({}, stripeConstants); +api.constants = { ...stripeConstants }; api.setStripeApi = setStripeApi; @@ -51,7 +51,7 @@ api.checkout = checkout; * @return undefined */ api.editSubscription = async function editSubscription (options, stripeInc) { - let {token, groupId, user} = options; + const { token, groupId, user } = options; let customerId; // @TODO: We need to mock this, but curently we don't have correct Dependency Injection. And the Stripe Api doesn't seem to be a singleton? @@ -59,14 +59,16 @@ api.editSubscription = async function editSubscription (options, stripeInc) { if (stripeInc) stripeApi = stripeInc; if (groupId) { - let groupFields = basicGroupFields.concat(' purchased'); - let group = await Group.getGroup({user, groupId, populateLeader: false, groupFields}); + const groupFields = basicGroupFields.concat(' purchased'); + const group = await Group.getGroup({ + user, groupId, populateLeader: false, groupFields, + }); if (!group) { throw new NotFound(i18n.t('groupNotFound')); } - let allowedManagers = [group.leader, group.purchased.plan.owner]; + const allowedManagers = [group.leader, group.purchased.plan.owner]; if (allowedManagers.indexOf(user._id) === -1) { throw new NotAuthorized(i18n.t('onlyGroupLeaderCanManageSubscription')); @@ -79,8 +81,8 @@ api.editSubscription = async function editSubscription (options, stripeInc) { if (!customerId) throw new NotAuthorized(i18n.t('missingSubscription')); if (!token) throw new BadRequest('Missing req.body.id'); - let subscriptions = await stripeApi.subscriptions.list({customer: customerId}); // @TODO: Handle Stripe Error response - let subscriptionId = subscriptions.data[0].id; + const subscriptions = await stripeApi.subscriptions.list({ customer: customerId }); // @TODO: Handle Stripe Error response + const subscriptionId = subscriptions.data[0].id; await stripeApi.subscriptions.update(subscriptionId, { card: token }); }; @@ -95,7 +97,7 @@ api.editSubscription = async function editSubscription (options, stripeInc) { * @return undefined */ api.cancelSubscription = async function cancelSubscription (options, stripeInc) { - let {groupId, user, cancellationReason} = options; + const { groupId, user, cancellationReason } = options; let customerId; // @TODO: We need to mock this, but curently we don't have correct Dependency Injection. And the Stripe Api doesn't seem to be a singleton? @@ -103,14 +105,16 @@ api.cancelSubscription = async function cancelSubscription (options, stripeInc) if (stripeInc) stripeApi = stripeInc; if (groupId) { - let groupFields = basicGroupFields.concat(' purchased'); - let group = await Group.getGroup({user, groupId, populateLeader: false, groupFields}); + const groupFields = basicGroupFields.concat(' purchased'); + const group = await Group.getGroup({ + user, groupId, populateLeader: false, groupFields, + }); if (!group) { throw new NotFound(i18n.t('groupNotFound')); } - let allowedManagers = [group.leader, group.purchased.plan.owner]; + const allowedManagers = [group.leader, group.purchased.plan.owner]; if (allowedManagers.indexOf(user._id) === -1) { throw new NotAuthorized(i18n.t('onlyGroupLeaderCanManageSubscription')); @@ -123,13 +127,11 @@ api.cancelSubscription = async function cancelSubscription (options, stripeInc) if (!customerId) throw new NotAuthorized(i18n.t('missingSubscription')); // @TODO: Handle error response - let customer = await stripeApi.customers.retrieve(customerId).catch(function errorCatch (err) { - return err; - }); + const customer = await stripeApi.customers.retrieve(customerId).catch(err => err); let nextBill = moment().add(30, 'days').unix() * 1000; if (customer && (customer.subscription || customer.subscriptions)) { - let subscription = customer.subscription; + let { subscription } = customer; if (!subscription && customer.subscriptions) { subscription = customer.subscriptions.data[0]; } @@ -150,15 +152,15 @@ api.cancelSubscription = async function cancelSubscription (options, stripeInc) }; api.chargeForAdditionalGroupMember = async function chargeForAdditionalGroupMember (group) { - let stripeApi = getStripeApi(); - let plan = shared.content.subscriptionBlocks.group_monthly; + const stripeApi = getStripeApi(); + const plan = shared.content.subscriptionBlocks.group_monthly; await stripeApi.subscriptions.update( group.purchased.plan.subscriptionId, { plan: plan.key, quantity: group.memberCount + plan.quantity - 1, - } + }, ); group.purchased.plan.quantity = group.memberCount + plan.quantity - 1; @@ -174,7 +176,7 @@ api.chargeForAdditionalGroupMember = async function chargeForAdditionalGroupMemb * @return undefined */ api.handleWebhooks = async function handleWebhooks (options, stripeInc) { - let {requestBody} = options; + const { requestBody } = options; // @TODO: We need to mock this, but curently we don't have correct Dependency Injection. And the Stripe Api doesn't seem to be a singleton? let stripeApi = getStripeApi(); @@ -197,8 +199,8 @@ api.handleWebhooks = async function handleWebhooks (options, stripeInc) { let groupId; if (isGroupSub) { - let groupFields = basicGroupFields.concat(' purchased'); - let group = await Group.findOne({ + const groupFields = basicGroupFields.concat(' purchased'); + const group = await Group.findOne({ 'purchased.plan.customerId': customerId, 'purchased.plan.paymentMethod': this.constants.PAYMENT_METHOD, }).select(groupFields).exec(); @@ -223,13 +225,13 @@ api.handleWebhooks = async function handleWebhooks (options, stripeInc) { groupId, paymentMethod: this.constants.PAYMENT_METHOD, // Give three extra days to allow the user to resubscribe without losing benefits - nextBill: moment().add({days: 3}).toDate(), + nextBill: moment().add({ days: 3 }).toDate(), }); break; } default: { - logger.error(new Error(`Missing handler for Stripe webhook ${event.type}`), {event}); + logger.error(new Error(`Missing handler for Stripe webhook ${event.type}`), { event }); } } }; diff --git a/website/server/libs/payments/stripe/checkout.js b/website/server/libs/payments/stripe/checkout.js index 36851507aa..3666650643 100644 --- a/website/server/libs/payments/stripe/checkout.js +++ b/website/server/libs/payments/stripe/checkout.js @@ -12,7 +12,7 @@ import { BadRequest, NotAuthorized, } from '../../errors'; -import payments from './../payments'; +import payments from '../payments'; import stripeConstants from './constants'; function getGiftAmount (gift) { @@ -50,11 +50,11 @@ async function buyGems (gift, user, token, stripeApi) { async function buySubscription (sub, coupon, email, user, token, groupId, stripeApi) { if (sub.discount) { if (!coupon) throw new BadRequest(shared.i18n.t('couponCodeRequired')); - coupon = await Coupon.findOne({_id: cc.validate(coupon), event: sub.key}).exec(); + coupon = await Coupon.findOne({ _id: cc.validate(coupon), event: sub.key }).exec(); if (!coupon) throw new BadRequest(shared.i18n.t('invalidCoupon')); } - let customerObject = { + const customerObject = { email, metadata: { uuid: user._id }, card: token, @@ -64,7 +64,9 @@ async function buySubscription (sub, coupon, email, user, token, groupId, stripe if (groupId) { customerObject.quantity = sub.quantity; const groupFields = basicGroupFields.concat(' purchased'); - const group = await Group.getGroup({user, groupId, populateLeader: false, groupFields}); + const group = await Group.getGroup({ + user, groupId, populateLeader: false, groupFields, + }); const membersCount = await group.getMemberCount(); customerObject.quantity = membersCount + sub.quantity - 1; } @@ -95,7 +97,7 @@ async function applyGemPayment (user, response, gift) { } async function checkout (options, stripeInc) { - let { + const { token, user, gift, diff --git a/website/server/libs/payments/subscriptions.js b/website/server/libs/payments/subscriptions.js index 1e606c1a47..e590a44405 100644 --- a/website/server/libs/payments/subscriptions.js +++ b/website/server/libs/payments/subscriptions.js @@ -24,13 +24,13 @@ const JOINED_GROUP_PLAN = 'joined group plan'; function revealMysteryItems (user) { const pushedItems = []; - _.each(shared.content.gear.flat, function findMysteryItems (item) { + _.each(shared.content.gear.flat, item => { if ( - item.klass === 'mystery' && - moment().isAfter(shared.content.mystery[item.mystery].start) && - moment().isBefore(shared.content.mystery[item.mystery].end) && - !user.items.gear.owned[item.key] && - user.purchased.plan.mysteryItems.indexOf(item.key) === -1 + item.klass === 'mystery' + && moment().isAfter(shared.content.mystery[item.mystery].start) + && moment().isBefore(shared.content.mystery[item.mystery].end) + && !user.items.gear.owned[item.key] + && user.purchased.plan.mysteryItems.indexOf(item.key) === -1 ) { user.purchased.plan.mysteryItems.push(item.key); pushedItems.push(item.key); @@ -50,10 +50,10 @@ function _dateDiff (earlyDate, lateDate) { async function createSubscription (data) { let recipient = data.gift ? data.gift.member : data.user; - let block = shared.content.subscriptionBlocks[data.gift ? data.gift.subscription.key : data.sub.key]; - let autoRenews = data.autoRenews !== undefined ? data.autoRenews : true; - let months = Number(block.months); - let today = new Date(); + const block = shared.content.subscriptionBlocks[data.gift ? data.gift.subscription.key : data.sub.key]; + const autoRenews = data.autoRenews !== undefined ? data.autoRenews : true; + const months = Number(block.months); + const today = new Date(); let plan; let group; let groupId; @@ -63,8 +63,10 @@ async function createSubscription (data) { // If we are buying a group subscription if (data.groupId) { - let groupFields = basicGroupFields.concat(' purchased'); - group = await Group.getGroup({user: data.user, groupId: data.groupId, populateLeader: false, groupFields}); + const groupFields = basicGroupFields.concat(' purchased'); + group = await Group.getGroup({ + user: data.user, groupId: data.groupId, populateLeader: false, groupFields, + }); if (!group) { throw new NotFound(shared.i18n.t('groupNotFound')); @@ -92,9 +94,9 @@ async function createSubscription (data) { } else { if (!recipient.isSubscribed() || !plan.dateUpdated) plan.dateUpdated = today; if (moment(plan.dateTerminated).isAfter()) { - plan.dateTerminated = moment(plan.dateTerminated).add({months}).toDate(); + plan.dateTerminated = moment(plan.dateTerminated).add({ months }).toDate(); } else { - plan.dateTerminated = moment().add({months}).toDate(); + plan.dateTerminated = moment().add({ months }).toDate(); plan.dateCreated = today; } } @@ -131,7 +133,7 @@ async function createSubscription (data) { } // Block sub perks - let perks = Math.floor(months / 3); + const perks = Math.floor(months / 3); if (perks) { plan.consecutive.offset += months; plan.consecutive.gemCapExtra += perks * 5; @@ -166,10 +168,10 @@ async function createSubscription (data) { if (!group) data.user.purchased.txnCount++; if (data.gift) { - let byUserName = getUserInfo(data.user, ['name']).name; + const byUserName = getUserInfo(data.user, ['name']).name; // generate the message in both languages, so both users can understand it - let languages = [data.user.preferences.language, data.gift.member.preferences.language]; + const languages = [data.user.preferences.language, data.gift.member.preferences.language]; let senderMsg = shared.i18n.t('giftedSubscriptionFull', { username: data.gift.member.profile.name, sender: byUserName, @@ -193,8 +195,8 @@ async function createSubscription (data) { if (data.gift.member.preferences.emailNotifications.giftedSubscription !== false) { txnEmail(data.gift.member, 'gifted-subscription', [ - {name: 'GIFTER', content: byUserName}, - {name: 'X_MONTHS_SUBSCRIPTION', content: months}, + { name: 'GIFTER', content: byUserName }, + { name: 'X_MONTHS_SUBSCRIPTION', content: months }, ]); } @@ -203,11 +205,10 @@ async function createSubscription (data) { sendPushNotification(data.gift.member, { title: shared.i18n.t('giftedSubscription', languages[1]), - message: shared.i18n.t('giftedSubscriptionInfo', {months, name: byUserName}, languages[1]), + message: shared.i18n.t('giftedSubscriptionInfo', { months, name: byUserName }, languages[1]), identifier: 'giftedSubscription', - payload: {replyTo: data.user._id}, - } - ); + payload: { replyTo: data.user._id }, + }); } } } @@ -240,26 +241,28 @@ async function cancelSubscription (data) { let cancelType = 'unsubscribe'; let groupId; let emailType; - let emailMergeData = []; + const emailMergeData = []; let sendEmail = true; if (data.groupId) { // cancelling a group plan - let groupFields = basicGroupFields.concat(' purchased'); - group = await Group.getGroup({user: data.user, groupId: data.groupId, populateLeader: false, groupFields}); + const groupFields = basicGroupFields.concat(' purchased'); + group = await Group.getGroup({ + user: data.user, groupId: data.groupId, populateLeader: false, groupFields, + }); if (!group) { throw new NotFound(shared.i18n.t('groupNotFound')); } - let allowedManagers = [group.leader, group.purchased.plan.owner]; + const allowedManagers = [group.leader, group.purchased.plan.owner]; if (allowedManagers.indexOf(data.user._id) === -1) { throw new NotAuthorized(shared.i18n.t('onlyGroupLeaderCanManageSubscription')); } plan = group.purchased.plan; emailType = 'group-cancel-subscription'; - emailMergeData.push({name: 'GROUP_NAME', content: group.name}); + emailMergeData.push({ name: 'GROUP_NAME', content: group.name }); await this.cancelGroupUsersSubscription(group); } else { @@ -271,7 +274,7 @@ async function cancelSubscription (data) { if (data.cancellationReason && data.cancellationReason === JOINED_GROUP_PLAN) sendEmail = false; } - let now = moment(); + const now = moment(); let defaultRemainingDays = 30; if (plan.customerId === this.constants.GROUP_PLAN_CUSTOMER_ID) { @@ -279,17 +282,16 @@ async function cancelSubscription (data) { sendEmail = false; // because group-member-cancel email has already been sent } - let remaining = data.nextBill ? moment(data.nextBill).diff(new Date(), 'days', true) : defaultRemainingDays; + const remaining = data.nextBill ? moment(data.nextBill).diff(new Date(), 'days', true) : defaultRemainingDays; if (plan.extraMonths < 0) plan.extraMonths = 0; - let extraDays = Math.ceil(30.5 * plan.extraMonths); - let nowStr = `${now.format('MM')}/${now.format('DD')}/${now.format('YYYY')}`; - let nowStrFormat = 'MM/DD/YYYY'; + const extraDays = Math.ceil(30.5 * plan.extraMonths); + const nowStr = `${now.format('MM')}/${now.format('DD')}/${now.format('YYYY')}`; + const nowStrFormat = 'MM/DD/YYYY'; - plan.dateTerminated = - moment(nowStr, nowStrFormat) - .add({days: remaining}) - .add({days: extraDays}) - .toDate(); + plan.dateTerminated = moment(nowStr, nowStrFormat) + .add({ days: remaining }) + .add({ days: extraDays }) + .toDate(); plan.extraMonths = 0; // clear extra time. If they subscribe again, it'll be recalculated from p.dateTerminated diff --git a/website/server/libs/preening.js b/website/server/libs/preening.js index 34f769815d..41513840e1 100644 --- a/website/server/libs/preening.js +++ b/website/server/libs/preening.js @@ -12,12 +12,10 @@ function _aggregate (history, aggregateBy, timezoneOffset, dayStart) { .toPairs() // [key, entry] .sortBy(([key]) => key) // sort by date .map(keyEntryPair => { - let entries = keyEntryPair[1]; // 1 is entry, 0 is key + const entries = keyEntryPair[1]; // 1 is entry, 0 is key return { date: Number(entries[0].date), - value: _.reduce(entries, (previousValue, entry) => { - return previousValue + entry.value; - }, 0) / entries.length, + value: _.reduce(entries, (previousValue, entry) => previousValue + entry.value, 0) / entries.length, }; }) .value(); @@ -40,7 +38,7 @@ export function preenHistory (history, isSubscribed, timezoneOffset = 0, dayStar const cutOff = now.subtract(isSubscribed ? 365 : 60, 'days').startOf('day'); // Keep uncompressed entries (modifies history and returns removed items) - let newHistory = _.remove(history, entry => { + const newHistory = _.remove(history, entry => { if (!entry) return true; // sometimes entries are `null` const entryDate = moment(entry.date).zone(timezoneOffset); if (entryDate.hour() < dayStart) entryDate.subtract(1, 'day'); @@ -48,8 +46,8 @@ export function preenHistory (history, isSubscribed, timezoneOffset = 0, dayStar }); // Date after which to begin compressing data by year - let monthsCutOff = cutOff.subtract(isSubscribed ? 12 : 10, 'months').startOf('day'); - let aggregateByMonth = _.remove(history, entry => { + const monthsCutOff = cutOff.subtract(isSubscribed ? 12 : 10, 'months').startOf('day'); + const aggregateByMonth = _.remove(history, entry => { if (!entry) return true; // sometimes entries are `null` const entryDate = moment(entry.date).zone(timezoneOffset); if (entryDate.hour() < dayStart) entryDate.subtract(1, 'day'); @@ -64,10 +62,10 @@ export function preenHistory (history, isSubscribed, timezoneOffset = 0, dayStar // Preen history for users and tasks. export function preenUserHistory (user, tasksByType) { - let isSubscribed = user.isSubscribed(); - let timezoneOffset = user.preferences.timezoneOffset; - let dayStart = user.preferences.dayStart; - let minHistoryLength = isSubscribed ? 365 : 60; + const isSubscribed = user.isSubscribed(); + const { timezoneOffset } = user.preferences; + const { dayStart } = user.preferences; + const minHistoryLength = isSubscribed ? 365 : 60; function _processTask (task) { if (task.history && task.history.length > minHistoryLength) { diff --git a/website/server/libs/pushNotifications.js b/website/server/libs/pushNotifications.js index 16d95678d0..5f4eaf30e0 100644 --- a/website/server/libs/pushNotifications.js +++ b/website/server/libs/pushNotifications.js @@ -1,8 +1,8 @@ import _ from 'lodash'; import nconf from 'nconf'; import apn from 'apn'; -import logger from './logger'; import gcmLib from 'node-gcm'; // works with FCM notifications too +import logger from './logger'; const FCM_API_KEY = nconf.get('PUSH_CONFIGS_FCM_SERVER_API_KEY'); @@ -26,13 +26,13 @@ if (APN_ENABLED) { function sendNotification (user, details = {}) { if (!user) throw new Error('User is required.'); if (user.preferences.pushNotifications.unsubscribeFromAll === true) return; - let pushDevices = user.pushDevices.toObject ? user.pushDevices.toObject() : user.pushDevices; + const pushDevices = user.pushDevices.toObject ? user.pushDevices.toObject() : user.pushDevices; if (!details.identifier) throw new Error('details.identifier is required.'); if (!details.title) throw new Error('details.title is required.'); if (!details.message) throw new Error('details.message is required.'); - let payload = details.payload ? details.payload : {}; + const payload = details.payload ? details.payload : {}; payload.identifier = details.identifier; _.each(pushDevices, pushDevice => { @@ -43,21 +43,22 @@ function sendNotification (user, details = {}) { payload.body = details.message; if (fcmSender) { - let message = new gcmLib.Message({ + const message = new gcmLib.Message({ data: payload, }); fcmSender.send(message, { registrationTokens: [pushDevice.regId], - }, 10, (err) => logger.error(err, 'FCM Error')); + }, 10, err => logger.error(err, 'FCM Error')); } break; case 'ios': if (apnProvider) { const notification = new apn.Notification({ - alert: {title: details.title, - body: details.message, + alert: { + title: details.title, + body: details.message, }, sound: 'default', category: details.category, @@ -65,8 +66,8 @@ function sendNotification (user, details = {}) { payload, }); apnProvider.send(notification, pushDevice.regId) - .then((response) => { - response.failed.forEach((failure) => { + .then(response => { + response.failed.forEach(failure => { if (failure.error) { logger.error('APN error', failure.error); } else { diff --git a/website/server/libs/routes.js b/website/server/libs/routes.js index 857a9c4244..817f5a7a71 100644 --- a/website/server/libs/routes.js +++ b/website/server/libs/routes.js @@ -6,27 +6,28 @@ import { // Wrapper function to handler `async` route handlers that return promises // It takes the async function, execute it and pass any error to next (args[2]) -let _wrapAsyncFn = fn => (...args) => fn(...args).catch(args[2]); -let noop = (req, res, next) => next(); +const _wrapAsyncFn = fn => (...args) => fn(...args).catch(args[2]); +const noop = (req, res, next) => next(); export function readController (router, controller, overrides = []) { - _.each(controller, (action) => { - let {method, url, middlewares = [], handler} = action; + _.each(controller, action => { + let { + method, url, middlewares = [], handler, + } = action; // Allow to specify a list of routes (METHOD + URL) to skip if (overrides.indexOf(`${method}-${url}`) !== -1) return; // If an authentication middleware is used run getUserLanguage after it, otherwise before // for cron instead use it only if an authentication middleware is present - let authMiddlewareIndex = _.findIndex(middlewares, middleware => { + const authMiddlewareIndex = _.findIndex(middlewares, middleware => { if (middleware.name.indexOf('authWith') === 0) { // authWith{Headers|Session|Url|...} return true; - } else { - return false; } + return false; }); - let middlewaresToAdd = [getUserLanguage]; + const middlewaresToAdd = [getUserLanguage]; if (action.noLanguage !== true) { if (authMiddlewareIndex !== -1) { // the user will be authenticated, getUserLanguage after authentication @@ -41,7 +42,7 @@ export function readController (router, controller, overrides = []) { } method = method.toLowerCase(); - let fn = handler ? _wrapAsyncFn(handler) : noop; + const fn = handler ? _wrapAsyncFn(handler) : noop; router[method](url, ...middlewares, fn); }); @@ -54,7 +55,7 @@ export function walkControllers (router, filePath, overrides) { if (!fs.statSync(filePath + fileName).isFile()) { walkControllers(router, `${filePath}${fileName}/`, overrides); } else if (fileName.match(/\.js$/)) { - let controller = require(filePath + fileName).default; // eslint-disable-line global-require + const controller = require(filePath + fileName).default; // eslint-disable-line global-require readController(router, controller, overrides); } }); diff --git a/website/server/libs/setupMongoose.js b/website/server/libs/setupMongoose.js index fc8260c298..db7610426e 100644 --- a/website/server/libs/setupMongoose.js +++ b/website/server/libs/setupMongoose.js @@ -1,6 +1,6 @@ import nconf from 'nconf'; -import logger from './logger'; import mongoose from 'mongoose'; +import logger from './logger'; const IS_PROD = nconf.get('IS_PROD'); const MAINTENANCE_MODE = nconf.get('MAINTENANCE_MODE'); @@ -17,7 +17,7 @@ if (MAINTENANCE_MODE !== 'true') { const NODE_DB_URI = nconf.get('IS_TEST') ? nconf.get('TEST_DB_URI') : nconf.get('NODE_DB_URI'); - mongoose.connect(NODE_DB_URI, mongooseOptions, (err) => { + mongoose.connect(NODE_DB_URI, mongooseOptions, err => { if (err) throw err; logger.info('Connected with Mongoose.'); }); diff --git a/website/server/libs/setupNconf.js b/website/server/libs/setupNconf.js index 142ca3d9d9..96eeb75662 100644 --- a/website/server/libs/setupNconf.js +++ b/website/server/libs/setupNconf.js @@ -5,7 +5,7 @@ const { join, resolve } = require('path'); const PATH_TO_CONFIG = join(resolve(__dirname, '../../../config.json')); module.exports = function setupNconf (file) { - let configFile = file || PATH_TO_CONFIG; + const configFile = file || PATH_TO_CONFIG; nconf .argv() diff --git a/website/server/libs/slack.js b/website/server/libs/slack.js index 2ed4c2c6e9..fb1f7b57cc 100644 --- a/website/server/libs/slack.js +++ b/website/server/libs/slack.js @@ -1,9 +1,9 @@ /* eslint-disable camelcase */ import { IncomingWebhook } from '@slack/client'; -import logger from './logger'; -import { TAVERN_ID } from '../models/group'; import nconf from 'nconf'; import moment from 'moment'; +import logger from './logger'; +import { TAVERN_ID } from '../models/group'; const SLACK_FLAGGING_URL = nconf.get('SLACK_FLAGGING_URL'); const SLACK_FLAGGING_FOOTER_LINK = nconf.get('SLACK_FLAGGING_FOOTER_LINK'); @@ -115,17 +115,17 @@ function sendInboxFlagNotification ({ if (SKIP_FLAG_METHODS) { return; } - let titleLink = ''; + const titleLink = ''; let authorName; - let title = `Flag in ${flagger.profile.name}'s Inbox`; + const title = `Flag in ${flagger.profile.name}'s Inbox`; let text = `${flagger.profile.name} (${flagger.id}; language: ${flagger.preferences.language}) flagged a PM`; - let footer = ''; + const footer = ''; if (userComment) { text += ` and commented: ${userComment}`; } - let messageText = message.text; + const messageText = message.text; let sender = ''; let recipient = ''; @@ -180,7 +180,7 @@ function sendSubscriptionNotification ({ return; } let text; - let timestamp = new Date(); + const timestamp = new Date(); if (recipient.id) { text = `${buyer.name} ${buyer.id} ${buyer.email} bought a ${months}-month gift subscription for ${recipient.name} ${recipient.id} ${recipient.email} using ${paymentMethod} on ${timestamp}`; } else if (groupId) { @@ -205,8 +205,8 @@ function sendShadowMutedPostNotification ({ } let titleLink; let authorName; - let title = `Shadow-Muted Post in ${group.name}`; - let text = `@${author.auth.local.username} / ${author.profile.name} posted while shadow-muted`; + const title = `Shadow-Muted Post in ${group.name}`; + const text = `@${author.auth.local.username} / ${author.profile.name} posted while shadow-muted`; if (group.id === TAVERN_ID) { titleLink = `${BASE_URL}/groups/tavern`; @@ -249,7 +249,7 @@ function sendSlurNotification ({ let titleLink; let authorName; let title = `Slur in ${group.name}`; - let text = `${author.profile.name} (${author._id}) tried to post a slur`; + const text = `${author.profile.name} (${author._id}) tried to post a slur`; if (group.id === TAVERN_ID) { titleLink = `${BASE_URL}/groups/tavern`; diff --git a/website/server/libs/sleep.js b/website/server/libs/sleep.js index 2df97b91ff..8bf0562aa7 100644 --- a/website/server/libs/sleep.js +++ b/website/server/libs/sleep.js @@ -1,7 +1,7 @@ export default async function (seconds = 1) { - let milliseconds = seconds * 1000; + const milliseconds = seconds * 1000; - return new Promise((resolve) => { + return new Promise(resolve => { setTimeout(resolve, milliseconds); }); -} \ No newline at end of file +} diff --git a/website/server/libs/spells.js b/website/server/libs/spells.js index 8c46fcb1ab..d8cb4938d5 100644 --- a/website/server/libs/spells.js +++ b/website/server/libs/spells.js @@ -9,7 +9,7 @@ import common from '../../common'; import { model as Group, } from '../models/group'; -import apiError from '../libs/apiError'; +import apiError from './apiError'; const partyMembersFields = 'profile.name stats achievements items.special notifications flags'; // Excluding notifications and flags from the list of public fields to return. @@ -101,12 +101,12 @@ async function castUserSpell (res, req, party, partyMembers, targetId, user, spe if (!targetId) throw new BadRequest(res.t('targetIdUUID')); if (!party) throw new NotFound(res.t('partyNotFound')); partyMembers = await User - .findOne({_id: targetId, 'party._id': party._id}) + .findOne({ _id: targetId, 'party._id': party._id }) .select(partyMembersFields) .exec(); } - if (!partyMembers) throw new NotFound(res.t('userWithIDNotFound', {userId: targetId})); + if (!partyMembers) throw new NotFound(res.t('userWithIDNotFound', { userId: targetId })); for (let i = 0; i < quantity; i += 1) { spell.cast(user, partyMembers, req); @@ -124,27 +124,27 @@ async function castUserSpell (res, req, party, partyMembers, targetId, user, spe return partyMembers; } -async function castSpell (req, res, {isV3 = false}) { - const user = res.locals.user; - const spellId = req.params.spellId; - const targetId = req.query.targetId; +async function castSpell (req, res, { isV3 = false }) { + const { user } = res.locals; + const { spellId } = req.params; + const { targetId } = req.query; const quantity = req.body.quantity || 1; // optional because not required by all targetTypes, presence is checked later if necessary req.checkQuery('targetId', res.t('targetIdUUID')).optional().isUUID(); - let reqValidationErrors = req.validationErrors(); + const reqValidationErrors = req.validationErrors(); if (reqValidationErrors) throw reqValidationErrors; - let klass = common.content.spells.special[spellId] ? 'special' : user.stats.class; - let spell = common.content.spells[klass][spellId]; + const klass = common.content.spells.special[spellId] ? 'special' : user.stats.class; + const spell = common.content.spells[klass][spellId]; - if (!spell) throw new NotFound(apiError('spellNotFound', {spellId})); + if (!spell) throw new NotFound(apiError('spellNotFound', { spellId })); if (spell.mana > user.stats.mp) throw new NotAuthorized(res.t('notEnoughMana')); if (spell.value > user.stats.gp && !spell.previousPurchase) throw new NotAuthorized(res.t('messageNotEnoughGold')); - if (spell.lvl > user.stats.lvl) throw new NotAuthorized(res.t('spellLevelTooHigh', {level: spell.lvl})); + if (spell.lvl > user.stats.lvl) throw new NotAuthorized(res.t('spellLevelTooHigh', { level: spell.lvl })); - let targetType = spell.target; + const targetType = spell.target; if (targetType === 'task') { const results = await castTaskSpell(res, req, targetId, user, spell, quantity); @@ -170,7 +170,7 @@ async function castSpell (req, res, {isV3 = false}) { if (isV3) response.user = await response.user.toJSONWithInbox(); res.respond(200, response); } else if (targetType === 'party' || targetType === 'user') { - const party = await Group.getGroup({groupId: 'party', user}); + const party = await Group.getGroup({ groupId: 'party', user }); // arrays of users when targetType is 'party' otherwise single users let partyMembers; @@ -184,9 +184,7 @@ async function castSpell (req, res, {isV3 = false}) { // Only return some fields. // We can't just return the selected fields because they're private - partyMembersRes = partyMembersRes.map(partyMember => { - return common.pickDeep(partyMember.toJSON(), common.$w(partyMembersPublicFields)); - }); + partyMembersRes = partyMembersRes.map(partyMember => common.pickDeep(partyMember.toJSON(), common.$w(partyMembersPublicFields))); let userToJson = user; if (isV3) userToJson = await userToJson.toJSONWithInbox(); @@ -199,7 +197,7 @@ async function castSpell (req, res, {isV3 = false}) { if (party && !spell.silent) { if (targetType === 'user') { const newChatMessage = party.sendChat({ - message: `\`${common.i18n.t('chatCastSpellUser', {username: user.profile.name, spell: spell.text(), target: partyMembers.profile.name}, 'en')}\``, + message: `\`${common.i18n.t('chatCastSpellUser', { username: user.profile.name, spell: spell.text(), target: partyMembers.profile.name }, 'en')}\``, info: { type: 'spell_cast_user', user: user.profile.name, @@ -211,7 +209,7 @@ async function castSpell (req, res, {isV3 = false}) { await newChatMessage.save(); } else { const newChatMessage = party.sendChat({ - message: `\`${common.i18n.t('chatCastSpellParty', {username: user.profile.name, spell: spell.text()}, 'en')}\``, + message: `\`${common.i18n.t('chatCastSpellParty', { username: user.profile.name, spell: spell.text() }, 'en')}\``, info: { type: 'spell_cast_party', user: user.profile.name, diff --git a/website/server/libs/stringUtils.js b/website/server/libs/stringUtils.js index f10deb99f0..19d9c3213c 100644 --- a/website/server/libs/stringUtils.js +++ b/website/server/libs/stringUtils.js @@ -4,13 +4,13 @@ export function removePunctuationFromString (str) { } export function getMatchesByWordArray (str, wordsToMatch) { - let matchedWords = []; - let wordRegexs = wordsToMatch.map((word) => new RegExp(`\\b([^a-z]+)?${word}([^a-z]+)?\\b`, 'i')); + const matchedWords = []; + const wordRegexs = wordsToMatch.map(word => new RegExp(`\\b([^a-z]+)?${word}([^a-z]+)?\\b`, 'i')); for (let i = 0; i < wordRegexs.length; i += 1) { - let regEx = wordRegexs[i]; - let match = str.match(regEx); + const regEx = wordRegexs[i]; + const match = str.match(regEx); if (match !== null && match[0] !== null) { - let trimmedMatch = removePunctuationFromString(match[0]).trim(); + const trimmedMatch = removePunctuationFromString(match[0]).trim(); matchedWords.push(trimmedMatch); } } diff --git a/website/server/libs/taskManager.js b/website/server/libs/taskManager.js index 6cc246f531..af125ea70c 100644 --- a/website/server/libs/taskManager.js +++ b/website/server/libs/taskManager.js @@ -1,4 +1,5 @@ import moment from 'moment'; +import _ from 'lodash'; import * as Tasks from '../models/task'; import { BadRequest, @@ -6,12 +7,11 @@ import { import { SHARED_COMPLETION, } from './groupTasks'; -import _ from 'lodash'; import shared from '../../common'; async function _validateTaskAlias (tasks, res) { - let tasksWithAliases = tasks.filter(task => task.alias); - let aliases = tasksWithAliases.map(task => task.alias); + const tasksWithAliases = tasks.filter(task => task.alias); + const aliases = tasksWithAliases.map(task => task.alias); // Compares the short names in tasks against // a Set, where values cannot repeat. If the @@ -20,9 +20,7 @@ async function _validateTaskAlias (tasks, res) { throw new BadRequest(res.t('taskAliasAlreadyUsed')); } - await Promise.all(tasksWithAliases.map((task) => { - return task.validate(); - })); + await Promise.all(tasksWithAliases.map(task => task.validate())); } export function setNextDue (task, user, dueDateOption) { @@ -44,16 +42,14 @@ export function setNextDue (task, user, dueDateOption) { } - let optionsForShouldDo = user.preferences.toObject(); + const optionsForShouldDo = user.preferences.toObject(); optionsForShouldDo.now = now; task.isDue = shared.shouldDo(dateTaskIsDue, task, optionsForShouldDo); optionsForShouldDo.nextDue = true; - let nextDue = shared.shouldDo(dateTaskIsDue, task, optionsForShouldDo); + const nextDue = shared.shouldDo(dateTaskIsDue, task, optionsForShouldDo); if (nextDue && nextDue.length > 0) { - task.nextDue = nextDue.map((dueDate) => { - return dueDate.toISOString(); - }); + task.nextDue = nextDue.map(dueDate => dueDate.toISOString()); } } @@ -70,27 +66,27 @@ export function setNextDue (task, user, dueDateOption) { * @return The created tasks */ export async function createTasks (req, res, options = {}) { - let { + const { user, challenge, group, } = options; - let owner = group || challenge || user; + const owner = group || challenge || user; let toSave = Array.isArray(req.body) ? req.body : [req.body]; // Return if no tasks are passed, avoids errors with mongo $push being empty if (toSave.length === 0) return []; - let taskOrderToAdd = {}; + const taskOrderToAdd = {}; toSave = toSave.map(taskData => { // Validate that task.type is valid if (!taskData || Tasks.tasksTypes.indexOf(taskData.type) === -1) throw new BadRequest(res.t('invalidTaskType')); - let taskType = taskData.type; - let newTask = new Tasks[taskType](Tasks.Task.sanitize(taskData)); + const taskType = taskData.type; + const newTask = new Tasks[taskType](Tasks.Task.sanitize(taskData)); if (challenge) { newTask.challenge.id = challenge.id; @@ -108,7 +104,7 @@ export async function createTasks (req, res, options = {}) { // Validate that the task is valid and throw if it isn't // otherwise since we're saving user/challenge/group and task in parallel it could save the user/challenge/group with a tasksOrder that doens't match reality - let validationErrors = newTask.validateSync(); + const validationErrors = newTask.validateSync(); if (validationErrors) throw validationErrors; // Otherwise update the user/challenge/group @@ -119,8 +115,8 @@ export async function createTasks (req, res, options = {}) { }); // Push all task ids - let taskOrderUpdateQuery = {$push: {}}; - for (let taskType in taskOrderToAdd) { + const taskOrderUpdateQuery = { $push: {} }; + for (const taskType in taskOrderToAdd) { taskOrderUpdateQuery.$push[`tasksOrder.${taskType}`] = { $each: taskOrderToAdd[taskType], $position: 0, @@ -138,7 +134,7 @@ export async function createTasks (req, res, options = {}) { toSave.unshift(owner.save()); - let tasks = await Promise.all(toSave); + const tasks = await Promise.all(toSave); tasks.splice(0, 1); // Remove user, challenge, or group promise return tasks; } @@ -156,25 +152,25 @@ export async function createTasks (req, res, options = {}) { * @return The tasks found */ export async function getTasks (req, res, options = {}) { - let { + const { user, challenge, group, dueDate, } = options; - let query = {userId: user._id}; + let query = { userId: user._id }; let limit; let sort; - let owner = group || challenge || user; + const owner = group || challenge || user; if (challenge) { - query = {'challenge.id': challenge.id, userId: {$exists: false}}; + query = { 'challenge.id': challenge.id, userId: { $exists: false } }; } else if (group) { - query = {'group.id': group._id, userId: {$exists: false}}; + query = { 'group.id': group._id, userId: { $exists: false } }; } - let type = req.query.type; + const { type } = req.query; if (type) { if (type === 'todos') { @@ -202,32 +198,32 @@ export async function getTasks (req, res, options = {}) { } } else { query.$or = [ // Exclude completed todos - {type: 'todo', completed: false}, - {type: {$in: ['habit', 'daily', 'reward']}}, + { type: 'todo', completed: false }, + { type: { $in: ['habit', 'daily', 'reward'] } }, ]; } - let mQuery = Tasks.Task.find(query); + const mQuery = Tasks.Task.find(query); if (limit) mQuery.limit(limit); if (sort) mQuery.sort(sort); - let tasks = await mQuery.exec(); + const tasks = await mQuery.exec(); if (dueDate) { - tasks.forEach((task) => { + tasks.forEach(task => { setNextDue(task, user, dueDate); }); } // Order tasks based on tasksOrder if (type && type !== 'completedTodos' && type !== '_allCompletedTodos') { - let order = owner.tasksOrder[type]; + const order = owner.tasksOrder[type]; let orderedTasks = new Array(tasks.length); - let unorderedTasks = []; // what we want to add later + const unorderedTasks = []; // what we want to add later tasks.forEach((task, index) => { - let taskId = task._id; - let i = order[index] === taskId ? index : order.indexOf(taskId); + const taskId = task._id; + const i = order[index] === taskId ? index : order.indexOf(taskId); if (i === -1) { unorderedTasks.push(task); } else { @@ -238,17 +234,16 @@ export async function getTasks (req, res, options = {}) { // Remove empty values from the array and add any unordered task orderedTasks = _.compact(orderedTasks).concat(unorderedTasks); return orderedTasks; - } else { - return tasks; } + return tasks; } // Takes a Task document and return a plain object of attributes that can be synced to the user export function syncableAttrs (task) { - let t = task.toObject(); // lodash doesn't seem to like _.omit on Document + const t = task.toObject(); // lodash doesn't seem to like _.omit on Document // only sync/compare important attrs - let omitAttrs = ['_id', 'userId', 'challenge', 'history', 'tags', 'completed', 'streak', 'notes', 'updatedAt', 'createdAt', 'group', 'checklist', 'attribute']; + const omitAttrs = ['_id', 'userId', 'challenge', 'history', 'tags', 'completed', 'streak', 'notes', 'updatedAt', 'createdAt', 'group', 'checklist', 'attribute']; if (t.type !== 'reward') omitAttrs.push('value'); return _.omit(t, omitAttrs); } @@ -263,7 +258,7 @@ export function syncableAttrs (task) { * @return Empty */ export function moveTask (order, taskId, to) { - let currentIndex = order.indexOf(taskId); + const currentIndex = order.indexOf(taskId); // If for some reason the task isn't ordered (should never happen), push it in the new position // if the task is moved to a non existing position diff --git a/website/server/libs/user/index.js b/website/server/libs/user/index.js index a96504ff57..bf1a185a2d 100644 --- a/website/server/libs/user/index.js +++ b/website/server/libs/user/index.js @@ -1,16 +1,16 @@ +import _ from 'lodash'; import common from '../../../common'; import * as Tasks from '../../models/task'; -import _ from 'lodash'; import { BadRequest, NotAuthorized, -} from '../../libs/errors'; +} from '../errors'; import { model as User, schema as UserSchema } from '../../models/user'; -import {nameContainsSlur} from './validation'; +import { nameContainsSlur } from './validation'; export async function get (req, res, { isV3 = false }) { - const user = res.locals.user; + const { user } = res.locals; let userToJSON; if (isV3) { @@ -23,7 +23,7 @@ export async function get (req, res, { isV3 = false }) { delete userToJSON.apiToken; if (!req.query.userFields) { - let {daysMissed} = user.daysUserHasMissed(new Date(), req); + const { daysMissed } = user.daysUserHasMissed(new Date(), req); userToJSON.needsCron = false; if (daysMissed > 0) userToJSON.needsCron = true; User.addComputedStatsToJSONObj(userToJSON.stats, userToJSON); @@ -61,10 +61,8 @@ const updatablePaths = [ // This tells us for which paths users can call `PUT /user`. // The trick here is to only accept leaf paths, not root/intermediate paths (see http://goo.gl/OEzkAs) -let acceptablePUTPaths = _.reduce(UserSchema.paths, (accumulator, val, leaf) => { - let found = _.find(updatablePaths, (rootPath) => { - return leaf.indexOf(rootPath) === 0; - }); +const acceptablePUTPaths = _.reduce(UserSchema.paths, (accumulator, val, leaf) => { + const found = _.find(updatablePaths, rootPath => leaf.indexOf(rootPath) === 0); if (found) accumulator[leaf] = true; @@ -79,7 +77,7 @@ const restrictedPUTSubPaths = [ 'preferences.webhooks', ]; -_.each(restrictedPUTSubPaths, (removePath) => { +_.each(restrictedPUTSubPaths, removePath => { delete acceptablePUTPaths[removePath]; }); @@ -97,8 +95,8 @@ const requiresPurchase = { }; function checkPreferencePurchase (user, path, item) { - let itemPath = `${path}.${item}`; - let appearance = _.get(common.content.appearances, itemPath); + const itemPath = `${path}.${item}`; + const appearance = _.get(common.content.appearances, itemPath); if (!appearance) return false; if (appearance.price === 0) return true; @@ -106,7 +104,7 @@ function checkPreferencePurchase (user, path, item) { } export async function update (req, res, { isV3 = false }) { - const user = res.locals.user; + const { user } = res.locals; let promisesForTagsRemoval = []; @@ -118,7 +116,7 @@ export async function update (req, res, { isV3 = false }) { } _.each(req.body, (val, key) => { - let purchasable = requiresPurchase[key]; + const purchasable = requiresPurchase[key]; if (purchasable && !checkPreferencePurchase(user, purchasable, val)) { throw new NotAuthorized(res.t('mustPurchaseToSet', { val, key })); @@ -145,7 +143,7 @@ export async function update (req, res, { isV3 = false }) { user.tags = oldTags; val.forEach(t => { - let oldI = removedTagsIds.findIndex(id => id === t.id); + const oldI = removedTagsIds.findIndex(id => id === t.id); if (oldI > -1) { removedTagsIds.splice(oldI, 1); } @@ -156,15 +154,13 @@ export async function update (req, res, { isV3 = false }) { // Remove from all the tasks // NOTE each tag to remove requires a query - promisesForTagsRemoval = removedTagsIds.map(tagId => { - return Tasks.Task.update({ - userId: user._id, - }, { - $pull: { - tags: tagId, - }, - }, {multi: true}).exec(); - }); + promisesForTagsRemoval = removedTagsIds.map(tagId => Tasks.Task.update({ + userId: user._id, + }, { + $pull: { + tags: tagId, + }, + }, { multi: true }).exec()); } else { throw new NotAuthorized(res.t('messageUserOperationProtected', { operation: key })); } @@ -181,7 +177,7 @@ export async function update (req, res, { isV3 = false }) { } export async function reset (req, res, { isV3 = false }) { - const user = res.locals.user; + const { user } = res.locals; const tasks = await Tasks.Task.find({ userId: user._id, @@ -194,7 +190,7 @@ export async function reset (req, res, { isV3 = false }) { } await Promise.all([ - Tasks.Task.remove({_id: {$in: resetRes[0].tasksToRemove}, userId: user._id}), + Tasks.Task.remove({ _id: { $in: resetRes[0].tasksToRemove }, userId: user._id }), user.save(), ]); @@ -208,19 +204,19 @@ export async function reset (req, res, { isV3 = false }) { } export async function reroll (req, res, { isV3 = false }) { - let user = res.locals.user; - let query = { + const { user } = res.locals; + const query = { userId: user._id, - type: {$in: ['daily', 'habit', 'todo']}, + type: { $in: ['daily', 'habit', 'todo'] }, ...Tasks.taskIsGroupOrChallengeQuery, }; - let tasks = await Tasks.Task.find(query).exec(); + const tasks = await Tasks.Task.find(query).exec(); const rerollRes = common.ops.reroll(user, tasks, req, res.analytics); if (isV3) { rerollRes[0].user = await rerollRes[0].user.toJSONWithInbox(); } - let promises = tasks.map(task => task.save()); + const promises = tasks.map(task => task.save()); promises.push(user.save()); await Promise.all(promises); @@ -229,10 +225,10 @@ export async function reroll (req, res, { isV3 = false }) { } export async function rebirth (req, res, { isV3 = false }) { - const user = res.locals.user; + const { user } = res.locals; const tasks = await Tasks.Task.find({ userId: user._id, - type: {$in: ['daily', 'habit', 'todo']}, + type: { $in: ['daily', 'habit', 'todo'] }, ...Tasks.taskIsGroupOrChallengeQuery, }).exec(); diff --git a/website/server/libs/user/validation.js b/website/server/libs/user/validation.js index 097ef70cdf..cbdc6487d0 100644 --- a/website/server/libs/user/validation.js +++ b/website/server/libs/user/validation.js @@ -1,8 +1,8 @@ import bannedSlurs from '../bannedSlurs'; -import {getMatchesByWordArray} from '../stringUtils'; +import { getMatchesByWordArray } from '../stringUtils'; import forbiddenUsernames from '../forbiddenUsernames'; -const bannedSlurRegexs = bannedSlurs.map((word) => new RegExp(`.*${word}.*`, 'i')); +const bannedSlurRegexs = bannedSlurs.map(word => new RegExp(`.*${word}.*`, 'i')); export function nameContainsSlur (username) { for (let i = 0; i < bannedSlurRegexs.length; i += 1) { @@ -22,12 +22,12 @@ function usernameIsForbidden (username) { const invalidCharsRegex = new RegExp('[^a-z0-9_-]', 'i'); function usernameContainsInvalidCharacters (username) { - let match = username.match(invalidCharsRegex); + const match = username.match(invalidCharsRegex); return match !== null && match[0] !== null; } export function verifyDisplayName (displayName, res) { - let issues = []; + const issues = []; if (displayName.length < 1 || displayName.length > 30) issues.push(res.t('displaynameIssueLength')); if (nameContainsSlur(displayName)) issues.push(res.t('displaynameIssueSlur')); @@ -35,7 +35,7 @@ export function verifyDisplayName (displayName, res) { } export function verifyUsername (username, res) { - let issues = []; + const issues = []; if (username.length < 1 || username.length > 20) issues.push(res.t('usernameIssueLength')); if (usernameContainsInvalidCharacters(username)) issues.push(res.t('usernameIssueInvalidCharacters')); if (nameContainsSlur(username)) issues.push(res.t('usernameIssueSlur')); diff --git a/website/server/libs/webhook.js b/website/server/libs/webhook.js index 7956953bc4..7499a3ad4e 100644 --- a/website/server/libs/webhook.js +++ b/website/server/libs/webhook.js @@ -1,7 +1,7 @@ import got from 'got'; import { isURL } from 'validator'; -import logger from './logger'; import nconf from 'nconf'; +import logger from './logger'; import { model as User, } from '../models/user'; @@ -17,7 +17,7 @@ function sendWebhook (url, body) { function isValidWebhook (hook) { return hook.enabled && isURL(hook.url, { - require_tld: IS_PRODUCTION ? true : false, // eslint-disable-line camelcase + require_tld: !!IS_PRODUCTION, // eslint-disable-line camelcase }); } @@ -43,9 +43,9 @@ export class WebhookSender { } send (user, data) { - const webhooks = user.webhooks; + const { webhooks } = user; - let hooks = webhooks.filter((hook) => { + const hooks = webhooks.filter(hook => { if (!isValidWebhook(hook)) return false; if (hook.type === 'globalActivity') return true; @@ -56,34 +56,36 @@ export class WebhookSender { return; // prevents running the body creation code if there are no webhooks to send } - let body = this.transformData(data); + const body = this.transformData(data); this.attachDefaultData(user, body); - hooks.forEach((hook) => { + hooks.forEach(hook => { sendWebhook(hook.url, body); }); } } -export let taskScoredWebhook = new WebhookSender({ +export const taskScoredWebhook = new WebhookSender({ type: 'taskActivity', webhookFilter (hook) { - let scored = hook.options && hook.options.scored; + const scored = hook.options && hook.options.scored; return scored; }, transformData (data) { - let { user, task, direction, delta } = data; + const { + user, task, direction, delta, + } = data; - let extendedStats = User.addComputedStatsToJSONObj(user.stats.toJSON(), user); + const extendedStats = User.addComputedStatsToJSONObj(user.stats.toJSON(), user); - let userData = { + const userData = { // _id: user._id, added automatically when the webhook is sent _tmp: user._tmp, stats: extendedStats, }; - let dataToSend = { + const dataToSend = { type: 'scored', direction, delta, @@ -95,32 +97,32 @@ export let taskScoredWebhook = new WebhookSender({ }, }); -export let taskActivityWebhook = new WebhookSender({ +export const taskActivityWebhook = new WebhookSender({ type: 'taskActivity', webhookFilter (hook, data) { - let { type } = data; + const { type } = data; return hook.options[type]; }, }); -export let userActivityWebhook = new WebhookSender({ +export const userActivityWebhook = new WebhookSender({ type: 'userActivity', webhookFilter (hook, data) { - let { type } = data; + const { type } = data; return hook.options[type]; }, }); -export let questActivityWebhook = new WebhookSender({ +export const questActivityWebhook = new WebhookSender({ type: 'questActivity', webhookFilter (hook, data) { - let { type } = data; + const { type } = data; return hook.options[type]; }, transformData (data) { - let { group, quest, type } = data; + const { group, quest, type } = data; - let dataToSend = { + const dataToSend = { type, group: { id: group.id, @@ -135,15 +137,15 @@ export let questActivityWebhook = new WebhookSender({ }, }); -export let groupChatReceivedWebhook = new WebhookSender({ +export const groupChatReceivedWebhook = new WebhookSender({ type: 'groupChatReceived', webhookFilter (hook, data) { return hook.options.groupId === data.group.id; }, transformData (data) { - let { group, chat } = data; + const { group, chat } = data; - let dataToSend = { + const dataToSend = { group: { id: group.id, name: group.name, diff --git a/website/server/middlewares/appRoutes.js b/website/server/middlewares/appRoutes.js index 9e63ca9641..179ed0082d 100644 --- a/website/server/middlewares/appRoutes.js +++ b/website/server/middlewares/appRoutes.js @@ -1,9 +1,9 @@ import express from 'express'; import expressValidator from 'express-validator'; +import path from 'path'; import analytics from './analytics'; import setupBody from './setupBody'; import * as routes from '../libs/routes'; -import path from 'path'; const API_V3_CONTROLLERS_PATH = path.join(__dirname, '/../controllers/api-v3/'); const API_V4_CONTROLLERS_PATH = path.join(__dirname, '/../controllers/api-v4/'); diff --git a/website/server/middlewares/auth.js b/website/server/middlewares/auth.js index 402cb93c27..5299e1b440 100644 --- a/website/server/middlewares/auth.js +++ b/website/server/middlewares/auth.js @@ -1,11 +1,11 @@ +import nconf from 'nconf'; +import url from 'url'; import { NotAuthorized, } from '../libs/errors'; import { model as User, } from '../models/user'; -import nconf from 'nconf'; -import url from 'url'; import gcpStackdriverTracer from '../libs/gcpTraceAgent'; const COMMUNITY_MANAGER_EMAIL = nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL'); @@ -16,12 +16,9 @@ function getUserFields (options, req) { // Must be an array if (options.userFieldsToExclude) { return options.userFieldsToExclude - .filter(field => { - return !USER_FIELDS_ALWAYS_LOADED.find(fieldToInclude => field.startsWith(fieldToInclude)); - }) - .map(field => { - return `-${field}`; // -${field} means exclude ${field} in mongodb - }) + .filter(field => !USER_FIELDS_ALWAYS_LOADED.find(fieldToInclude => field.startsWith(fieldToInclude))) + .map(field => `-${field}`, // -${field} means exclude ${field} in mongodb + ) .join(' '); } @@ -31,7 +28,7 @@ function getUserFields (options, req) { // Allows GET requests to /user to specify a list of user fields to return instead of the entire doc const urlPath = url.parse(req.url).pathname; - const userFields = req.query.userFields; + const { userFields } = req.query; if (!userFields || urlPath !== '/user') return ''; const userFieldOptions = userFields.split(','); @@ -72,9 +69,9 @@ export function authWithHeaders (options = {}) { return findPromise .exec() - .then((user) => { + .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) throw new NotAuthorized(res.t('accountSuspended', { communityManagerEmail: COMMUNITY_MANAGER_EMAIL, userId: user._id })); res.locals.user = user; req.session.userId = user._id; @@ -88,22 +85,21 @@ export function authWithHeaders (options = {}) { // Authenticate a request through a valid session export function authWithSession (req, res, next) { - let userId = req.session.userId; + const { userId } = req.session; // Always allow authentication with headers if (!userId) { if (!req.header('x-api-user') || !req.header('x-api-key')) { return next(new NotAuthorized(res.t('invalidCredentials'))); - } else { - return authWithHeaders()(req, res, next); } + return authWithHeaders()(req, res, next); } return User.findOne({ _id: userId, }) .exec() - .then((user) => { + .then(user => { if (!user) throw new NotAuthorized(res.t('invalidCredentials')); res.locals.user = user; diff --git a/website/server/middlewares/cron.js b/website/server/middlewares/cron.js index 1c85af0464..3fdddb9fed 100644 --- a/website/server/middlewares/cron.js +++ b/website/server/middlewares/cron.js @@ -9,16 +9,16 @@ const CRON_TIMEOUT_WAIT = new Date(60 * 60 * 1000).getTime(); async function checkForActiveCron (user, now) { // set _cronSignature to current time in ms since epoch time so we can make sure to wait at least CRONT_TIMEOUT_WAIT before attempting another cron - let _cronSignature = now.getTime(); + const _cronSignature = now.getTime(); // Calculate how long ago cron must have been attempted to try again - let cronRetryTime = _cronSignature - CRON_TIMEOUT_WAIT; + const cronRetryTime = _cronSignature - CRON_TIMEOUT_WAIT; // To avoid double cron we first set _cronSignature and then check that it's not changed while processing - let userUpdateResult = await User.update({ + const userUpdateResult = await User.update({ _id: user._id, $or: [ // Make sure last cron was successful or failed before cronRetryTime - {_cronSignature: 'NOT_RUNNING'}, - {_cronSignature: {$lt: cronRetryTime}}, + { _cronSignature: 'NOT_RUNNING' }, + { _cronSignature: { $lt: cronRetryTime } }, ], }, { $set: { @@ -51,17 +51,17 @@ async function unlockUser (user) { } async function cronAsync (req, res) { - let user = res.locals.user; + let { user } = res.locals; if (!user) return null; // User might not be available when authentication is not mandatory - let analytics = res.analytics; - let now = new Date(); + const { analytics } = res; + const now = new Date(); try { await checkForActiveCron(user, now); - user = res.locals.user = await User.findOne({_id: user._id}).exec(); - let {daysMissed, timezoneOffsetFromUserPrefs} = user.daysUserHasMissed(now, req); + user = res.locals.user = await User.findOne({ _id: user._id }).exec(); + const { daysMissed, timezoneOffsetFromUserPrefs } = user.daysUserHasMissed(now, req); await updateLastCron(user, now); @@ -71,19 +71,23 @@ async function cronAsync (req, res) { return null; } - let tasks = await Tasks.Task.find({ + const tasks = await Tasks.Task.find({ userId: user._id, $or: [ // Exclude completed todos - {type: 'todo', completed: false}, - {type: {$in: ['habit', 'daily', 'reward']}}, + { type: 'todo', completed: false }, + { type: { $in: ['habit', 'daily', 'reward'] } }, ], }).exec(); - let tasksByType = {habits: [], dailys: [], todos: [], rewards: []}; + const tasksByType = { + habits: [], dailys: [], todos: [], rewards: [], + }; tasks.forEach(task => tasksByType[`${task.type}s`].push(task)); // Run cron - let progress = cron({user, tasksByType, now, daysMissed, analytics, timezoneOffsetFromUserPrefs, headers: req.headers}); + const progress = cron({ + user, tasksByType, now, daysMissed, analytics, timezoneOffsetFromUserPrefs, headers: req.headers, + }); // Clear old completed todos - 30 days for free users, 90 for subscribers // Do not delete challenges completed todos TODO unless the task is broken? @@ -95,8 +99,8 @@ async function cronAsync (req, res) { dateCompleted: { $lt: moment(now).subtract(user.isSubscribed() ? 90 : 30, 'days').toDate(), }, - 'challenge.id': {$exists: false}, - 'group.id': {$exists: false}, + 'challenge.id': { $exists: false }, + 'group.id': { $exists: false }, }).exec(); res.locals.wasModified = true; // TODO remove after v2 is retired @@ -104,7 +108,7 @@ async function cronAsync (req, res) { Group.tavernBoss(user, progress); // Save user and tasks - let toSave = [user.save()]; + const toSave = [user.save()]; tasks.forEach(async task => { if (task.isModified()) toSave.push(task.save()); if (task.isModified() && task.group && task.group.taskId) { @@ -133,7 +137,7 @@ async function cronAsync (req, res) { }).exec(); // Reload user - res.locals.user = await User.findOne({_id: user._id}).exec(); + res.locals.user = await User.findOne({ _id: user._id }).exec(); return null; } catch (err) { // If cron was aborted for a race condition try to recover from it @@ -141,7 +145,7 @@ async function cronAsync (req, res) { // Recovering after abort, wait 300ms and reload user // do it for max 5 times then reset _cronSignature so that it doesn't prevent cron from running // at the next request - let recoveryStatus = { + const recoveryStatus = { times: 0, }; diff --git a/website/server/middlewares/ensureAccessRight.js b/website/server/middlewares/ensureAccessRight.js index 4e38f04c5b..b4dc41f9a4 100644 --- a/website/server/middlewares/ensureAccessRight.js +++ b/website/server/middlewares/ensureAccessRight.js @@ -4,7 +4,7 @@ import { import apiError from '../libs/apiError'; export function ensureAdmin (req, res, next) { - let user = res.locals.user; + const { user } = res.locals; if (!user.contributor.admin) { return next(new NotAuthorized(res.t('noAdminAccess'))); @@ -14,7 +14,7 @@ export function ensureAdmin (req, res, next) { } export function ensureSudo (req, res, next) { - let user = res.locals.user; + const { user } = res.locals; if (!user.contributor.sudo) { return next(new NotAuthorized(apiError('noSudoAccess'))); diff --git a/website/server/middlewares/errorHandler.js b/website/server/middlewares/errorHandler.js index 2b631dfc03..4fddc09dd0 100644 --- a/website/server/middlewares/errorHandler.js +++ b/website/server/middlewares/errorHandler.js @@ -1,15 +1,15 @@ // The error handler middleware that handles all errors // and respond to the client +import { + map, + omit, +} from 'lodash'; import logger from '../libs/logger'; import { CustomError, BadRequest, InternalServerError, } from '../libs/errors'; -import { - map, - omit, -} from 'lodash'; export default function errorHandler (err, req, res, next) { // eslint-disable-line no-unused-vars // In case of a CustomError class, use it's data @@ -28,26 +28,22 @@ export default function errorHandler (err, req, res, next) { // eslint-disable-l // Handle errors by express-validator if (Array.isArray(err) && err[0].param && err[0].msg) { responseErr = new BadRequest(res.t('invalidReqParams')); - responseErr.errors = err.map((paramErr) => { - return { - message: paramErr.msg, - param: paramErr.param, - value: paramErr.value, - }; - }); + responseErr.errors = err.map(paramErr => ({ + message: paramErr.msg, + param: paramErr.param, + value: paramErr.value, + })); } // Handle mongoose validation errors if (err.name === 'ValidationError') { const model = err.message.split(' ')[0]; responseErr = new BadRequest(`${model} validation failed`); - responseErr.errors = map(err.errors, (mongooseErr) => { - return { - message: mongooseErr.message, - path: mongooseErr.path, - value: mongooseErr.value, - }; - }); + responseErr.errors = map(err.errors, mongooseErr => ({ + message: mongooseErr.message, + path: mongooseErr.path, + value: mongooseErr.value, + })); } // Handle Stripe Card errors errors (can be safely shown to the users) @@ -78,7 +74,7 @@ export default function errorHandler (err, req, res, next) { // eslint-disable-l isHandledError: responseErr.httpCode < 500, }); - let jsonRes = { + const jsonRes = { success: false, error: responseErr.name, message: responseErr.message, diff --git a/website/server/middlewares/index.js b/website/server/middlewares/index.js index fbea1f69ba..580cf3a73b 100644 --- a/website/server/middlewares/index.js +++ b/website/server/middlewares/index.js @@ -1,18 +1,20 @@ // This module is only used to attach middlewares to the express app -import errorHandler from './errorHandler'; import bodyParser from 'body-parser'; -import notFoundHandler from './notFound'; import nconf from 'nconf'; import morgan from 'morgan'; import cookieSession from 'cookie-session'; +import mongoose from 'mongoose'; +import compression from 'compression'; +import methodOverride from 'method-override'; +import passport from 'passport'; +import basicAuth from 'express-basic-auth'; +import helmet from 'helmet'; +import errorHandler from './errorHandler'; +import notFoundHandler from './notFound'; import cors from './cors'; import staticMiddleware from './static'; import domainMiddleware from './domain'; -import mongoose from 'mongoose'; -import compression from 'compression'; // import favicon from 'serve-favicon'; -import methodOverride from 'method-override'; -import passport from 'passport'; // import path from 'path'; import maintenanceMode from './maintenanceMode'; import { @@ -26,8 +28,6 @@ import responseHandler from './response'; import { attachTranslateFunction, } from './language'; -import basicAuth from 'express-basic-auth'; -import helmet from 'helmet'; const IS_PROD = nconf.get('IS_PROD'); const DISABLE_LOGGING = nconf.get('DISABLE_REQUEST_LOGGING') === 'true'; diff --git a/website/server/middlewares/language.js b/website/server/middlewares/language.js index d5d6f2d96f..6af98448a7 100644 --- a/website/server/middlewares/language.js +++ b/website/server/middlewares/language.js @@ -1,28 +1,26 @@ -import { model as User } from '../models/user'; import accepts from 'accepts'; -import common from '../../common'; import _ from 'lodash'; +import { model as User } from '../models/user'; +import common from '../../common'; import { translations, defaultLangCodes, multipleVersionsLanguages, } from '../libs/i18n'; -const i18n = common.i18n; +const { i18n } = common; function _getUniqueListOfLanguages (languages) { - let acceptableLanguages = _(languages).map((lang) => { - return lang.slice(0, 2); - }).uniq().value(); + const acceptableLanguages = _(languages).map(lang => lang.slice(0, 2)).uniq().value(); - let uniqueListOfLanguages = _.intersection(acceptableLanguages, defaultLangCodes); + const uniqueListOfLanguages = _.intersection(acceptableLanguages, defaultLangCodes); return uniqueListOfLanguages; } function _checkForApplicableLanguageVariant (originalLanguageOptions) { - let languageVariant = _.find(originalLanguageOptions, (accepted) => { - let trimmedAccepted = accepted.slice(0, 2); + const languageVariant = _.find(originalLanguageOptions, accepted => { + const trimmedAccepted = accepted.slice(0, 2); return multipleVersionsLanguages[trimmedAccepted]; }); @@ -31,10 +29,10 @@ function _checkForApplicableLanguageVariant (originalLanguageOptions) { } function _getFromBrowser (req) { - let originalLanguageOptions = accepts(req).languages(); - let uniqueListOfLanguages = _getUniqueListOfLanguages(originalLanguageOptions); - let baseLanguage = (uniqueListOfLanguages[0] || '').toLowerCase(); - let languageMapping = multipleVersionsLanguages[baseLanguage]; + const originalLanguageOptions = accepts(req).languages(); + const uniqueListOfLanguages = _getUniqueListOfLanguages(originalLanguageOptions); + const baseLanguage = (uniqueListOfLanguages[0] || '').toLowerCase(); + const languageMapping = multipleVersionsLanguages[baseLanguage]; if (languageMapping) { let languageVariant = _checkForApplicableLanguageVariant(originalLanguageOptions); @@ -46,14 +44,13 @@ function _getFromBrowser (req) { } return languageMapping[languageVariant] || baseLanguage; - } else { - return baseLanguage || 'en'; } + return baseLanguage || 'en'; } function _getFromUser (user, req) { - let preferredLang = user && user.preferences && user.preferences.language; - let lang = translations[preferredLang] ? preferredLang : _getFromBrowser(req); + const preferredLang = user && user.preferences && user.preferences.language; + const lang = translations[preferredLang] ? preferredLang : _getFromBrowser(req); return lang; } @@ -70,22 +67,21 @@ export function getUserLanguage (req, res, next) { if (req.query.lang) { // In case the language is specified in the request url, use it req.language = translations[req.query.lang] ? req.query.lang : 'en'; return next(); - } else if (req.locals && req.locals.user) { // If the request is authenticated, use the user's preferred language + } if (req.locals && req.locals.user) { // If the request is authenticated, use the user's preferred language req.language = _getFromUser(req.locals.user, req); return next(); - } else if (req.session && req.session.userId) { // Same thing if the user has a valid session + } if (req.session && req.session.userId) { // Same thing if the user has a valid session return User.findOne({ _id: req.session.userId, }, 'preferences.language') .lean() .exec() - .then((user) => { + .then(user => { req.language = _getFromUser(user, req); return next(); }) .catch(next); - } else { // Otherwise get from browser - req.language = _getFromUser(null, req); - return next(); - } + } // Otherwise get from browser + req.language = _getFromUser(null, req); + return next(); } diff --git a/website/server/middlewares/maintenanceMode.js b/website/server/middlewares/maintenanceMode.js index a26191f99c..2e2501b276 100644 --- a/website/server/middlewares/maintenanceMode.js +++ b/website/server/middlewares/maintenanceMode.js @@ -1,15 +1,15 @@ -import { getUserLanguage } from './language'; import nconf from 'nconf'; +import { getUserLanguage } from './language'; const MAINTENANCE_MODE = nconf.get('MAINTENANCE_MODE'); export default function maintenanceMode (req, res, next) { if (MAINTENANCE_MODE !== 'true') return next(); - getUserLanguage(req, res, (err) => { + getUserLanguage(req, res, err => { if (err) return next(err); - let pageVariables = { + const pageVariables = { maintenanceStart: nconf.get('MAINTENANCE_START'), maintenanceEnd: nconf.get('MAINTENANCE_END'), translation: res.t, @@ -18,14 +18,12 @@ export default function maintenanceMode (req, res, next) { if (req.headers && req.headers.accept && req.headers.accept.indexOf('text/html') !== -1) { if (req.path === '/views/static/maintenance-info') { return res.status(503).render('../../views/static/maintenance-info', pageVariables); - } else { - return res.status(503).render('../../views/static/maintenance', pageVariables); } - } else { - return res.status(503).send({ - error: 'Maintenance', - message: 'Server offline for maintenance.', - }); + return res.status(503).render('../../views/static/maintenance', pageVariables); } + return res.status(503).send({ + error: 'Maintenance', + message: 'Server offline for maintenance.', + }); }); } diff --git a/website/server/middlewares/notFound.js b/website/server/middlewares/notFound.js index 6337748817..cffa2b9bc6 100644 --- a/website/server/middlewares/notFound.js +++ b/website/server/middlewares/notFound.js @@ -28,7 +28,6 @@ export default function NotFoundMiddleware (req, res, next) { if (isExistingRoute || req.method !== 'GET') { return next(new NotFound()); - } else { - serveClient(res); } + serveClient(res); } diff --git a/website/server/middlewares/redirects.js b/website/server/middlewares/redirects.js index 587474378e..7bdb7df630 100644 --- a/website/server/middlewares/redirects.js +++ b/website/server/middlewares/redirects.js @@ -12,15 +12,15 @@ const BASE_URL_HOST = url.parse(BASE_URL).hostname; function isHTTP (req) { return ( // eslint-disable-line no-extra-parens - req.header('x-forwarded-proto') && - req.header('x-forwarded-proto') === 'http' && - IS_PROD && - BASE_URL.indexOf('https') === 0 + req.header('x-forwarded-proto') + && req.header('x-forwarded-proto') === 'http' + && IS_PROD + && BASE_URL.indexOf('https') === 0 ); } export function forceSSL (req, res, next) { - const skipSSLCheck = req.query.skipSSLCheck; + const { skipSSLCheck } = req.query; if (isHTTP(req) && (!SKIP_SSL_CHECK_KEY || !skipSSLCheck || skipSSLCheck !== SKIP_SSL_CHECK_KEY)) { return res.redirect(BASE_URL + req.originalUrl); } diff --git a/website/server/middlewares/response.js b/website/server/middlewares/response.js index 51d7dc5418..945cf19dea 100644 --- a/website/server/middlewares/response.js +++ b/website/server/middlewares/response.js @@ -6,9 +6,9 @@ import { export default function responseHandler (req, res, next) { // Only used for successful responses res.respond = function respond (status = 200, data = {}, message) { - let user = res.locals && res.locals.user; + const user = res.locals && res.locals.user; - let response = { + const response = { success: status < 400, data, }; diff --git a/website/server/middlewares/v1.js b/website/server/middlewares/v1.js index 32b1ea30d0..2362b7cb97 100644 --- a/website/server/middlewares/v1.js +++ b/website/server/middlewares/v1.js @@ -11,8 +11,8 @@ const router = express.Router(); // eslint-disable-line new-cap const BASE_URL = nconf.get('BASE_URL'); -router.all('*', function deprecatedV1 (req, res, next) { - let error = new NotFound(`API v1 is no longer supported, please use API v3 instead (${BASE_URL}/apidoc).`); +router.all('*', (req, res, next) => { + const error = new NotFound(`API v1 is no longer supported, please use API v3 instead (${BASE_URL}/apidoc).`); return next(error); }); diff --git a/website/server/middlewares/v2.js b/website/server/middlewares/v2.js index 1b0cde86fb..5e2b012f07 100644 --- a/website/server/middlewares/v2.js +++ b/website/server/middlewares/v2.js @@ -11,8 +11,8 @@ const router = express.Router(); // eslint-disable-line new-cap const BASE_URL = nconf.get('BASE_URL'); -router.all('*', function deprecatedV2 (req, res, next) { - let error = new NotFound(`API v2 is no longer supported, please use API v3 instead (${BASE_URL}/apidoc).`); +router.all('*', (req, res, next) => { + const error = new NotFound(`API v2 is no longer supported, please use API v3 instead (${BASE_URL}/apidoc).`); return next(error); }); diff --git a/website/server/models/challenge.js b/website/server/models/challenge.js index 97b37338d8..b38b393be8 100644 --- a/website/server/models/challenge.js +++ b/website/server/models/challenge.js @@ -1,7 +1,8 @@ import mongoose from 'mongoose'; import validator from 'validator'; -import baseModel from '../libs/baseModel'; import _ from 'lodash'; +import { TaskQueue } from 'cwait'; +import baseModel from '../libs/baseModel'; import * as Tasks from './task'; import { model as User } from './user'; import { @@ -11,33 +12,36 @@ import { removeFromArray } from '../libs/collectionManipulators'; import shared from '../../common'; import { sendTxn as txnEmail } from '../libs/email'; import { sendNotification as sendPushNotification } from '../libs/pushNotifications'; -import { TaskQueue } from 'cwait'; import { syncableAttrs, setNextDue } from '../libs/taskManager'; -const Schema = mongoose.Schema; +const { Schema } = mongoose; -const MIN_SHORTNAME_SIZE_FOR_CHALLENGES = shared.constants.MIN_SHORTNAME_SIZE_FOR_CHALLENGES; -const MAX_SUMMARY_SIZE_FOR_CHALLENGES = shared.constants.MAX_SUMMARY_SIZE_FOR_CHALLENGES; +const { MIN_SHORTNAME_SIZE_FOR_CHALLENGES } = shared.constants; +const { MAX_SUMMARY_SIZE_FOR_CHALLENGES } = shared.constants; -let schema = new Schema({ - name: {$type: String, required: true}, - shortName: {$type: String, required: true, minlength: MIN_SHORTNAME_SIZE_FOR_CHALLENGES}, - summary: {$type: String, maxlength: MAX_SUMMARY_SIZE_FOR_CHALLENGES}, +const schema = new Schema({ + name: { $type: String, required: true }, + shortName: { $type: String, required: true, minlength: MIN_SHORTNAME_SIZE_FOR_CHALLENGES }, + summary: { $type: String, maxlength: MAX_SUMMARY_SIZE_FOR_CHALLENGES }, description: String, - official: {$type: Boolean, default: false}, + official: { $type: Boolean, default: false }, tasksOrder: { - habits: [{$type: String, ref: 'Task'}], - dailys: [{$type: String, ref: 'Task'}], - todos: [{$type: String, ref: 'Task'}], - rewards: [{$type: String, ref: 'Task'}], + habits: [{ $type: String, ref: 'Task' }], + dailys: [{ $type: String, ref: 'Task' }], + todos: [{ $type: String, ref: 'Task' }], + rewards: [{ $type: String, ref: 'Task' }], }, - leader: {$type: String, ref: 'User', validate: [v => validator.isUUID(v), 'Invalid uuid.'], required: true}, - group: {$type: String, ref: 'Group', validate: [v => validator.isUUID(v), 'Invalid uuid.'], required: true}, - memberCount: {$type: Number, default: 0}, - prize: {$type: Number, default: 0, min: 0}, + leader: { + $type: String, ref: 'User', validate: [v => validator.isUUID(v), 'Invalid uuid.'], required: true, + }, + group: { + $type: String, ref: 'Group', validate: [v => validator.isUUID(v), 'Invalid uuid.'], required: true, + }, + memberCount: { $type: Number, default: 0 }, + prize: { $type: Number, default: 0, min: 0 }, categories: [{ - slug: {$type: String}, - name: {$type: String}, + slug: { $type: String }, + name: { $type: String }, }], }, { strict: true, @@ -50,7 +54,7 @@ schema.plugin(baseModel, { timestamps: true, }); -schema.pre('init', function ensureSummaryIsFetched (chal) { +schema.pre('init', chal => { // The Vue website makes the summary be mandatory for all new challenges, but the // Angular website did not, and the API does not yet for backwards-compatibilty. // When any challenge without a summary is fetched from the database, this code @@ -62,7 +66,7 @@ schema.pre('init', function ensureSummaryIsFetched (chal) { }); // A list of additional fields that cannot be updated (but can be set on creation) -let noUpdate = ['group', 'leader', 'official', 'shortName', 'prize']; +const noUpdate = ['group', 'leader', 'official', 'shortName', 'prize']; schema.statics.sanitizeUpdate = function sanitizeUpdate (updateObj) { return this.sanitize(updateObj, noUpdate); }; @@ -99,7 +103,7 @@ schema.methods.canView = function canViewChallenge (user, group) { // Sync challenge to user, including tasks and tags. // Used when user joins the challenge or to force sync. schema.methods.syncToUser = async function syncChallengeToUser (user) { - let challenge = this; + const challenge = this; challenge.shortName = challenge.shortName || challenge.name; // Add challenge to user.challenges @@ -109,8 +113,8 @@ schema.methods.syncToUser = async function syncChallengeToUser (user) { user.challenges = user.challenges.concat([challenge._id]); } // Sync tags - let userTags = user.tags; - let i = _.findIndex(userTags, {id: challenge._id}); + const userTags = user.tags; + const i = _.findIndex(userTags, { id: challenge._id }); if (i !== -1) { if (userTags[i].name !== challenge.shortName) { @@ -126,10 +130,10 @@ schema.methods.syncToUser = async function syncChallengeToUser (user) { }); } - let [challengeTasks, userTasks] = await Promise.all([ + const [challengeTasks, userTasks] = await Promise.all([ // Find original challenge tasks Tasks.Task.find({ - userId: {$exists: false}, + userId: { $exists: false }, 'challenge.id': challenge._id, }).exec(), // Find user's tasks linked to this challenge @@ -139,21 +143,21 @@ schema.methods.syncToUser = async function syncChallengeToUser (user) { }).exec(), ]); - let toSave = []; // An array of things to save + const toSave = []; // An array of things to save challengeTasks.forEach(chalTask => { let matchingTask = _.find(userTasks, userTask => userTask.challenge.taskId === chalTask._id); if (!matchingTask) { // If the task is new, create it matchingTask = new Tasks[chalTask.type](Tasks.Task.sanitize(syncableAttrs(chalTask))); - matchingTask.challenge = {taskId: chalTask._id, id: challenge._id, shortName: challenge.shortName}; + matchingTask.challenge = { taskId: chalTask._id, id: challenge._id, shortName: challenge.shortName }; matchingTask.userId = user._id; user.tasksOrder[`${chalTask.type}s`].push(matchingTask._id); setNextDue(matchingTask, user); } else { _.merge(matchingTask, syncableAttrs(chalTask)); // Make sure the task is in user.tasksOrder - let orderList = user.tasksOrder[`${chalTask.type}s`]; + const orderList = user.tasksOrder[`${chalTask.type}s`]; if (orderList.indexOf(matchingTask._id) === -1 && (matchingTask.type !== 'todo' || !matchingTask.completed)) orderList.push(matchingTask._id); } @@ -175,23 +179,23 @@ schema.methods.syncToUser = async function syncChallengeToUser (user) { }; async function _fetchMembersIds (challengeId) { - return (await User.find({challenges: {$in: [challengeId]}}).select('_id').lean().exec()).map(member => member._id); + return (await User.find({ challenges: { $in: [challengeId] } }).select('_id').lean().exec()).map(member => member._id); } async function _addTaskFn (challenge, tasks, memberId) { - let updateTasksOrderQ = {$push: {}}; - let toSave = []; + const updateTasksOrderQ = { $push: {} }; + const toSave = []; tasks.forEach(chalTask => { - let userTask = new Tasks[chalTask.type](Tasks.Task.sanitize(syncableAttrs(chalTask))); - userTask.challenge = {taskId: chalTask._id, id: challenge._id, shortName: challenge.shortName}; + const userTask = new Tasks[chalTask.type](Tasks.Task.sanitize(syncableAttrs(chalTask))); + userTask.challenge = { taskId: chalTask._id, id: challenge._id, shortName: challenge.shortName }; userTask.userId = memberId; // We want to sync the notes and tags when the task is first added to the challenge userTask.notes = chalTask.notes; userTask.tags.push(challenge._id); - let tasksOrderList = updateTasksOrderQ.$push[`tasksOrder.${chalTask.type}s`]; + const tasksOrderList = updateTasksOrderQ.$push[`tasksOrder.${chalTask.type}s`]; if (!tasksOrderList) { updateTasksOrderQ.$push[`tasksOrder.${chalTask.type}s`] = { $position: 0, // unshift @@ -207,60 +211,58 @@ async function _addTaskFn (challenge, tasks, memberId) { }); // Update the user - toSave.unshift(User.update({_id: memberId}, updateTasksOrderQ).exec()); + toSave.unshift(User.update({ _id: memberId }, updateTasksOrderQ).exec()); return await Promise.all(toSave); } // Add a new task to challenge members schema.methods.addTasks = async function challengeAddTasks (tasks) { - let challenge = this; - let membersIds = await _fetchMembersIds(challenge._id); + const challenge = this; + const membersIds = await _fetchMembersIds(challenge._id); - let queue = new TaskQueue(Promise, 25); // process only 5 users concurrently + const queue = new TaskQueue(Promise, 25); // process only 5 users concurrently - await Promise.all(membersIds.map(queue.wrap((memberId) => { - return _addTaskFn(challenge, tasks, memberId); - }))); + await Promise.all(membersIds.map(queue.wrap(memberId => _addTaskFn(challenge, tasks, memberId)))); }; // Sync updated task to challenge members schema.methods.updateTask = async function challengeUpdateTask (task) { - let challenge = this; + const challenge = this; - let updateCmd = {$set: {}}; + const updateCmd = { $set: {} }; - let syncableTask = syncableAttrs(task); - for (let key in syncableTask) { + const syncableTask = syncableAttrs(task); + for (const key in syncableTask) { updateCmd.$set[key] = syncableTask[key]; } - let taskSchema = Tasks[task.type]; + const taskSchema = Tasks[task.type]; // Updating instead of loading and saving for performances, risks becoming a problem if we introduce more complexity in tasks await taskSchema.update({ - userId: {$exists: true}, + userId: { $exists: true }, 'challenge.id': challenge.id, 'challenge.taskId': task._id, - }, updateCmd, {multi: true}).exec(); + }, updateCmd, { multi: true }).exec(); }; // Remove a task from challenge members schema.methods.removeTask = async function challengeRemoveTask (task) { - let challenge = this; + const challenge = this; // Set the task as broken await Tasks.Task.update({ - userId: {$exists: true}, + userId: { $exists: true }, 'challenge.id': challenge.id, 'challenge.taskId': task._id, }, { - $set: {'challenge.broken': 'TASK_DELETED'}, - }, {multi: true}).exec(); + $set: { 'challenge.broken': 'TASK_DELETED' }, + }, { multi: true }).exec(); }; // Unlink challenges tasks (and the challenge itself) from user. TODO rename to 'leave' schema.methods.unlinkTasks = async function challengeUnlinkTasks (user, keep, saveUser = true) { - let challengeId = this._id; - let findQuery = { + const challengeId = this._id; + const findQuery = { userId: user._id, 'challenge.id': challengeId, }; @@ -270,8 +272,8 @@ schema.methods.unlinkTasks = async function challengeUnlinkTasks (user, keep, sa if (keep === 'keep-all') { await Tasks.Task.update(findQuery, { - $set: {challenge: {}}, - }, {multi: true}).exec(); + $set: { challenge: {} }, + }, { multi: true }).exec(); const promises = [this.save()]; @@ -280,45 +282,44 @@ schema.methods.unlinkTasks = async function challengeUnlinkTasks (user, keep, sa if (saveUser) promises.push(user.save()); return Promise.all(promises); - } else { // keep = 'remove-all' - let tasks = await Tasks.Task.find(findQuery).select('_id type completed').exec(); - let taskPromises = tasks.map(task => { - // Remove task from user.tasksOrder and delete them - if (task.type !== 'todo' || !task.completed) { - removeFromArray(user.tasksOrder[`${task.type}s`], task._id); - } + } // keep = 'remove-all' + const tasks = await Tasks.Task.find(findQuery).select('_id type completed').exec(); + const taskPromises = tasks.map(task => { + // Remove task from user.tasksOrder and delete them + if (task.type !== 'todo' || !task.completed) { + removeFromArray(user.tasksOrder[`${task.type}s`], task._id); + } - return task.remove(); - }); - user.markModified('tasksOrder'); - taskPromises.push(this.save()); + return task.remove(); + }); + user.markModified('tasksOrder'); + taskPromises.push(this.save()); - // When multiple tasks are being unlinked at the same time, - // save the user once outside of this function - if (saveUser) taskPromises.push(user.save()); + // When multiple tasks are being unlinked at the same time, + // save the user once outside of this function + if (saveUser) taskPromises.push(user.save()); - return Promise.all(taskPromises); - } + return Promise.all(taskPromises); }; // TODO everything here should be moved to a worker // actually even for a worker it's probably just too big and will kill mongo, figure out something else schema.methods.closeChal = async function closeChal (broken = {}) { - let challenge = this; + const challenge = this; - let winner = broken.winner; - let brokenReason = broken.broken; + const { winner } = broken; + const brokenReason = broken.broken; // Delete the challenge - await this.model('Challenge').remove({_id: challenge._id}).exec(); + await this.model('Challenge').remove({ _id: challenge._id }).exec(); // Refund the leader if the challenge is deleted (no winner chosen) if (brokenReason === 'CHALLENGE_DELETED') { - await User.update({_id: challenge.leader}, {$inc: {balance: challenge.prize / 4}}).exec(); + await User.update({ _id: challenge.leader }, { $inc: { balance: challenge.prize / 4 } }).exec(); } // Update the challengeCount on the group - await Group.update({_id: challenge.group}, {$inc: {challengeCount: -1}}).exec(); + await Group.update({ _id: challenge.group }, { $inc: { challengeCount: -1 } }).exec(); // Award prize to winner and notify if (winner) { @@ -328,18 +329,18 @@ schema.methods.closeChal = async function closeChal (broken = {}) { // reimburse the leader const winnerCanGetGems = await winner.canGetGems(); if (!winnerCanGetGems) { - await User.update({_id: challenge.leader}, {$inc: {balance: challenge.prize / 4}}).exec(); + await User.update({ _id: challenge.leader }, { $inc: { balance: challenge.prize / 4 } }).exec(); } else { winner.balance += challenge.prize / 4; } winner.addNotification('WON_CHALLENGE'); - let savedWinner = await winner.save(); + const savedWinner = await winner.save(); if (savedWinner.preferences.emailNotifications.wonChallenge !== false) { txnEmail(savedWinner, 'won-challenge', [ - {name: 'CHALLENGE_NAME', content: challenge.name}, + { name: 'CHALLENGE_NAME', content: challenge.name }, ]); } if (savedWinner.preferences.pushNotifications.wonChallenge !== false) { @@ -353,17 +354,17 @@ schema.methods.closeChal = async function closeChal (broken = {}) { } // Run some operations in the background withouth blocking the thread - let backgroundTasks = [ + const backgroundTasks = [ // And it's tasks - Tasks.Task.remove({'challenge.id': challenge._id, userId: {$exists: false}}).exec(), + Tasks.Task.remove({ 'challenge.id': challenge._id, userId: { $exists: false } }).exec(), // Set the challenge tag to non-challenge status and remove the challenge from the user's challenges User.update({ challenges: challenge._id, 'tags.id': challenge._id, }, { - $set: {'tags.$.challenge': false}, - $pull: {challenges: challenge._id}, - }, {multi: true}).exec(), + $set: { 'tags.$.challenge': false }, + $pull: { challenges: challenge._id }, + }, { multi: true }).exec(), // Break users' tasks Tasks.Task.update({ 'challenge.id': challenge._id, @@ -372,10 +373,10 @@ schema.methods.closeChal = async function closeChal (broken = {}) { 'challenge.broken': brokenReason, 'challenge.winner': winner && winner.profile.name, }, - }, {multi: true}).exec(), + }, { multi: true }).exec(), ]; Promise.all(backgroundTasks); }; -export let model = mongoose.model('Challenge', schema); +export const model = mongoose.model('Challenge', schema); diff --git a/website/server/models/coupon.js b/website/server/models/coupon.js index 1505835a31..a36b1f06ea 100644 --- a/website/server/models/coupon.js +++ b/website/server/models/coupon.js @@ -2,18 +2,18 @@ import mongoose from 'mongoose'; import _ from 'lodash'; -import shared from '../../common'; import couponCode from 'coupon-code'; +import shared from '../../common'; import baseModel from '../libs/baseModel'; import { BadRequest, NotAuthorized, } from '../libs/errors'; -export let schema = new mongoose.Schema({ - _id: {$type: String, default: couponCode.generate, required: true}, - event: {$type: String, enum: ['wondercon', 'google_6mo']}, - user: {$type: String, ref: 'User'}, +export const schema = new mongoose.Schema({ + _id: { $type: String, default: couponCode.generate, required: true }, + event: { $type: String, enum: ['wondercon', 'google_6mo'] }, + user: { $type: String, ref: 'User' }, }, { strict: true, minimize: false, // So empty objects are returned @@ -26,15 +26,13 @@ schema.plugin(baseModel, { }); schema.statics.generate = async function generateCoupons (event, count = 1) { - let coupons = _.times(count, () => { - return {event}; - }); + const coupons = _.times(count, () => ({ event })); return await this.create(coupons); }; schema.statics.apply = async function applyCoupon (user, req, code) { - let coupon = await this.findById(couponCode.validate(code)).exec(); + const coupon = await this.findById(couponCode.validate(code)).exec(); if (!coupon) throw new BadRequest(shared.i18n.t('invalidCoupon', req.language)); if (coupon.user) throw new NotAuthorized(shared.i18n.t('couponUsed', req.language)); @@ -48,7 +46,7 @@ schema.statics.apply = async function applyCoupon (user, req, code) { user.items.gear.owned.body_special_wondercon_gold = true; user.markModified('items.gear.owned'); - user.extra = {signupEvent: 'wondercon'}; + user.extra = { signupEvent: 'wondercon' }; } await user.save(); @@ -56,4 +54,4 @@ schema.statics.apply = async function applyCoupon (user, req, code) { await coupon.save(); }; -export let model = mongoose.model('Coupon', schema); +export const model = mongoose.model('Coupon', schema); diff --git a/website/server/models/emailUnsubscription.js b/website/server/models/emailUnsubscription.js index da21d264cb..1e7e5a149e 100644 --- a/website/server/models/emailUnsubscription.js +++ b/website/server/models/emailUnsubscription.js @@ -3,7 +3,7 @@ import validator from 'validator'; import baseModel from '../libs/baseModel'; // A collection used to store mailing list unsubscription for non registered email addresses -export let schema = new mongoose.Schema({ +export const schema = new mongoose.Schema({ email: { $type: String, required: true, @@ -22,4 +22,4 @@ schema.plugin(baseModel, { timestamps: true, }); -export let model = mongoose.model('EmailUnsubscription', schema); +export const model = mongoose.model('EmailUnsubscription', schema); diff --git a/website/server/models/group.js b/website/server/models/group.js index f7325bc636..23cd0c00fb 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -1,19 +1,20 @@ import moment from 'moment'; import mongoose from 'mongoose'; +import _ from 'lodash'; +import validator from 'validator'; +import nconf from 'nconf'; import { model as User, nameFields, } from './user'; import shared from '../../common'; -import _ from 'lodash'; -import { model as Challenge} from './challenge'; +import { model as Challenge } from './challenge'; import { chatModel as Chat, setUserStyles, messageDefaults, } from './message'; import * as Tasks from './task'; -import validator from 'validator'; import { removeFromArray } from '../libs/collectionManipulators'; import payments from '../libs/payments/payments'; import { @@ -27,7 +28,6 @@ import { } from '../libs/errors'; import baseModel from '../libs/baseModel'; import { sendTxn as sendTxnEmail } from '../libs/email'; -import nconf from 'nconf'; import { sendNotification as sendPushNotification } from '../libs/pushNotifications'; import { syncableAttrs, @@ -42,18 +42,18 @@ import { model as UserNotification } from './userNotification'; import { sendChatPushNotifications } from '../libs/chat'; const questScrolls = shared.content.quests; -const questSeriesAchievements = shared.content.questSeriesAchievements; -const Schema = mongoose.Schema; +const { questSeriesAchievements } = shared.content; +const { Schema } = mongoose; export const INVITES_LIMIT = 100; // must not be greater than MAX_EMAIL_INVITES_BY_USER -export const TAVERN_ID = shared.TAVERN_ID; +export const { TAVERN_ID } = shared; const NO_CHAT_NOTIFICATIONS = [TAVERN_ID]; -const LARGE_GROUP_COUNT_MESSAGE_CUTOFF = shared.constants.LARGE_GROUP_COUNT_MESSAGE_CUTOFF; -const MAX_SUMMARY_SIZE_FOR_GUILDS = shared.constants.MAX_SUMMARY_SIZE_FOR_GUILDS; -const GUILDS_PER_PAGE = shared.constants.GUILDS_PER_PAGE; +const { LARGE_GROUP_COUNT_MESSAGE_CUTOFF } = shared.constants; +const { MAX_SUMMARY_SIZE_FOR_GUILDS } = shared.constants; +const { GUILDS_PER_PAGE } = shared.constants; -const CHAT_FLAG_LIMIT_FOR_HIDING = shared.constants.CHAT_FLAG_LIMIT_FOR_HIDING; +const { CHAT_FLAG_LIMIT_FOR_HIDING } = shared.constants; const CRON_SAFE_MODE = nconf.get('CRON_SAFE_MODE') === 'true'; const CRON_SEMI_SAFE_MODE = nconf.get('CRON_SEMI_SAFE_MODE') === 'true'; @@ -72,64 +72,73 @@ export const SPAM_MIN_EXEMPT_CONTRIB_LEVEL = 4; export const MAX_CHAT_COUNT = 200; export const MAX_SUBBED_GROUP_CHAT_COUNT = 400; -export let schema = new Schema({ - name: {$type: String, required: true}, - summary: {$type: String, maxlength: MAX_SUMMARY_SIZE_FOR_GUILDS}, +export const schema = new Schema({ + name: { $type: String, required: true }, + summary: { $type: String, maxlength: MAX_SUMMARY_SIZE_FOR_GUILDS }, description: String, - leader: {$type: String, ref: 'User', validate: [v => validator.isUUID(v), 'Invalid uuid.'], required: true}, - type: {$type: String, enum: ['guild', 'party'], required: true}, - privacy: {$type: String, enum: ['private', 'public'], default: 'private', required: true}, + leader: { + $type: String, ref: 'User', validate: [v => validator.isUUID(v), 'Invalid uuid.'], required: true, + }, + type: { $type: String, enum: ['guild', 'party'], required: true }, + privacy: { + $type: String, enum: ['private', 'public'], default: 'private', required: true, + }, chat: Array, // Used for backward compatibility, but messages aren't stored here leaderOnly: { // restrict group actions to leader (members can't do them) - challenges: {$type: Boolean, default: false, required: true}, + challenges: { $type: Boolean, default: false, required: true }, // invites: {$type: Boolean, default: false, required: true}, // Some group plans prevent members from getting gems - getGems: {$type: Boolean, default: false}, + getGems: { $type: Boolean, default: false }, }, - memberCount: {$type: Number, default: 1}, - challengeCount: {$type: Number, default: 0}, - balance: {$type: Number, default: 0}, + memberCount: { $type: Number, default: 1 }, + challengeCount: { $type: Number, default: 0 }, + balance: { $type: Number, default: 0 }, logo: String, leaderMessage: String, quest: { key: String, - active: {$type: Boolean, default: false}, - leader: {$type: String, ref: 'User'}, + active: { $type: Boolean, default: false }, + leader: { $type: String, ref: 'User' }, progress: { hp: Number, - collect: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, // {feather: 5, ingot: 3} + collect: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, // {feather: 5, ingot: 3} rage: Number, // limit break / "energy stored in shell", for explosion-attacks }, // Shows boolean for each party-member who has accepted the quest. Eg {UUID: true, UUID: false}. Once all users click // 'Accept', the quest begins. If a false user waits too long, probably a good sign to prod them or boot them. // TODO when booting user, remove from .joined and check again if we can now start the quest - members: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, - extra: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, + members: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, + extra: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, }, tasksOrder: { - habits: [{$type: String, ref: 'Task'}], - dailys: [{$type: String, ref: 'Task'}], - todos: [{$type: String, ref: 'Task'}], - rewards: [{$type: String, ref: 'Task'}], + habits: [{ $type: String, ref: 'Task' }], + dailys: [{ $type: String, ref: 'Task' }], + todos: [{ $type: String, ref: 'Task' }], + rewards: [{ $type: String, ref: 'Task' }], }, purchased: { - plan: {$type: SubscriptionPlanSchema, default: () => { - return {}; - }}, + plan: { + $type: SubscriptionPlanSchema, + default: () => ({}), + }, + }, + managers: { + $type: Schema.Types.Mixed, + default: () => ({}), }, - managers: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, categories: [{ - slug: {$type: String}, - name: {$type: String}, + slug: { $type: String }, + name: { $type: String }, }], }, { strict: true, @@ -145,7 +154,7 @@ schema.plugin(baseModel, { }, }); -schema.pre('init', function ensureSummaryIsFetched (group) { +schema.pre('init', group => { // The Vue website makes the summary be mandatory for all new groups, but the // Angular website did not, and the API does not yet for backwards-compatibilty. // When any guild without a summary is fetched from the database, this code @@ -159,13 +168,13 @@ schema.pre('init', function ensureSummaryIsFetched (group) { }); // A list of additional fields that cannot be updated (but can be set on creation) -let noUpdate = ['privacy', 'type']; +const noUpdate = ['privacy', 'type']; schema.statics.sanitizeUpdate = function sanitizeUpdate (updateObj) { return this.sanitize(updateObj, noUpdate); }; // Basic fields to fetch for populating a group info -export let basicFields = 'name type privacy leader summary categories'; +export const basicFields = 'name type privacy leader summary categories'; schema.pre('remove', true, async function preRemoveGroup (next, done) { next(); @@ -179,7 +188,7 @@ schema.pre('remove', true, async function preRemoveGroup (next, done) { // return clean updates for each user in a party without resetting their progress function _cleanQuestParty (merge) { - let updates = { + const updates = { $set: { 'party.quest.key': null, 'party.quest.completed': null, @@ -205,7 +214,7 @@ function _cleanQuestUser (userProgress) { userProgress = userProgress.toObject(); } - let clean = { + const clean = { key: null, progress: userProgress, completed: null, @@ -216,12 +225,14 @@ function _cleanQuestUser (userProgress) { } schema.statics.getGroup = async function getGroup (options = {}) { - let {user, groupId, fields, optionalMembership = false, populateLeader = false, requireMembership = false} = options; + const { + user, groupId, fields, optionalMembership = false, populateLeader = false, requireMembership = false, + } = options; let query; - let isUserParty = groupId === 'party' || user.party._id === groupId; - let isUserGuild = user.guilds.indexOf(groupId) !== -1; - let isTavern = ['habitrpg', TAVERN_ID].indexOf(groupId) !== -1; + const isUserParty = groupId === 'party' || user.party._id === groupId; + const isUserGuild = user.guilds.indexOf(groupId) !== -1; + const isTavern = ['habitrpg', TAVERN_ID].indexOf(groupId) !== -1; // When requireMembership is true check that user is member even in public guild if (requireMembership && !isUserParty && !isUserGuild && !isTavern) { @@ -230,21 +241,21 @@ schema.statics.getGroup = async function getGroup (options = {}) { // When optionalMembership is true it's not required for the user to be a member of the group if (isUserParty) { - query = {type: 'party', _id: user.party._id}; + query = { type: 'party', _id: user.party._id }; } else if (isTavern) { - query = {_id: TAVERN_ID}; + query = { _id: TAVERN_ID }; } else if (optionalMembership === true) { - query = {_id: groupId}; + query = { _id: groupId }; } else if (isUserGuild) { - query = {type: 'guild', _id: groupId}; + query = { type: 'guild', _id: groupId }; } else { - query = {type: 'guild', privacy: 'public', _id: groupId}; + query = { type: 'guild', privacy: 'public', _id: groupId }; } - let mQuery = this.findOne(query); + const mQuery = this.findOne(query); if (fields) mQuery.select(fields); if (populateLeader === true) mQuery.populate('leader', nameFields); - let group = await mQuery.exec(); + const group = await mQuery.exec(); if (!group) { if (groupId === user.party._id) { @@ -262,44 +273,46 @@ schema.statics.getGroup = async function getGroup (options = {}) { export const VALID_QUERY_TYPES = ['party', 'guilds', 'privateGuilds', 'publicGuilds', 'tavern']; schema.statics.getGroups = async function getGroups (options = {}) { - let { + const { user, types, groupFields = basicFields, sort = '-memberCount', populateLeader = false, paginate = false, page = 0, // optional pagination for public guilds filters = {}, } = options; - let queries = []; + const queries = []; // Throw error if an invalid type is supplied - let areValidTypes = types.every(type => VALID_QUERY_TYPES.indexOf(type) !== -1); + const areValidTypes = types.every(type => VALID_QUERY_TYPES.indexOf(type) !== -1); if (!areValidTypes) throw new BadRequest(shared.i18n.t('groupTypesRequired')); types.forEach(type => { switch (type) { case 'party': { - queries.push(this.getGroup({user, groupId: 'party', fields: groupFields, populateLeader})); + queries.push(this.getGroup({ + user, groupId: 'party', fields: groupFields, populateLeader, + })); break; } case 'guilds': { - let query = { + const query = { type: 'guild', - _id: {$in: user.guilds}, + _id: { $in: user.guilds }, }; _.assign(query, filters); - let userGuildsQuery = this.find(query).select(groupFields); + const userGuildsQuery = this.find(query).select(groupFields); if (populateLeader === true) userGuildsQuery.populate('leader', nameFields); userGuildsQuery.sort(sort).exec(); queries.push(userGuildsQuery); break; } case 'privateGuilds': { - let query = { + const query = { type: 'guild', privacy: 'private', - _id: {$in: user.guilds}, + _id: { $in: user.guilds }, }; _.assign(query, filters); - let privateGuildsQuery = this.find(query).select(groupFields); + const privateGuildsQuery = this.find(query).select(groupFields); if (populateLeader === true) privateGuildsQuery.populate('leader', nameFields); privateGuildsQuery.sort(sort).exec(); queries.push(privateGuildsQuery); @@ -308,12 +321,12 @@ schema.statics.getGroups = async function getGroups (options = {}) { // NOTE: when returning publicGuilds we use `.lean()` so all mongoose methods won't be available. // Docs are going to be plain javascript objects case 'publicGuilds': { - let query = { + const query = { type: 'guild', privacy: 'public', }; _.assign(query, filters); - let publicGuildsQuery = this.find(query).select(groupFields); + const publicGuildsQuery = this.find(query).select(groupFields); if (populateLeader === true) publicGuildsQuery.populate('leader', nameFields); if (paginate === true) publicGuildsQuery.limit(GUILDS_PER_PAGE).skip(page * GUILDS_PER_PAGE); publicGuildsQuery.sort(sort).lean().exec(); @@ -322,14 +335,14 @@ schema.statics.getGroups = async function getGroups (options = {}) { } case 'tavern': { if (types.indexOf('publicGuilds') === -1) { - queries.push(this.getGroup({user, groupId: TAVERN_ID, fields: groupFields})); + queries.push(this.getGroup({ user, groupId: TAVERN_ID, fields: groupFields })); } break; } } }); - let groupsArray = _.reduce(await Promise.all(queries), (previousValue, currentValue) => { + const groupsArray = _.reduce(await Promise.all(queries), (previousValue, currentValue) => { if (_.isEmpty(currentValue)) return previousValue; // don't add anything to the results if the query returned null or an empty array return previousValue.concat(Array.isArray(currentValue) ? currentValue : [currentValue]); // otherwise concat the new results to the previousValue }, []); @@ -440,7 +453,7 @@ schema.statics.validateInvitations = async function getInvitationError (invites, const totalInvites = getInviteCount(uuids, emails); if (totalInvites > INVITES_LIMIT) { - throw new BadRequest(res.t('canOnlyInviteMaxInvites', {maxInvites: INVITES_LIMIT})); + throw new BadRequest(res.t('canOnlyInviteMaxInvites', { maxInvites: INVITES_LIMIT })); } // If party, check the limit of members @@ -451,17 +464,17 @@ schema.statics.validateInvitations = async function getInvitationError (invites, memberCount += group.memberCount; // Count how many invitations currently exist in the party - let query = {}; + const query = {}; query['invitations.party.id'] = group._id; // @TODO invitations are now stored like this: `'invitations.parties': []` - let groupInvites = await User.count(query).exec(); + const groupInvites = await User.count(query).exec(); memberCount += groupInvites; // Counting the members that are going to be invited by email and uuids memberCount += totalInvites; if (memberCount > shared.constants.PARTY_LIMIT_MEMBERS) { - throw new BadRequest(res.t('partyExceedsMembersLimit', {maxMembersParty: shared.constants.PARTY_LIMIT_MEMBERS})); + throw new BadRequest(res.t('partyExceedsMembersLimit', { maxMembersParty: shared.constants.PARTY_LIMIT_MEMBERS })); } } }; @@ -471,13 +484,13 @@ schema.methods.getParticipatingQuestMembers = function getParticipatingQuestMemb }; schema.methods.removeGroupInvitations = async function removeGroupInvitations () { - let group = this; + const group = this; - let usersToRemoveInvitationsFrom = await User.find({ + const usersToRemoveInvitationsFrom = await User.find({ [`invitations.${group.type}${group.type === 'guild' ? 's' : ''}.id`]: group._id, }).exec(); - let userUpdates = usersToRemoveInvitationsFrom.map(user => { + const userUpdates = usersToRemoveInvitationsFrom.map(user => { if (group.type === 'party') { removeFromArray(user.invitations.parties, { id: group._id }); user.invitations.party = user.invitations.parties.length > 0 ? user.invitations.parties[user.invitations.parties.length - 1] : {}; @@ -495,11 +508,10 @@ schema.methods.removeGroupInvitations = async function removeGroupInvitations () schema.methods.isMember = function isGroupMember (user) { if (this._id === TAVERN_ID) { return true; // everyone is considered part of the tavern - } else if (this.type === 'party') { - return user.party._id === this._id ? true : false; - } else { // guilds - return user.guilds.indexOf(this._id) !== -1; - } + } if (this.type === 'party') { + return user.party._id === this._id; + } // guilds + return user.guilds.indexOf(this._id) !== -1; }; schema.methods.getMemberCount = async function getMemberCount () { @@ -513,8 +525,10 @@ schema.methods.getMemberCount = async function getMemberCount () { }; schema.methods.sendChat = function sendChat (options = {}) { - const {message, user, metaData, client, flagCount = 0, info = {}, translate} = options; - let newMessage = messageDefaults(message, user, client, flagCount, info); + const { + message, user, metaData, client, flagCount = 0, info = {}, translate, + } = options; + const newMessage = messageDefaults(message, user, client, flagCount, info); let newChatMessage = new Chat(); newChatMessage = Object.assign(newChatMessage, newMessage); newChatMessage.groupId = this._id; @@ -549,7 +563,7 @@ schema.methods.sendChat = function sendChat (options = {}) { query.guilds = this._id; } - query._id = { $ne: user ? user._id : ''}; + query._id = { $ne: user ? user._id : '' }; // First remove the old notification (if it exists) const lastSeenUpdateRemoveOld = { @@ -561,7 +575,7 @@ schema.methods.sendChat = function sendChat (options = {}) { // Then add the new notification const lastSeenUpdateAddNew = { $set: { // old notification, supported until mobile is updated and we release api v4 - [`newMessages.${this._id}`]: {name: this.name, value: true}, + [`newMessages.${this._id}`]: { name: this.name, value: true }, }, $push: { notifications: new UserNotification({ @@ -571,8 +585,8 @@ schema.methods.sendChat = function sendChat (options = {}) { }, }; - User.update(query, lastSeenUpdateRemoveOld, {multi: true}).exec().then(() => { - User.update(query, lastSeenUpdateAddNew, {multi: true}).exec(); + User.update(query, lastSeenUpdateRemoveOld, { multi: true }).exec().then(() => { + User.update(query, lastSeenUpdateAddNew, { multi: true }).exec(); }); if (this.type === 'party' && user) { @@ -588,8 +602,8 @@ schema.methods.startQuest = async function startQuest (user) { if (!this.quest.key) throw new InternalServerError('Party does not have a pending quest'); if (this.quest.active) throw new InternalServerError('Quest is already active'); - let userIsParticipating = this.quest.members[user._id]; - let quest = questScrolls[this.quest.key]; + const userIsParticipating = this.quest.members[user._id]; + const quest = questScrolls[this.quest.key]; let collected = {}; if (quest.collect) { collected = _.transform(quest.collect, (result, n, itemToCollect) => { @@ -606,13 +620,11 @@ schema.methods.startQuest = async function startQuest (user) { this.quest.progress.collect = collected; } - let nonMembers = Object.keys(_.pickBy(this.quest.members, (member) => { - return !member; - })); + const nonMembers = Object.keys(_.pickBy(this.quest.members, member => !member)); // Changes quest.members to only include participating members this.quest.members = _.pickBy(this.quest.members, _.identity); - let nonUserQuestMembers = _.keys(this.quest.members); + const nonUserQuestMembers = _.keys(this.quest.members); removeFromArray(nonUserQuestMembers, user._id); // remove any users from quest.members who aren't in the party @@ -620,7 +632,7 @@ schema.methods.startQuest = async function startQuest (user) { const members = []; await User.find({ - _id: {$in: Object.keys(this.quest.members)}, + _id: { $in: Object.keys(this.quest.members) }, }) .select('party.quest party._id items.quests auth preferences.emailNotifications preferences.pushNotifications preferences.language pushDevices profile.name webhooks') .lean() @@ -650,7 +662,7 @@ schema.methods.startQuest = async function startQuest (user) { user.markModified('items.quests'); promises.push(user.save()); } else { // another user is starting the quest, update the leader separately - promises.push(User.update({_id: this.quest.leader}, { + promises.push(User.update({ _id: this.quest.leader }, { $inc: { [`items.quests.${this.quest.key}`]: -1, }, @@ -678,7 +690,7 @@ schema.methods.startQuest = async function startQuest (user) { { multi: true }).exec(); const newMessage = this.sendChat({ - message: `\`${shared.i18n.t('chatQuestStarted', {questName: quest.text('en')}, 'en')}\``, + message: `\`${shared.i18n.t('chatQuestStarted', { questName: quest.text('en') }, 'en')}\``, metaData: { participatingMembers: this.getParticipatingQuestMembers().join(', '), }, @@ -725,7 +737,7 @@ schema.methods.startQuest = async function startQuest (user) { }; schema.methods.sendGroupChatReceivedWebhooks = function sendGroupChatReceivedWebhooks (chat) { - let query = { + const query = { webhooks: { $elemMatch: { type: 'groupChatReceived', @@ -740,14 +752,15 @@ schema.methods.sendGroupChatReceivedWebhooks = function sendGroupChatReceivedWeb query.guilds = this._id; } - User.find(query).select({webhooks: 1}).lean().exec().then((users) => { - users.forEach((user) => { - groupChatReceivedWebhook.send(user, { - group: this, - chat, + User.find(query).select({ webhooks: 1 }).lean().exec() + .then(users => { + users.forEach(user => { + groupChatReceivedWebhook.send(user, { + group: this, + chat, + }); }); }); - }); }; schema.statics.cleanQuestParty = _cleanQuestParty; @@ -771,7 +784,7 @@ function _getUserUpdateForQuestReward (itemToAward, allAwardedItems) { $set: {}, $inc: {}, }; - let dropK = itemToAward.key; + const dropK = itemToAward.key; switch (itemToAward.type) { case 'gear': { @@ -783,7 +796,7 @@ function _getUserUpdateForQuestReward (itemToAward, allAwardedItems) { case 'food': case 'hatchingPotions': case 'quests': { - updates.$inc[`items.${itemToAward.type}.${dropK}`] = _.filter(allAwardedItems, {type: itemToAward.type, key: itemToAward.key}).length; + updates.$inc[`items.${itemToAward.type}.${dropK}`] = _.filter(allAwardedItems, { type: itemToAward.type, key: itemToAward.key }).length; break; } case 'pets': { @@ -806,17 +819,16 @@ async function _updateUserWithRetries (userId, updates, numTry = 1, query = {}) } catch (err) { if (numTry < MAX_UPDATE_RETRIES) { return _updateUserWithRetries(userId, updates, ++numTry, query); - } else { - throw err; } + throw err; } } // Participants: Grant rewards & achievements, finish quest. // Changes the group object update members schema.methods.finishQuest = async function finishQuest (quest) { - let questK = quest.key; - let updates = { + const questK = quest.key; + const updates = { $inc: { [`achievements.quests.${questK}`]: 1, 'stats.gp': Number(quest.drop.gp), @@ -828,41 +840,40 @@ schema.methods.finishQuest = async function finishQuest (quest) { if (this._id === TAVERN_ID) { updates.$set['party.quest.completed'] = questK; // Just show the notif } else { - _.merge(updates, _cleanQuestParty({$set: {'party.quest.completed': questK}})); // clear quest progress + _.merge(updates, _cleanQuestParty({ $set: { 'party.quest.completed': questK } })); // clear quest progress } - _.each(_.reject(quest.drop.items, 'onlyOwner'), (item) => { + _.each(_.reject(quest.drop.items, 'onlyOwner'), item => { _.merge(updates, _getUserUpdateForQuestReward(item, quest.drop.items)); }); - let questOwnerUpdates = {}; - let questLeader = this.quest.leader; + const questOwnerUpdates = {}; + const questLeader = this.quest.leader; - _.each(_.filter(quest.drop.items, 'onlyOwner'), (item) => { + _.each(_.filter(quest.drop.items, 'onlyOwner'), item => { _.merge(questOwnerUpdates, _getUserUpdateForQuestReward(item, quest.drop.items)); }); _.merge(questOwnerUpdates, updates); - let participants = this._id === TAVERN_ID ? {} : this.getParticipatingQuestMembers(); + const participants = this._id === TAVERN_ID ? {} : this.getParticipatingQuestMembers(); this.quest = {}; this.markModified('quest'); if (this._id === TAVERN_ID) { - return await User.update({}, updates, {multi: true}).exec(); + return await User.update({}, updates, { multi: true }).exec(); } - let promises = participants.map(userId => { + const promises = participants.map(userId => { if (userId === questLeader) { return _updateUserWithRetries(userId, questOwnerUpdates); - } else { - return _updateUserWithRetries(userId, updates); } + return _updateUserWithRetries(userId, updates); }); // Send webhooks in background // @TODO move the find users part to a worker as well, not just the http request User.find({ - _id: {$in: participants}, + _id: { $in: participants }, webhooks: { $elemMatch: { type: 'questActivity', @@ -886,16 +897,16 @@ schema.methods.finishQuest = async function finishQuest (quest) { _.forEach(questSeriesAchievements, (questList, achievement) => { if (questList.includes(questK)) { - let questAchievementQuery = {}; - questAchievementQuery[`achievements.${achievement}`] = {$ne: true}; + const questAchievementQuery = {}; + questAchievementQuery[`achievements.${achievement}`] = { $ne: true }; - _.forEach(questList, (questName) => { + _.forEach(questList, questName => { if (questName !== questK) { - questAchievementQuery[`achievements.quests.${questName}`] = {$gt: 0}; + questAchievementQuery[`achievements.quests.${questName}`] = { $gt: 0 }; } }); - let questAchievementUpdate = {$set: {}, $push: {}}; + const questAchievementUpdate = { $set: {}, $push: {} }; questAchievementUpdate.$set[`achievements.${achievement}`] = true; const achievementTitleCase = `${achievement.slice(0, 1).toUpperCase()}${achievement.slice(1, achievement.length)}`; const achievementSnakeCase = `ACHIEVEMENT_${_.snakeCase(achievement).toUpperCase()}`; @@ -910,9 +921,7 @@ schema.methods.finishQuest = async function finishQuest (quest) { }).toObject(), }; - promises.push(participants.map(userId => { - return _updateUserWithRetries(userId, questAchievementUpdate, null, questAchievementQuery); - })); + promises.push(participants.map(userId => _updateUserWithRetries(userId, questAchievementUpdate, null, questAchievementQuery))); } }); @@ -924,24 +933,24 @@ function _isOnQuest (user, progress, group) { } schema.methods._processBossQuest = async function processBossQuest (options) { - let { + const { user, progress, } = options; - let group = this; - let quest = questScrolls[group.quest.key]; - let down = progress.down * quest.boss.str; // multiply by boss strength + const group = this; + const quest = questScrolls[group.quest.key]; + const down = progress.down * quest.boss.str; // multiply by boss strength // Everyone takes damage - let updates = { - $inc: {'stats.hp': down}, + const updates = { + $inc: { 'stats.hp': down }, }; const promises = []; group.quest.progress.hp -= progress.up; if (CRON_SAFE_MODE || CRON_SEMI_SAFE_MODE) { const groupMessage = group.sendChat({ - message: `\`${shared.i18n.t('chatBossDontAttack', {bossName: quest.boss.name('en')}, 'en')}\``, + message: `\`${shared.i18n.t('chatBossDontAttack', { bossName: quest.boss.name('en') }, 'en')}\``, info: { type: 'boss_dont_attack', user: user.profile.name, @@ -952,7 +961,9 @@ schema.methods._processBossQuest = async function processBossQuest (options) { promises.push(groupMessage.save()); } else { const groupMessage = group.sendChat({ - message: `\`${shared.i18n.t('chatBossDamage', {username: user.profile.name, bossName: quest.boss.name('en'), userDamage: progress.up.toFixed(1), bossDamage: Math.abs(down).toFixed(1)}, user.preferences.language)}\``, + message: `\`${shared.i18n.t('chatBossDamage', { + username: user.profile.name, bossName: quest.boss.name('en'), userDamage: progress.up.toFixed(1), bossDamage: Math.abs(down).toFixed(1), + }, user.preferences.language)}\``, info: { type: 'boss_damage', user: user.profile.name, @@ -982,17 +993,18 @@ schema.methods._processBossQuest = async function processBossQuest (options) { if (quest.boss.rage.healing) group.quest.progress.hp += group.quest.progress.hp * quest.boss.rage.healing; if (group.quest.progress.hp > quest.boss.hp) group.quest.progress.hp = quest.boss.hp; if (quest.boss.rage.mpDrain) { - updates.$set = {'stats.mp': 0}; + updates.$set = { 'stats.mp': 0 }; } } } await User.update( - {_id: - {$in: this.getParticipatingQuestMembers()}, + { + _id: + { $in: this.getParticipatingQuestMembers() }, }, updates, - {multi: true} + { multi: true }, ).exec(); // Apply changes the currently cronning user locally so we don't have to reload it to get the updated state // TODO how to mark not modified? https://github.com/Automattic/mongoose/pull/1167 @@ -1003,7 +1015,7 @@ schema.methods._processBossQuest = async function processBossQuest (options) { // Boss slain, finish quest if (group.quest.progress.hp <= 0) { const questFinishChat = group.sendChat({ - message: `\`${shared.i18n.t('chatBossDefeated', {bossName: quest.boss.name('en')}, 'en')}\``, + message: `\`${shared.i18n.t('chatBossDefeated', { bossName: quest.boss.name('en') }, 'en')}\``, info: { type: 'boss_defeated', quest: quest.key, @@ -1020,18 +1032,16 @@ schema.methods._processBossQuest = async function processBossQuest (options) { }; schema.methods._processCollectionQuest = async function processCollectionQuest (options) { - let { + const { user, progress, } = options; - let group = this; - let quest = questScrolls[group.quest.key]; - let itemsFound = {}; + const group = this; + const quest = questScrolls[group.quest.key]; + const itemsFound = {}; - const possibleItemKeys = Object.keys(quest.collect).filter((key) => { - return group.quest.progress.collect[key] !== quest.collect[key].count; - }); + const possibleItemKeys = Object.keys(quest.collect).filter(key => group.quest.progress.collect[key] !== quest.collect[key].count); const possibleItemsToCollect = possibleItemKeys.reduce((accumulator, current, index) => { accumulator[possibleItemKeys[index]] = quest.collect[current]; @@ -1039,7 +1049,7 @@ schema.methods._processCollectionQuest = async function processCollectionQuest ( }, {}); _.times(progress.collectedItems, () => { - let item = shared.randomVal(possibleItemsToCollect, {key: true}); + const item = shared.randomVal(possibleItemsToCollect, { key: true }); if (!itemsFound[item]) { itemsFound[item] = 0; @@ -1049,7 +1059,7 @@ schema.methods._processCollectionQuest = async function processCollectionQuest ( }); // Add 0 for all items not found - Object.keys(this.quest.progress.collect).forEach((item) => { + Object.keys(this.quest.progress.collect).forEach(item => { if (!itemsFound[item]) { itemsFound[item] = 0; } @@ -1062,7 +1072,7 @@ schema.methods._processCollectionQuest = async function processCollectionQuest ( foundText = foundText.join(', '); const foundChat = group.sendChat({ - message: `\`${shared.i18n.t('chatFindItems', {username: user.profile.name, items: foundText}, 'en')}\``, + message: `\`${shared.i18n.t('chatFindItems', { username: user.profile.name, items: foundText }, 'en')}\``, info: { type: 'user_found_items', user: user.profile.name, @@ -1073,9 +1083,7 @@ schema.methods._processCollectionQuest = async function processCollectionQuest ( group.markModified('quest.progress.collect'); // Still needs completing - const needsCompleted = _.find(quest.collect, (v, k) => { - return group.quest.progress.collect[k] < v.count; - }); + const needsCompleted = _.find(quest.collect, (v, k) => group.quest.progress.collect[k] < v.count); if (needsCompleted) { return await Promise.all([group.save(), foundChat.save()]); @@ -1097,15 +1105,15 @@ schema.methods._processCollectionQuest = async function processCollectionQuest ( schema.statics.processQuestProgress = async function processQuestProgress (user, progress) { if (user.preferences.sleep) return; - let group = await this.getGroup({user, groupId: 'party'}); + const group = await this.getGroup({ user, groupId: 'party' }); if (!_isOnQuest(user, progress, group)) return; - let quest = shared.content.quests[group.quest.key]; + const quest = shared.content.quests[group.quest.key]; if (!quest) return; // TODO should this throw an error instead? - let questType = quest.boss ? 'Boss' : 'Collection'; + const questType = quest.boss ? 'Boss' : 'Collection'; await group[`_process${questType}Quest`]({ // _processBossQuest, _processCollectionQuest user, @@ -1116,8 +1124,8 @@ schema.statics.processQuestProgress = async function processQuestProgress (user, // to set a boss: `db.groups.update({_id:TAVERN_ID},{$set:{quest:{key:'dilatory',active:true,progress:{hp:1000,rage:1500}}}}).exec()` // we export an empty object that is then populated with the query-returned data -export let tavernQuest = {}; -let tavernQ = {_id: TAVERN_ID, 'quest.key': {$ne: null}}; +export const tavernQuest = {}; +const tavernQ = { _id: TAVERN_ID, 'quest.key': { $ne: null } }; // we use process.nextTick because at this point the model is not yet available process.nextTick(() => { @@ -1140,13 +1148,13 @@ schema.statics.tavernBoss = async function tavernBoss (user, progress) { if (user.preferences.sleep) return; // hack: prevent crazy damage to world boss - let dmg = Math.min(900, Math.abs(progress.up || 0)); - let rage = -Math.min(900, Math.abs(progress.down || 0)); + const dmg = Math.min(900, Math.abs(progress.up || 0)); + const rage = -Math.min(900, Math.abs(progress.down || 0)); - let tavern = await this.findOne(tavernQ).exec(); + const tavern = await this.findOne(tavernQ).exec(); if (!(tavern && tavern.quest && tavern.quest.key)) return; - let quest = shared.content.quests[tavern.quest.key]; + const quest = shared.content.quests[tavern.quest.key]; const chatPromises = []; @@ -1160,85 +1168,84 @@ schema.statics.tavernBoss = async function tavernBoss (user, progress) { }); chatPromises.push(completeChat.save()); await tavern.finishQuest(quest); - _.assign(tavernQuest, {extra: null}); + _.assign(tavernQuest, { extra: null }); return tavern.save(); - } else { - // Deal damage. Note a couple things here, str & def are calculated. If str/def are defined in the database, - // use those first - which allows us to update the boss on the go if things are too easy/hard. - if (!tavern.quest.extra) tavern.quest.extra = {}; - tavern.quest.progress.hp -= dmg / (tavern.quest.extra.def || quest.boss.def); - tavern.quest.progress.rage -= rage * (tavern.quest.extra.str || quest.boss.str); + } + // Deal damage. Note a couple things here, str & def are calculated. If str/def are defined in the database, + // use those first - which allows us to update the boss on the go if things are too easy/hard. + if (!tavern.quest.extra) tavern.quest.extra = {}; + tavern.quest.progress.hp -= dmg / (tavern.quest.extra.def || quest.boss.def); + tavern.quest.progress.rage -= rage * (tavern.quest.extra.str || quest.boss.str); - if (tavern.quest.progress.rage >= quest.boss.rage.value) { - if (!tavern.quest.extra.worldDmg) tavern.quest.extra.worldDmg = {}; + if (tavern.quest.progress.rage >= quest.boss.rage.value) { + if (!tavern.quest.extra.worldDmg) tavern.quest.extra.worldDmg = {}; - let wd = tavern.quest.extra.worldDmg; - // Dysheartener attacks Seasonal Sorceress, Alex, Ian - let scene; - if (wd.quests) { - scene = false; - } else if (wd.market) { - scene = 'quests'; - } else if (wd.seasonalShop) { - scene = 'market'; - } else { - scene = 'seasonalShop'; - } - - if (!scene) { - const tiredChat = tavern.sendChat({ - message: `\`${shared.i18n.t('tavernBossTired', {rageName: quest.boss.rage.title('en'), bossName: quest.boss.name('en')}, 'en')}\``, - info: { - type: 'tavern_boss_rage_tired', - quest: quest.key, - }, - }); - chatPromises.push(tiredChat.save()); - tavern.quest.progress.rage = 0; // quest.boss.rage.value; - } else { - const rageChat = tavern.sendChat({ - message: quest.boss.rage[scene]('en'), - info: { - type: 'tavern_boss_rage', - quest: quest.key, - scene, - }, - }); - chatPromises.push(rageChat.save()); - tavern.quest.extra.worldDmg[scene] = true; - tavern.markModified('quest.extra.worldDmg'); - tavern.quest.progress.rage = 0; - if (quest.boss.rage.healing) { - tavern.quest.progress.hp += quest.boss.rage.healing * tavern.quest.progress.hp; - } - } + const wd = tavern.quest.extra.worldDmg; + // Dysheartener attacks Seasonal Sorceress, Alex, Ian + let scene; + if (wd.quests) { + scene = false; + } else if (wd.market) { + scene = 'quests'; + } else if (wd.seasonalShop) { + scene = 'market'; + } else { + scene = 'seasonalShop'; } - if (quest.boss.desperation && tavern.quest.progress.hp < quest.boss.desperation.threshold && !tavern.quest.extra.desperate) { - const progressChat = tavern.sendChat({ - message: quest.boss.desperation.text('en'), + if (!scene) { + const tiredChat = tavern.sendChat({ + message: `\`${shared.i18n.t('tavernBossTired', { rageName: quest.boss.rage.title('en'), bossName: quest.boss.name('en') }, 'en')}\``, info: { - type: 'tavern_boss_desperation', + type: 'tavern_boss_rage_tired', quest: quest.key, }, }); - chatPromises.push(progressChat.save()); - tavern.quest.extra.desperate = true; - tavern.quest.extra.def = quest.boss.desperation.def; - tavern.quest.extra.str = quest.boss.desperation.str; - tavern.markModified('quest.extra'); + chatPromises.push(tiredChat.save()); + tavern.quest.progress.rage = 0; // quest.boss.rage.value; + } else { + const rageChat = tavern.sendChat({ + message: quest.boss.rage[scene]('en'), + info: { + type: 'tavern_boss_rage', + quest: quest.key, + scene, + }, + }); + chatPromises.push(rageChat.save()); + tavern.quest.extra.worldDmg[scene] = true; + tavern.markModified('quest.extra.worldDmg'); + tavern.quest.progress.rage = 0; + if (quest.boss.rage.healing) { + tavern.quest.progress.hp += quest.boss.rage.healing * tavern.quest.progress.hp; + } } - - _.assign(tavernQuest, tavern.quest.toObject()); - - chatPromises.unshift(tavern.save()); - return Promise.all(chatPromises); } + + if (quest.boss.desperation && tavern.quest.progress.hp < quest.boss.desperation.threshold && !tavern.quest.extra.desperate) { + const progressChat = tavern.sendChat({ + message: quest.boss.desperation.text('en'), + info: { + type: 'tavern_boss_desperation', + quest: quest.key, + }, + }); + chatPromises.push(progressChat.save()); + tavern.quest.extra.desperate = true; + tavern.quest.extra.def = quest.boss.desperation.def; + tavern.quest.extra.str = quest.boss.desperation.str; + tavern.markModified('quest.extra'); + } + + _.assign(tavernQuest, tavern.quest.toObject()); + + chatPromises.unshift(tavern.save()); + return Promise.all(chatPromises); }; schema.methods.leave = async function leaveGroup (user, keep = 'keep-all', keepChallenges = 'leave-challenges') { - let group = this; - let update = {}; + const group = this; + const update = {}; if (group.memberCount <= 1 && group.privacy === 'private' && group.hasNotCancelled()) { throw new NotAuthorized(shared.i18n.t('cannotDeleteActiveGroup')); @@ -1250,39 +1257,35 @@ schema.methods.leave = async function leaveGroup (user, keep = 'keep-all', keepC // only remove user from challenges if it's set to leave-challenges if (keepChallenges === 'leave-challenges') { - let challenges = await Challenge.find({ - _id: {$in: user.challenges}, + const challenges = await Challenge.find({ + _id: { $in: user.challenges }, group: group._id, }).exec(); - let challengesToRemoveUserFrom = challenges.map(chal => { - return chal.unlinkTasks(user, keep, false); - }); + const challengesToRemoveUserFrom = challenges.map(chal => chal.unlinkTasks(user, keep, false)); await Promise.all(challengesToRemoveUserFrom); } // Unlink group tasks - let assignedTasks = await Tasks.Task.find({ + const assignedTasks = await Tasks.Task.find({ 'group.id': group._id, - userId: {$exists: false}, + userId: { $exists: false }, 'group.assignedUsers': user._id, }).exec(); - let assignedTasksToRemoveUserFrom = assignedTasks.map(task => { - return this.unlinkTask(task, user, keep, false); - }); + const assignedTasksToRemoveUserFrom = assignedTasks.map(task => this.unlinkTask(task, user, keep, false)); await Promise.all(assignedTasksToRemoveUserFrom); // the user could be modified by calls to `unlinkTask` for challenge and group tasks // it has not been saved before to avoid multiple saves in parallel - let promises = user.isModified() ? [user.save()] : []; + const promises = user.isModified() ? [user.save()] : []; // remove the group from the user's groups if (group.type === 'guild') { - promises.push(User.update({_id: user._id}, {$pull: {guilds: group._id}}).exec()); + promises.push(User.update({ _id: user._id }, { $pull: { guilds: group._id } }).exec()); } else { - promises.push(User.update({_id: user._id}, {$set: {party: {}}}).exec()); + promises.push(User.update({ _id: user._id }, { $set: { party: {} } }).exec()); - update.$unset = {[`quest.members.${user._id}`]: 1}; + update.$unset = { [`quest.members.${user._id}`]: 1 }; } if (group.purchased.plan.customerId) { @@ -1294,34 +1297,34 @@ schema.methods.leave = async function leaveGroup (user, keep = 'keep-all', keepC // double check the member count is correct so we don't accidentally delete a group that still has users in it let members; if (group.type === 'guild') { - members = await User.find({guilds: group._id}).select('_id').exec(); + members = await User.find({ guilds: group._id }).select('_id').exec(); } else { - members = await User.find({'party._id': group._id}).select('_id').exec(); + members = await User.find({ 'party._id': group._id }).select('_id').exec(); } - _.remove(members, {_id: user._id}); + _.remove(members, { _id: user._id }); if (members.length === 0) { promises.push(group.remove()); return await Promise.all(promises); } } else if (group.leader === user._id) { // otherwise If the leader is leaving (or if the leader previously left, and this wasn't accounted for) - let query = group.type === 'party' ? {'party._id': group._id} : {guilds: group._id}; - query._id = {$ne: user._id}; - let seniorMember = await User.findOne(query).select('_id').exec(); + const query = group.type === 'party' ? { 'party._id': group._id } : { guilds: group._id }; + query._id = { $ne: user._id }; + const seniorMember = await User.findOne(query).select('_id').exec(); // could be missing in case of public guild (that can have 0 members) with 1 member who is leaving - if (seniorMember) update.$set = {leader: seniorMember._id}; + if (seniorMember) update.$set = { leader: seniorMember._id }; } // otherwise If the leader is leaving (or if the leader previously left, and this wasn't accounted for) - update.$inc = {memberCount: -1}; + update.$inc = { memberCount: -1 }; if (group.leader === user._id) { - let query = group.type === 'party' ? {'party._id': group._id} : {guilds: group._id}; - query._id = {$ne: user._id}; - let seniorMember = await User.findOne(query).select('_id').exec(); + const query = group.type === 'party' ? { 'party._id': group._id } : { guilds: group._id }; + query._id = { $ne: user._id }; + const seniorMember = await User.findOne(query).select('_id').exec(); // could be missing in case of public guild (that can have 0 members) with 1 member who is leaving - if (seniorMember) update.$set = {leader: seniorMember._id}; + if (seniorMember) update.$set = { leader: seniorMember._id }; } promises.push(group.update(update).exec()); @@ -1338,12 +1341,12 @@ schema.methods.leave = async function leaveGroup (user, keep = 'keep-all', keepC * @return The created tasks */ schema.methods.updateTask = async function updateTask (taskToSync, options = {}) { - let group = this; + const group = this; - let updateCmd = {$set: {}}; + const updateCmd = { $set: {} }; - let syncableAttributes = syncableAttrs(taskToSync); - for (let key in syncableAttributes) { + const syncableAttributes = syncableAttrs(taskToSync); + for (const key in syncableAttributes) { updateCmd.$set[key] = syncableAttributes[key]; } @@ -1351,62 +1354,62 @@ schema.methods.updateTask = async function updateTask (taskToSync, options = {}) updateCmd.$set['group.assignedUsers'] = taskToSync.group.assignedUsers; updateCmd.$set['group.sharedCompletion'] = taskToSync.group.sharedCompletion; - let taskSchema = Tasks[taskToSync.type]; + const taskSchema = Tasks[taskToSync.type]; - let updateQuery = { - userId: {$exists: true}, + const updateQuery = { + userId: { $exists: true }, 'group.id': group.id, 'group.taskId': taskToSync._id, }; if (options.newCheckListItem) { - let newCheckList = {completed: false}; + const newCheckList = { completed: false }; newCheckList.linkId = options.newCheckListItem.id; newCheckList.text = options.newCheckListItem.text; updateCmd.$push = { checklist: newCheckList }; } if (options.removedCheckListItemId) { - updateCmd.$pull = { checklist: {linkId: {$in: [options.removedCheckListItemId]} } }; + updateCmd.$pull = { checklist: { linkId: { $in: [options.removedCheckListItemId] } } }; } if (options.updateCheckListItems && options.updateCheckListItems.length > 0) { - let checkListIdsToRemove = []; - let checkListItemsToAdd = []; + const checkListIdsToRemove = []; + const checkListItemsToAdd = []; - options.updateCheckListItems.forEach(function gatherChecklists (updateCheckListItem) { + options.updateCheckListItems.forEach(updateCheckListItem => { checkListIdsToRemove.push(updateCheckListItem.id); - let newCheckList = {completed: false}; + const newCheckList = { completed: false }; newCheckList.linkId = updateCheckListItem.id; newCheckList.text = updateCheckListItem.text; checkListItemsToAdd.push(newCheckList); }); - updateCmd.$pull = { checklist: {linkId: {$in: checkListIdsToRemove} } }; - await taskSchema.update(updateQuery, updateCmd, {multi: true}).exec(); + updateCmd.$pull = { checklist: { linkId: { $in: checkListIdsToRemove } } }; + await taskSchema.update(updateQuery, updateCmd, { multi: true }).exec(); delete updateCmd.$pull; updateCmd.$push = { checklist: { $each: checkListItemsToAdd } }; - await taskSchema.update(updateQuery, updateCmd, {multi: true}).exec(); + await taskSchema.update(updateQuery, updateCmd, { multi: true }).exec(); return; } // Updating instead of loading and saving for performances, risks becoming a problem if we introduce more complexity in tasks - await taskSchema.update(updateQuery, updateCmd, {multi: true}).exec(); + await taskSchema.update(updateQuery, updateCmd, { multi: true }).exec(); }; schema.methods.syncTask = async function groupSyncTask (taskToSync, user) { - let group = this; - let toSave = []; + const group = this; + const toSave = []; if (taskToSync.group.assignedUsers.indexOf(user._id) === -1) { taskToSync.group.assignedUsers.push(user._id); } // Sync tags - let userTags = user.tags; - let i = _.findIndex(userTags, {id: group._id}); + const userTags = user.tags; + const i = _.findIndex(userTags, { id: group._id }); if (i !== -1) { if (userTags[i].name !== group.name) { @@ -1422,7 +1425,7 @@ schema.methods.syncTask = async function groupSyncTask (taskToSync, user) { }); } - let findQuery = { + const findQuery = { 'group.taskId': taskToSync._id, userId: user._id, 'group.id': group._id, @@ -1440,7 +1443,7 @@ schema.methods.syncTask = async function groupSyncTask (taskToSync, user) { } else { _.merge(matchingTask, syncableAttrs(taskToSync)); // Make sure the task is in user.tasksOrder - let orderList = user.tasksOrder[`${taskToSync.type}s`]; + const orderList = user.tasksOrder[`${taskToSync.type}s`]; if (orderList.indexOf(matchingTask._id) === -1 && (matchingTask.type !== 'todo' || !matchingTask.completed)) orderList.push(matchingTask._id); } @@ -1450,8 +1453,8 @@ schema.methods.syncTask = async function groupSyncTask (taskToSync, user) { // sync checklist if (taskToSync.checklist) { - taskToSync.checklist.forEach(function syncCheckList (element) { - let newCheckList = {completed: false}; + taskToSync.checklist.forEach(element => { + const newCheckList = { completed: false }; newCheckList.linkId = element.id; newCheckList.text = element.text; matchingTask.checklist.push(newCheckList); @@ -1466,31 +1469,31 @@ schema.methods.syncTask = async function groupSyncTask (taskToSync, user) { }; schema.methods.unlinkTask = async function groupUnlinkTask (unlinkingTask, user, keep, saveUser = true) { - let findQuery = { + const findQuery = { 'group.taskId': unlinkingTask._id, userId: user._id, }; - let assignedUserIndex = unlinkingTask.group.assignedUsers.indexOf(user._id); + const assignedUserIndex = unlinkingTask.group.assignedUsers.indexOf(user._id); unlinkingTask.group.assignedUsers.splice(assignedUserIndex, 1); if (keep === 'keep-all') { await Tasks.Task.update(findQuery, { - $set: {group: {}}, + $set: { group: {} }, }).exec(); // When multiple tasks are being unlinked at the same time, // save the user once outside of this function if (saveUser) await user.save(); } else { // keep = 'remove-all' - let task = await Tasks.Task.findOne(findQuery).select('_id type completed').exec(); + const task = await Tasks.Task.findOne(findQuery).select('_id type completed').exec(); // Remove task from user.tasksOrder and delete them if (task && (task.type !== 'todo' || !task.completed)) { removeFromArray(user.tasksOrder[`${task.type}s`], task._id); user.markModified('tasksOrder'); } - let promises = [unlinkingTask.save()]; + const promises = [unlinkingTask.save()]; if (task) { promises.push(task.remove()); } @@ -1503,28 +1506,26 @@ schema.methods.unlinkTask = async function groupUnlinkTask (unlinkingTask, user, }; schema.methods.removeTask = async function groupRemoveTask (task) { - let group = this; + const group = this; // Set the task as broken await Tasks.Task.update({ - userId: {$exists: true}, + userId: { $exists: true }, 'group.id': group.id, 'group.taskId': task._id, }, { - $set: {'group.broken': 'TASK_DELETED'}, - }, {multi: true}).exec(); + $set: { 'group.broken': 'TASK_DELETED' }, + }, { multi: true }).exec(); // Get Managers const managerIds = Object.keys(group.managers); managerIds.push(group.leader); - const managers = await User.find({_id: managerIds}, 'notifications').exec(); // Use this method so we can get access to notifications + const managers = await User.find({ _id: managerIds }, 'notifications').exec(); // Use this method so we can get access to notifications // Remove old notifications - let removalPromises = []; - managers.forEach((manager) => { - let notificationIndex = manager.notifications.findIndex(function findNotification (notification) { - return notification && notification.data && notification.data.groupTaskId === task._id && notification.type === 'GROUP_TASK_APPROVAL'; - }); + const removalPromises = []; + managers.forEach(manager => { + const notificationIndex = manager.notifications.findIndex(notification => notification && notification.data && notification.data.groupTaskId === task._id && notification.type === 'GROUP_TASK_APPROVAL'); if (notificationIndex !== -1) { manager.notifications.splice(notificationIndex, 1); @@ -1543,14 +1544,14 @@ schema.methods.removeTask = async function groupRemoveTask (task) { schema.methods.checkChatSpam = function groupCheckChatSpam (user) { if (this._id !== TAVERN_ID) { return false; - } else if (user.contributor && user.contributor.level >= SPAM_MIN_EXEMPT_CONTRIB_LEVEL) { + } if (user.contributor && user.contributor.level >= SPAM_MIN_EXEMPT_CONTRIB_LEVEL) { return false; } - let currentTime = Date.now(); + const currentTime = Date.now(); let userMessages = 0; for (let i = 0; i < this.chat.length; i++) { - let message = this.chat[i]; + const message = this.chat[i]; if (message.uuid === user._id && currentTime - message.timestamp <= SPAM_WINDOW_LENGTH) { userMessages++; if (userMessages >= SPAM_MESSAGE_LIMIT) { @@ -1565,18 +1566,18 @@ schema.methods.checkChatSpam = function groupCheckChatSpam (user) { }; schema.methods.isSubscribed = function isSubscribed () { - let now = new Date(); - let plan = this.purchased.plan; + const now = new Date(); + const { plan } = this.purchased; return plan && plan.customerId && (!plan.dateTerminated || moment(plan.dateTerminated).isAfter(now)); }; schema.methods.hasNotCancelled = function hasNotCancelled () { - let plan = this.purchased.plan; + const { plan } = this.purchased; return Boolean(this.isSubscribed() && !plan.dateTerminated); }; schema.methods.hasCancelled = function hasNotCancelled () { - let plan = this.purchased.plan; + const { plan } = this.purchased; return Boolean(this.isSubscribed() && plan.dateTerminated); }; @@ -1584,9 +1585,9 @@ schema.methods.updateGroupPlan = async function updateGroupPlan (removingMember) // Recheck the group plan count let members; if (this.type === 'guild') { - members = await User.find({guilds: this._id}).select('_id').exec(); + members = await User.find({ guilds: this._id }).select('_id').exec(); } else { - members = await User.find({'party._id': this._id}).select('_id').exec(); + members = await User.find({ 'party._id': this._id }).select('_id').exec(); } this.memberCount = members.length; @@ -1602,7 +1603,7 @@ export let model = mongoose.model('Group', schema); // initialize tavern if !exists (fresh installs) // do not run when testing as it's handled by the tests and can easily cause a race condition if (!nconf.get('IS_TEST')) { - model.count({_id: TAVERN_ID}, (err, ct) => { + model.count({ _id: TAVERN_ID }, (err, ct) => { if (err) throw err; if (ct > 0) return; new model({ // eslint-disable-line new-cap diff --git a/website/server/models/iapPurchaseReceipt.js b/website/server/models/iapPurchaseReceipt.js index 6c2e339e47..b06b3e090a 100644 --- a/website/server/models/iapPurchaseReceipt.js +++ b/website/server/models/iapPurchaseReceipt.js @@ -1,13 +1,15 @@ import mongoose from 'mongoose'; -import baseModel from '../libs/baseModel'; import validator from 'validator'; +import baseModel from '../libs/baseModel'; -const Schema = mongoose.Schema; +const { Schema } = mongoose; -export let schema = new Schema({ - _id: {$type: String, required: true}, // Use a custom string as _id - consumed: {$type: Boolean, default: false, required: true}, - userId: {$type: String, ref: 'User', required: true, validate: [v => validator.isUUID(v), 'Invalid uuid.']}, +export const schema = new Schema({ + _id: { $type: String, required: true }, // Use a custom string as _id + consumed: { $type: Boolean, default: false, required: true }, + userId: { + $type: String, ref: 'User', required: true, validate: [v => validator.isUUID(v), 'Invalid uuid.'], + }, }, { strict: true, minimize: false, // So empty objects are returned @@ -20,4 +22,4 @@ schema.plugin(baseModel, { _id: false, // using custom _id }); -export let model = mongoose.model('IapPurchaseReceipt', schema); +export const model = mongoose.model('IapPurchaseReceipt', schema); diff --git a/website/server/models/message.js b/website/server/models/message.js index 9d380fcd10..5a267d831a 100644 --- a/website/server/models/message.js +++ b/website/server/models/message.js @@ -1,32 +1,32 @@ import mongoose from 'mongoose'; -import baseModel from '../libs/baseModel'; import { v4 as uuid } from 'uuid'; import { defaults } from 'lodash'; +import baseModel from '../libs/baseModel'; const defaultSchema = () => ({ id: String, timestamp: Date, text: String, - info: {$type: mongoose.Schema.Types.Mixed}, + info: { $type: mongoose.Schema.Types.Mixed }, // sender properties user: String, // profile name (unfortunately) username: String, - contributor: {$type: mongoose.Schema.Types.Mixed}, - backer: {$type: mongoose.Schema.Types.Mixed}, + contributor: { $type: mongoose.Schema.Types.Mixed }, + backer: { $type: mongoose.Schema.Types.Mixed }, uuid: String, // sender uuid - userStyles: {$type: mongoose.Schema.Types.Mixed}, + userStyles: { $type: mongoose.Schema.Types.Mixed }, - flags: {$type: mongoose.Schema.Types.Mixed, default: {}}, - flagCount: {$type: Number, default: 0}, - likes: {$type: mongoose.Schema.Types.Mixed}, + flags: { $type: mongoose.Schema.Types.Mixed, default: {} }, + flagCount: { $type: Number, default: 0 }, + likes: { $type: mongoose.Schema.Types.Mixed }, client: String, - _meta: {$type: mongoose.Schema.Types.Mixed}, + _meta: { $type: mongoose.Schema.Types.Mixed }, }); const chatSchema = new mongoose.Schema({ ...defaultSchema(), - groupId: {$type: String, ref: 'Group'}, + groupId: { $type: String, ref: 'Group' }, }, { minimize: false, // Allow for empty flags to be saved typeKey: '$type', // So that we can use fields named `type` @@ -37,11 +37,11 @@ chatSchema.plugin(baseModel, { }); const inboxSchema = new mongoose.Schema({ - sent: {$type: Boolean, default: false}, // if the owner sent this message + sent: { $type: Boolean, default: false }, // if the owner sent this message // the uuid of the user where the message is stored, // we store two copies of each inbox messages: // one for the sender and one for the receiver - ownerId: {$type: String, ref: 'User'}, + ownerId: { $type: String, ref: 'User' }, ...defaultSchema(), }, { minimize: false, // Allow for empty flags to be saved @@ -56,16 +56,16 @@ export const chatModel = mongoose.model('Chat', chatSchema); export const inboxModel = mongoose.model('Inbox', inboxSchema); export function setUserStyles (newMessage, user) { - let userStyles = {}; - userStyles.items = {gear: {}}; + const userStyles = {}; + userStyles.items = { gear: {} }; let userCopy = user; if (user.toObject) userCopy = user.toObject(); if (userCopy.items) { userStyles.items.gear = {}; - userStyles.items.gear.costume = Object.assign({}, userCopy.items.gear.costume); - userStyles.items.gear.equipped = Object.assign({}, userCopy.items.gear.equipped); + userStyles.items.gear.costume = { ...userCopy.items.gear.costume }; + userStyles.items.gear.equipped = { ...userCopy.items.gear.equipped }; userStyles.items.currentMount = userCopy.items.currentMount; userStyles.items.currentPet = userCopy.items.currentPet; diff --git a/website/server/models/pushDevice.js b/website/server/models/pushDevice.js index d9e7d29a55..b84a16aac4 100644 --- a/website/server/models/pushDevice.js +++ b/website/server/models/pushDevice.js @@ -1,11 +1,11 @@ import mongoose from 'mongoose'; import baseModel from '../libs/baseModel'; -const Schema = mongoose.Schema; +const { Schema } = mongoose; -export let schema = new Schema({ - regId: {$type: String, required: true}, - type: {$type: String, required: true, enum: ['ios', 'android']}, +export const schema = new Schema({ + regId: { $type: String, required: true }, + type: { $type: String, required: true, enum: ['ios', 'android'] }, }, { strict: true, minimize: false, // So empty objects are returned @@ -19,4 +19,4 @@ schema.plugin(baseModel, { _id: false, }); -export let model = mongoose.model('PushDevice', schema); +export const model = mongoose.model('PushDevice', schema); diff --git a/website/server/models/subscriptionPlan.js b/website/server/models/subscriptionPlan.js index aaf2cbf148..935fabb66d 100644 --- a/website/server/models/subscriptionPlan.js +++ b/website/server/models/subscriptionPlan.js @@ -1,30 +1,30 @@ import mongoose from 'mongoose'; -import baseModel from '../libs/baseModel'; import validator from 'validator'; +import baseModel from '../libs/baseModel'; -export let schema = new mongoose.Schema({ +export const schema = new mongoose.Schema({ planId: String, subscriptionId: String, - owner: {$type: String, ref: 'User', validate: [v => validator.isUUID(v), 'Invalid uuid.']}, - quantity: {$type: Number, default: 1}, + owner: { $type: String, ref: 'User', validate: [v => validator.isUUID(v), 'Invalid uuid.'] }, + quantity: { $type: Number, default: 1 }, paymentMethod: String, // enum: ['Paypal', 'Stripe', 'Gift', 'Amazon Payments', 'Google', '']} customerId: String, // Billing Agreement Id in case of Amazon Payments dateCreated: Date, dateTerminated: Date, dateUpdated: Date, - extraMonths: {$type: Number, default: 0}, - gemsBought: {$type: Number, default: 0}, - mysteryItems: {$type: Array, default: () => []}, + extraMonths: { $type: Number, default: 0 }, + gemsBought: { $type: Number, default: 0 }, + mysteryItems: { $type: Array, default: () => [] }, lastReminderDate: Date, // indicates the last time a subscription reminder was sent lastBillingDate: Date, // Used only for Amazon Payments to keep track of billing date additionalData: mongoose.Schema.Types.Mixed, // Example for Google: {'receipt': 'serialized receipt json', 'signature': 'signature string'} nextPaymentProcessing: Date, // indicates when the queue server should process this subscription again. nextBillingDate: Date, // Next time google will bill this user. consecutive: { - count: {$type: Number, default: 0}, - offset: {$type: Number, default: 0}, // when gifted subs, offset++ for each month. offset-- each new-month (cron). count doesn't ++ until offset==0 - gemCapExtra: {$type: Number, default: 0}, - trinkets: {$type: Number, default: 0}, + count: { $type: Number, default: 0 }, + offset: { $type: Number, default: 0 }, // when gifted subs, offset++ for each month. offset-- each new-month (cron). count doesn't ++ until offset==0 + gemCapExtra: { $type: Number, default: 0 }, + trinkets: { $type: Number, default: 0 }, }, }, { strict: true, @@ -40,4 +40,4 @@ schema.plugin(baseModel, { _id: false, }); -export let model = mongoose.model('SubscriptionPlan', schema); +export const model = mongoose.model('SubscriptionPlan', schema); diff --git a/website/server/models/tag.js b/website/server/models/tag.js index 5e96037656..270472dd24 100644 --- a/website/server/models/tag.js +++ b/website/server/models/tag.js @@ -1,20 +1,20 @@ import mongoose from 'mongoose'; -import baseModel from '../libs/baseModel'; import { v4 as uuid } from 'uuid'; import validator from 'validator'; +import baseModel from '../libs/baseModel'; -const Schema = mongoose.Schema; +const { Schema } = mongoose; -export let schema = new Schema({ +export const schema = new Schema({ id: { $type: String, default: uuid, validate: [v => validator.isUUID(v), 'Invalid uuid.'], required: true, }, - name: {$type: String, required: true}, - challenge: {$type: String}, - group: {$type: String}, + name: { $type: String, required: true }, + challenge: { $type: String }, + group: { $type: String }, }, { strict: true, minimize: false, // So empty objects are returned @@ -33,4 +33,4 @@ schema.statics.sanitizeUpdate = function sanitizeUpdate (updateObj) { return this.sanitize(updateObj, noUpdate); }; -export let model = mongoose.model('Tag', schema); +export const model = mongoose.model('Tag', schema); diff --git a/website/server/models/task.js b/website/server/models/task.js index c5bc014de9..89e84da05c 100644 --- a/website/server/models/task.js +++ b/website/server/models/task.js @@ -1,37 +1,37 @@ import mongoose from 'mongoose'; -import shared from '../../common'; import validator from 'validator'; import moment from 'moment'; +import _ from 'lodash'; +import shared from '../../common'; import baseModel from '../libs/baseModel'; import { InternalServerError } from '../libs/errors'; -import _ from 'lodash'; import { preenHistory } from '../libs/preening'; import { SHARED_COMPLETION } from '../libs/groupTasks'; -const Schema = mongoose.Schema; +const { Schema } = mongoose; -let discriminatorOptions = { +const discriminatorOptions = { discriminatorKey: 'type', // the key that distinguishes task types }; -let subDiscriminatorOptions = _.defaults(_.cloneDeep(discriminatorOptions), { +const subDiscriminatorOptions = _.defaults(_.cloneDeep(discriminatorOptions), { _id: false, minimize: false, // So empty objects are returned typeKey: '$type', // So that we can use fields named `type` }); -export let tasksTypes = ['habit', 'daily', 'todo', 'reward']; +export const tasksTypes = ['habit', 'daily', 'todo', 'reward']; export const taskIsGroupOrChallengeQuery = { $and: [ // exclude challenge and group tasks { $or: [ - {'challenge.id': {$exists: false}}, - {'challenge.broken': {$exists: true}}, + { 'challenge.id': { $exists: false } }, + { 'challenge.broken': { $exists: true } }, ], }, { $or: [ - {'group.id': {$exists: false}}, - {'group.broken': {$exists: true}}, + { 'group.id': { $exists: false } }, + { 'group.broken': { $exists: true } }, ], }, ], @@ -39,9 +39,11 @@ export const taskIsGroupOrChallengeQuery = { const reminderSchema = new Schema({ _id: false, - id: {$type: String, validate: [v => validator.isUUID(v), 'Invalid uuid.'], default: shared.uuid, required: true}, - startDate: {$type: Date}, - time: {$type: Date, required: true}, + id: { + $type: String, validate: [v => validator.isUUID(v), 'Invalid uuid.'], default: shared.uuid, required: true, + }, + startDate: { $type: Date }, + time: { $type: Date, required: true }, }, { strict: true, minimize: false, // So empty objects are returned @@ -56,10 +58,12 @@ reminderSchema.plugin(baseModel, { // Important // When something changes here remember to update the client side model at common/script/libs/taskDefaults -export let TaskSchema = new Schema({ - type: {$type: String, enum: tasksTypes, required: true, default: tasksTypes[0]}, - text: {$type: String, required: true}, - notes: {$type: String, default: ''}, +export const TaskSchema = new Schema({ + type: { + $type: String, enum: tasksTypes, required: true, default: tasksTypes[0], + }, + text: { $type: String, required: true }, + notes: { $type: String, default: '' }, alias: { $type: String, match: [/^[a-zA-Z0-9-_]+$/, 'Task short names can only contain alphanumeric characters, underscores and dashes.'], @@ -81,7 +85,7 @@ export let TaskSchema = new Schema({ alias, }).exec(); - return taskDuplicated ? false : true; + return !taskDuplicated; }, msg: 'Task alias already used on another task.', }], @@ -90,47 +94,47 @@ export let TaskSchema = new Schema({ $type: String, validate: [v => validator.isUUID(v), 'Invalid uuid.'], }], - value: {$type: Number, default: 0, required: true}, // redness or cost for rewards Required because it must be settable (for rewards) + value: { $type: Number, default: 0, required: true }, // redness or cost for rewards Required because it must be settable (for rewards) priority: { $type: Number, default: 1, required: true, validate: [ - (val) => [0.1, 1, 1.5, 2].indexOf(val) !== -1, + val => [0.1, 1, 1.5, 2].indexOf(val) !== -1, 'Valid priority values are 0.1, 1, 1.5, 2.', ], }, - attribute: {$type: String, default: 'str', enum: ['str', 'con', 'int', 'per']}, - userId: {$type: String, ref: 'User', validate: [v => validator.isUUID(v), 'Invalid uuid.']}, // When not set it belongs to a challenge + attribute: { $type: String, default: 'str', enum: ['str', 'con', 'int', 'per'] }, + userId: { $type: String, ref: 'User', validate: [v => validator.isUUID(v), 'Invalid uuid.'] }, // When not set it belongs to a challenge challenge: { - shortName: {$type: String}, - id: {$type: String, ref: 'Challenge', validate: [v => validator.isUUID(v), 'Invalid uuid.']}, // When set (and userId not set) it's the original task - taskId: {$type: String, ref: 'Task', validate: [v => validator.isUUID(v), 'Invalid uuid.']}, // When not set but challenge.id defined it's the original task - broken: {$type: String, enum: ['CHALLENGE_DELETED', 'TASK_DELETED', 'UNSUBSCRIBED', 'CHALLENGE_CLOSED', 'CHALLENGE_TASK_NOT_FOUND']}, // CHALLENGE_TASK_NOT_FOUND comes from v3 migration + shortName: { $type: String }, + id: { $type: String, ref: 'Challenge', validate: [v => validator.isUUID(v), 'Invalid uuid.'] }, // When set (and userId not set) it's the original task + taskId: { $type: String, ref: 'Task', validate: [v => validator.isUUID(v), 'Invalid uuid.'] }, // When not set but challenge.id defined it's the original task + broken: { $type: String, enum: ['CHALLENGE_DELETED', 'TASK_DELETED', 'UNSUBSCRIBED', 'CHALLENGE_CLOSED', 'CHALLENGE_TASK_NOT_FOUND'] }, // CHALLENGE_TASK_NOT_FOUND comes from v3 migration winner: String, // user.profile.name of the winner }, group: { - id: {$type: String, ref: 'Group', validate: [v => validator.isUUID(v), 'Invalid uuid.']}, - broken: {$type: String, enum: ['GROUP_DELETED', 'TASK_DELETED', 'UNSUBSCRIBED']}, - assignedUsers: [{$type: String, ref: 'User', validate: [v => validator.isUUID(v), 'Invalid uuid.']}], - assignedDate: {$type: Date}, - taskId: {$type: String, ref: 'Task', validate: [v => validator.isUUID(v), 'Invalid uuid.']}, + id: { $type: String, ref: 'Group', validate: [v => validator.isUUID(v), 'Invalid uuid.'] }, + broken: { $type: String, enum: ['GROUP_DELETED', 'TASK_DELETED', 'UNSUBSCRIBED'] }, + assignedUsers: [{ $type: String, ref: 'User', validate: [v => validator.isUUID(v), 'Invalid uuid.'] }], + assignedDate: { $type: Date }, + taskId: { $type: String, ref: 'Task', validate: [v => validator.isUUID(v), 'Invalid uuid.'] }, approval: { - required: {$type: Boolean, default: false}, - approved: {$type: Boolean, default: false}, - dateApproved: {$type: Date}, - approvingUser: {$type: String, ref: 'User', validate: [v => validator.isUUID(v), 'Invalid uuid.']}, - requested: {$type: Boolean, default: false}, - requestedDate: {$type: Date}, + required: { $type: Boolean, default: false }, + approved: { $type: Boolean, default: false }, + dateApproved: { $type: Date }, + approvingUser: { $type: String, ref: 'User', validate: [v => validator.isUUID(v), 'Invalid uuid.'] }, + requested: { $type: Boolean, default: false }, + requestedDate: { $type: Date }, }, - sharedCompletion: {$type: String, enum: _.values(SHARED_COMPLETION), default: SHARED_COMPLETION.single}, + sharedCompletion: { $type: String, enum: _.values(SHARED_COMPLETION), default: SHARED_COMPLETION.single }, }, reminders: [reminderSchema], - byHabitica: {$type: Boolean, default: false}, // Flag of Tasks that were created by Habitica + byHabitica: { $type: Boolean, default: false }, // Flag of Tasks that were created by Habitica }, _.defaults({ minimize: false, // So empty objects are returned strict: true, @@ -145,7 +149,7 @@ TaskSchema.plugin(baseModel, { } if (taskObj.priority) { - let parsedFloat = Number.parseFloat(taskObj.priority); + const parsedFloat = Number.parseFloat(taskObj.priority); if (!Number.isNaN(parsedFloat)) { taskObj.priority = parsedFloat.toFixed(1); @@ -163,7 +167,7 @@ TaskSchema.statics.findByIdOrAlias = async function findByIdOrAlias (identifier, if (!identifier) throw new InternalServerError('Task identifier is a required argument'); if (!userId) throw new InternalServerError('User identifier is a required argument'); - let query = _.cloneDeep(additionalQueries); + const query = _.cloneDeep(additionalQueries); if (validator.isUUID(String(identifier))) { query._id = identifier; @@ -172,7 +176,7 @@ TaskSchema.statics.findByIdOrAlias = async function findByIdOrAlias (identifier, query.alias = identifier; } - let task = await this.findOne(query).exec(); + const task = await this.findOne(query).exec(); return task; }; @@ -202,19 +206,19 @@ TaskSchema.statics.sanitizeReminder = function sanitizeReminder (reminderObj) { }; TaskSchema.methods.scoreChallengeTask = async function scoreChallengeTask (delta, direction) { - let chalTask = this; + const chalTask = this; chalTask.value += delta; if (chalTask.type === 'habit' || chalTask.type === 'daily') { // Add only one history entry per day - const history = chalTask.history; + const { history } = chalTask; const lastChallengeHistoryIndex = history.length - 1; const lastHistoryEntry = history[lastChallengeHistoryIndex]; if ( - lastHistoryEntry && lastHistoryEntry.date && - moment().isSame(lastHistoryEntry.date, 'day') + lastHistoryEntry && lastHistoryEntry.date + && moment().isSame(lastHistoryEntry.date, 'day') ) { lastHistoryEntry.value = chalTask.value; lastHistoryEntry.date = Number(new Date()); @@ -258,46 +262,46 @@ TaskSchema.methods.scoreChallengeTask = async function scoreChallengeTask (delta export let Task = mongoose.model('Task', TaskSchema); // habits and dailies shared fields -let habitDailySchema = () => { +const habitDailySchema = () => // Schema not defined because it causes serious perf problems // date is a date stored as a Number value // value is a Number // scoredUp and scoredDown only exist for habits and are numbers - return {history: Array}; -}; + ({ history: Array }) +; // dailys and todos shared fields -let dailyTodoSchema = () => { - return { - completed: {$type: Boolean, default: false}, - // Checklist fields (dailies and todos) - collapseChecklist: {$type: Boolean, default: false}, - checklist: [{ - completed: {$type: Boolean, default: false}, - text: {$type: String, required: false, default: ''}, // required:false because it can be empty on creation - _id: false, - id: {$type: String, default: shared.uuid, required: true, validate: [v => validator.isUUID(v), 'Invalid uuid.']}, - linkId: {$type: String}, - }], - }; -}; +const dailyTodoSchema = () => ({ + completed: { $type: Boolean, default: false }, + // Checklist fields (dailies and todos) + collapseChecklist: { $type: Boolean, default: false }, + checklist: [{ + completed: { $type: Boolean, default: false }, + text: { $type: String, required: false, default: '' }, // required:false because it can be empty on creation + _id: false, + id: { + $type: String, default: shared.uuid, required: true, validate: [v => validator.isUUID(v), 'Invalid uuid.'], + }, + linkId: { $type: String }, + }], +}); -export let HabitSchema = new Schema(_.defaults({ - up: {$type: Boolean, default: true}, - down: {$type: Boolean, default: true}, - counterUp: {$type: Number, default: 0}, - counterDown: {$type: Number, default: 0}, - frequency: {$type: String, default: 'daily', enum: ['daily', 'weekly', 'monthly']}, +export const HabitSchema = new Schema(_.defaults({ + up: { $type: Boolean, default: true }, + down: { $type: Boolean, default: true }, + counterUp: { $type: Number, default: 0 }, + counterDown: { $type: Number, default: 0 }, + frequency: { $type: String, default: 'daily', enum: ['daily', 'weekly', 'monthly'] }, }, habitDailySchema()), subDiscriminatorOptions); -export let habit = Task.discriminator('habit', HabitSchema); +export const habit = Task.discriminator('habit', HabitSchema); -export let DailySchema = new Schema(_.defaults({ - frequency: {$type: String, default: 'weekly', enum: ['daily', 'weekly', 'monthly', 'yearly']}, +export const DailySchema = new Schema(_.defaults({ + frequency: { $type: String, default: 'weekly', enum: ['daily', 'weekly', 'monthly', 'yearly'] }, everyX: { $type: Number, default: 1, validate: [ - (val) => val % 1 === 0 && val >= 0 && val <= 9999, + val => val % 1 === 0 && val >= 0 && val <= 9999, 'Valid everyX values are integers from 0 to 9999', ], }, @@ -309,29 +313,29 @@ export let DailySchema = new Schema(_.defaults({ required: true, }, repeat: { // used only for 'weekly' frequency, - m: {$type: Boolean, default: true}, - t: {$type: Boolean, default: true}, - w: {$type: Boolean, default: true}, - th: {$type: Boolean, default: true}, - f: {$type: Boolean, default: true}, - s: {$type: Boolean, default: true}, - su: {$type: Boolean, default: true}, + m: { $type: Boolean, default: true }, + t: { $type: Boolean, default: true }, + w: { $type: Boolean, default: true }, + th: { $type: Boolean, default: true }, + f: { $type: Boolean, default: true }, + s: { $type: Boolean, default: true }, + su: { $type: Boolean, default: true }, }, - streak: {$type: Number, default: 0}, - daysOfMonth: {$type: [Number], default: []}, // Days of the month that the daily should repeat on - weeksOfMonth: {$type: [Number], default: []}, // Weeks of the month that the daily should repeat on - isDue: {$type: Boolean}, - nextDue: [{$type: String}], - yesterDaily: {$type: Boolean, default: true, required: true}, + streak: { $type: Number, default: 0 }, + daysOfMonth: { $type: [Number], default: [] }, // Days of the month that the daily should repeat on + weeksOfMonth: { $type: [Number], default: [] }, // Weeks of the month that the daily should repeat on + isDue: { $type: Boolean }, + nextDue: [{ $type: String }], + yesterDaily: { $type: Boolean, default: true, required: true }, }, habitDailySchema(), dailyTodoSchema()), subDiscriminatorOptions); -export let daily = Task.discriminator('daily', DailySchema); +export const daily = Task.discriminator('daily', DailySchema); -export let TodoSchema = new Schema(_.defaults({ +export const TodoSchema = new Schema(_.defaults({ dateCompleted: Date, // TODO we're getting parse errors, people have stored as "today" and "3/13". Need to run a migration & put this back to $type: Date see http://stackoverflow.com/questions/1353684/detecting-an-invalid-date-date-instance-in-javascript date: String, // due date for todos }, dailyTodoSchema()), subDiscriminatorOptions); -export let todo = Task.discriminator('todo', TodoSchema); +export const todo = Task.discriminator('todo', TodoSchema); -export let RewardSchema = new Schema({}, subDiscriminatorOptions); -export let reward = Task.discriminator('reward', RewardSchema); +export const RewardSchema = new Schema({}, subDiscriminatorOptions); +export const reward = Task.discriminator('reward', RewardSchema); diff --git a/website/server/models/user/hooks.js b/website/server/models/user/hooks.js index 676231ef59..4dbab44a8c 100644 --- a/website/server/models/user/hooks.js +++ b/website/server/models/user/hooks.js @@ -1,6 +1,6 @@ -import common from '../../../common'; import _ from 'lodash'; import moment from 'moment'; +import common from '../../../common'; import baseModel from '../../libs/baseModel'; import * as Tasks from '../task'; import { @@ -33,9 +33,7 @@ schema.plugin(baseModel, { }); function findTag (user, tagName) { - let tagID = _.find(user.tags, (userTag) => { - return userTag.name === tagName(user.preferences.language); - }); + const tagID = _.find(user.tags, userTag => userTag.name === tagName(user.preferences.language)); return tagID.id; } @@ -47,11 +45,11 @@ function _populateDefaultTasks (user, taskTypes) { } else { defaultsData = common.content.userDefaults; } - let tagsI = taskTypes.indexOf('tag'); + const tagsI = taskTypes.indexOf('tag'); if (tagsI !== -1) { - user.tags = _.map(defaultsData.tags, (tag) => { - let newTag = _.cloneDeep(tag); + user.tags = _.map(defaultsData.tags, tag => { + const newTag = _.cloneDeep(tag); // tasks automatically get _id=helpers.uuid() from TaskSchema id.default, but tags are Schema.Types.Mixed - so we need to manually invoke here newTag.id = common.uuid(); @@ -63,7 +61,7 @@ function _populateDefaultTasks (user, taskTypes) { // @TODO: default tasks are handled differently now, and not during registration. We should move this code - let tasksToCreate = []; + const tasksToCreate = []; if (user.registeredThrough === 'habitica-web') return Promise.all(tasksToCreate); if (tagsI !== -1) { @@ -71,15 +69,15 @@ function _populateDefaultTasks (user, taskTypes) { taskTypes.splice(tagsI, 1); } - _.each(taskTypes, (taskType) => { - let tasksOfType = _.map(defaultsData[`${taskType}s`], (taskDefaults) => { - let newTask = new Tasks[taskType](taskDefaults); + _.each(taskTypes, taskType => { + const tasksOfType = _.map(defaultsData[`${taskType}s`], taskDefaults => { + const newTask = new Tasks[taskType](taskDefaults); newTask.userId = user._id; newTask.text = taskDefaults.text(user.preferences.language); if (newTask.notes) newTask.notes = taskDefaults.notes(user.preferences.language); if (taskDefaults.checklist) { - newTask.checklist = _.map(taskDefaults.checklist, (checklistItem) => { + newTask.checklist = _.map(taskDefaults.checklist, checklistItem => { checklistItem.text = checklistItem.text(user.preferences.language); return checklistItem; }); @@ -96,8 +94,8 @@ function _populateDefaultTasks (user, taskTypes) { }); return Promise.all(tasksToCreate) - .then((tasksCreated) => { - _.each(tasksCreated, (task) => { + .then(tasksCreated => { + _.each(tasksCreated, task => { user.tasksOrder[`${task.type}s`].push(task._id); }); }); @@ -115,14 +113,14 @@ function pinBaseItems (user) { })); user.pinnedItems.push( - {type: 'potion', path: 'potion'}, - {type: 'armoire', path: 'armoire'}, + { type: 'potion', path: 'potion' }, + { type: 'armoire', path: 'armoire' }, ); } function _setUpNewUser (user) { let taskTypes; - let iterableFlags = user.flags.toObject(); + const iterableFlags = user.flags.toObject(); user.items.quests.dustbunnies = 1; user.markModified('items.quests'); @@ -155,8 +153,8 @@ function _setUpNewUser (user) { } function _setProfileName (user) { - let localUsername = user.auth.local && user.auth.local.username; - let anonymous = 'profile name not found'; + const localUsername = user.auth.local && user.auth.local.username; + const anonymous = 'profile name not found'; return localUsername || anonymous; } @@ -186,22 +184,22 @@ schema.pre('save', true, function preSaveUser (next, done) { // do not calculate achievements if items or achievements are not selected if (this.isDirectSelected('items') && this.isDirectSelected('achievements')) { // Determines if Beast Master should be awarded - let beastMasterProgress = common.count.beastMasterProgress(this.items.pets); + const beastMasterProgress = common.count.beastMasterProgress(this.items.pets); if (beastMasterProgress >= 90 || this.achievements.beastMasterCount > 0) { this.achievements.beastMaster = true; } // Determines if Mount Master should be awarded - let mountMasterProgress = common.count.mountMasterProgress(this.items.mounts); + const mountMasterProgress = common.count.mountMasterProgress(this.items.mounts); if (mountMasterProgress >= 90 || this.achievements.mountMasterCount > 0) { this.achievements.mountMaster = true; } // Determines if Triad Bingo should be awarded - let dropPetCount = common.count.dropPetsCurrentlyOwned(this.items.pets); - let qualifiesForTriad = dropPetCount >= 90 && mountMasterProgress >= 90; + const dropPetCount = common.count.dropPetsCurrentlyOwned(this.items.pets); + const qualifiesForTriad = dropPetCount >= 90 && mountMasterProgress >= 90; if (qualifiesForTriad || this.achievements.triadBingoCount > 0) { this.achievements.triadBingo = true; @@ -216,10 +214,10 @@ schema.pre('save', true, function preSaveUser (next, done) { // Filter notifications, remove unvalid and not necessary, handle the ones that have special requirements if ( // Make sure all the data is loaded - this.isDirectSelected('notifications') && - this.isDirectSelected('stats') && - this.isDirectSelected('flags') && - this.isDirectSelected('preferences') + this.isDirectSelected('notifications') + && this.isDirectSelected('stats') + && this.isDirectSelected('flags') + && this.isDirectSelected('preferences') ) { const unallocatedPointsNotifications = []; @@ -289,7 +287,7 @@ schema.pre('save', true, function preSaveUser (next, done) { }); schema.pre('update', function preUpdateUser () { - this.update({}, {$inc: {_v: 1}}); + this.update({}, { $inc: { _v: 1 } }); }); schema.post('save', function postSaveUser () { @@ -299,7 +297,7 @@ schema.post('save', function postSaveUser () { const firstLvlNotification = lvlUpNotifications[0]; const lastLvlNotification = lvlUpNotifications[lvlUpNotifications.length - 1]; - const initialLvl = firstLvlNotification.initialLvl; + const { initialLvl } = firstLvlNotification; const finalLvl = lastLvlNotification.newLvl; userActivityWebhook.send(this, { diff --git a/website/server/models/user/index.js b/website/server/models/user/index.js index 2b7ddd3097..8412cb7d0c 100644 --- a/website/server/models/user/index.js +++ b/website/server/models/user/index.js @@ -6,29 +6,29 @@ import './hooks'; import './methods'; // A list of publicly accessible fields (not everything from preferences because there are also a lot of settings tha should remain private) -export let publicFields = `preferences.size preferences.hair preferences.skin preferences.shirt +export const publicFields = `preferences.size preferences.hair preferences.skin preferences.shirt preferences.chair preferences.costume preferences.sleep preferences.background preferences.tasks preferences.disableClasses profile stats achievements party backer contributor auth.timestamps items inbox.optOut loginIncentives flags.classSelected flags.verifiedUsername auth.local.username`; // The minimum amount of data needed when populating multiple users -export let nameFields = 'profile.name auth.local.username flags.verifiedUsername'; +export const nameFields = 'profile.name auth.local.username flags.verifiedUsername'; export { schema }; -export let model = mongoose.model('User', schema); +export const model = mongoose.model('User', schema); // Initially export an empty object so external requires will get // the right object by reference when it's defined later // Otherwise it would remain undefined if requested before the query executes -export let mods = []; +export const mods = []; mongoose.model('User') - .find({'contributor.admin': true}) + .find({ 'contributor.admin': true }) .sort('-contributor.level -backer.npc profile.name') .select('profile contributor backer') .exec() - .then((foundMods) => { + .then(foundMods => { // Using push to maintain the reference to mods mods.push(...foundMods); }); diff --git a/website/server/models/user/methods.js b/website/server/models/user/methods.js index 5a49fea5a0..4d226c8ba4 100644 --- a/website/server/models/user/methods.js +++ b/website/server/models/user/methods.js @@ -1,4 +1,7 @@ import moment from 'moment'; +import { + defaults, map, flatten, flow, compact, uniq, partialRight, +} from 'lodash'; import common from '../../../common'; import { @@ -12,7 +15,6 @@ import { inboxModel as Inbox, } from '../message'; -import { defaults, map, flatten, flow, compact, uniq, partialRight } from 'lodash'; import { model as UserNotification } from '../userNotification'; import schema from './schema'; import payments from '../../libs/payments/payments'; @@ -21,27 +23,27 @@ import amazonPayments from '../../libs/payments/amazon'; import stripePayments from '../../libs/payments/stripe'; import paypalPayments from '../../libs/payments/paypal'; -const daysSince = common.daysSince; +const { daysSince } = common; schema.methods.isSubscribed = function isSubscribed () { const now = new Date(); - const plan = this.purchased.plan; + const { plan } = this.purchased; return plan && plan.customerId && (!plan.dateTerminated || moment(plan.dateTerminated).isAfter(now)); }; schema.methods.hasNotCancelled = function hasNotCancelled () { - let plan = this.purchased.plan; + const { plan } = this.purchased; return Boolean(this.isSubscribed() && !plan.dateTerminated); }; schema.methods.hasCancelled = function hasCancelled () { - let plan = this.purchased.plan; + const { plan } = this.purchased; return Boolean(this.isSubscribed() && plan.dateTerminated); }; // Get an array of groups ids the user is member of schema.methods.getGroups = function getUserGroups () { - let userGroups = this.guilds.slice(0); // clone this.guilds so we don't modify the original + const userGroups = this.guilds.slice(0); // clone this.guilds so we don't modify the original if (this.party._id) userGroups.push(this.party._id); userGroups.push(TAVERN_ID); return userGroups; @@ -85,19 +87,19 @@ schema.methods.getObjectionsToInteraction = function getObjectionsToInteraction throw new Error(`Unknown kind of interaction: "${interaction}", expected one of ${KNOWN_INTERACTIONS.join(', ')}`); } - let sender = this; - let checks = [ + const sender = this; + const checks = [ INTERACTION_CHECKS.always, INTERACTION_CHECKS[interaction], ]; - let executeChecks = partialRight(map, (check) => check(sender, receiver)); + const executeChecks = partialRight(map, check => check(sender, receiver)); return flow( flatten, executeChecks, compact, // Remove passed checks (passed checks return falsy; failed checks return message keys) - uniq + uniq, )(checks); }; @@ -115,7 +117,7 @@ schema.methods.sendMessage = async function sendMessage (userToReceiveMessage, o const sender = this; const senderMsg = options.senderMsg || options.receiverMsg; // whether to save users after sending the message, defaults to true - const saveUsers = options.save === false ? false : true; + const saveUsers = options.save !== false; const newReceiverMessage = new Inbox({ ownerId: userToReceiveMessage._id, @@ -202,14 +204,14 @@ schema.methods.addNotification = function addUserNotification (type, data = {}, * @param data The data to add to the notification */ schema.statics.pushNotification = async function pushNotification (query, type, data = {}, seen = false) { - let newNotification = new UserNotification({type, data, seen}); + const newNotification = new UserNotification({ type, data, seen }); - let validationResult = newNotification.validateSync(); + const validationResult = newNotification.validateSync(); if (validationResult) { throw validationResult; } - await this.update(query, {$push: {notifications: newNotification.toObject()}}, {multi: true}).exec(); + await this.update(query, { $push: { notifications: newNotification.toObject() } }, { multi: true }).exec(); }; // Static method to add/remove properties to a JSON User object, @@ -255,14 +257,14 @@ schema.statics.addComputedStatsToJSONObj = function addComputedStatsToUserJSONOb // In summary, currently is is best practice to use this method to cancel a user subscription, rather than calling the // payment helper. schema.methods.cancelSubscription = async function cancelSubscription (options = {}) { - let plan = this.purchased.plan; + const { plan } = this.purchased; options.user = this; if (plan.paymentMethod === amazonPayments.constants.PAYMENT_METHOD) { return await amazonPayments.cancelSubscription(options); - } else if (plan.paymentMethod === stripePayments.constants.PAYMENT_METHOD) { + } if (plan.paymentMethod === stripePayments.constants.PAYMENT_METHOD) { return await stripePayments.cancelSubscription(options); - } else if (plan.paymentMethod === paypalPayments.constants.PAYMENT_METHOD) { + } if (plan.paymentMethod === paypalPayments.constants.PAYMENT_METHOD) { return await paypalPayments.subscribeCancel(options); } // Android and iOS subscriptions cannot be cancelled by Habitica. @@ -276,7 +278,7 @@ schema.methods.daysUserHasMissed = function daysUserHasMissed (now, req = {}) { // both timezones to work out if cron should run. // CDS = Custom Day Start time. let timezoneOffsetFromUserPrefs = this.preferences.timezoneOffset; - let timezoneOffsetAtLastCron = isFinite(this.preferences.timezoneOffsetAtLastCron) ? this.preferences.timezoneOffsetAtLastCron : timezoneOffsetFromUserPrefs; + const timezoneOffsetAtLastCron = isFinite(this.preferences.timezoneOffsetAtLastCron) ? this.preferences.timezoneOffsetAtLastCron : timezoneOffsetFromUserPrefs; let timezoneOffsetFromBrowser = typeof req.header === 'function' && Number(req.header('x-user-timezoneoffset')); timezoneOffsetFromBrowser = isFinite(timezoneOffsetFromBrowser) ? timezoneOffsetFromBrowser : timezoneOffsetFromUserPrefs; // NB: All timezone offsets can be 0, so can't use `... || ...` to apply non-zero defaults @@ -289,7 +291,7 @@ schema.methods.daysUserHasMissed = function daysUserHasMissed (now, req = {}) { } // How many days have we missed using the user's current timezone: - let daysMissed = daysSince(this.lastCron, defaults({now}, this.preferences)); + let daysMissed = daysSince(this.lastCron, defaults({ now }, this.preferences)); if (timezoneOffsetAtLastCron !== timezoneOffsetFromUserPrefs) { // Give the user extra time based on the difference in timezones @@ -300,8 +302,8 @@ schema.methods.daysUserHasMissed = function daysUserHasMissed (now, req = {}) { // Since cron last ran, the user's timezone has changed. // How many days have we missed using the old timezone: - let daysMissedNewZone = daysMissed; - let daysMissedOldZone = daysSince(this.lastCron, defaults({ + const daysMissedNewZone = daysMissed; + const daysMissedOldZone = daysSince(this.lastCron, defaults({ now, timezoneOffsetOverride: timezoneOffsetAtLastCron, }, this.preferences)); @@ -335,7 +337,7 @@ schema.methods.daysUserHasMissed = function daysUserHasMissed (now, req = {}) { // timezone interprets as being in today. daysMissed = 0; // prevent cron running now - let timezoneOffsetDiff = timezoneOffsetAtLastCron - timezoneOffsetFromUserPrefs; + const timezoneOffsetDiff = timezoneOffsetAtLastCron - timezoneOffsetFromUserPrefs; // e.g., for dangerous zone change: 240 - 300 = -60 or -660 - -600 = -60 this.lastCron = moment(this.lastCron).subtract(timezoneOffsetDiff, 'minutes'); @@ -355,7 +357,7 @@ schema.methods.daysUserHasMissed = function daysUserHasMissed (now, req = {}) { } } - return {daysMissed, timezoneOffsetFromUserPrefs}; + return { daysMissed, timezoneOffsetFromUserPrefs }; }; async function getUserGroupData (user) { @@ -363,7 +365,7 @@ async function getUserGroupData (user) { const groups = await Group .find({ - _id: {$in: userGroups}, + _id: { $in: userGroups }, }) .select('leaderOnly leader purchased') .exec(); @@ -376,7 +378,7 @@ async function getUserGroupData (user) { // its the group leader schema.methods.canGetGems = async function canObtainGems () { const user = this; - const plan = user.purchased.plan; + const { plan } = user.purchased; if (!user.isSubscribed() || plan.customerId !== payments.constants.GROUP_PLAN_CUSTOMER_ID) { return true; @@ -384,17 +386,13 @@ schema.methods.canGetGems = async function canObtainGems () { const groups = await getUserGroupData(user); - return groups.every(g => { - return !g.isSubscribed() || g.leader === user._id || g.leaderOnly.getGems !== true; - }); + return groups.every(g => !g.isSubscribed() || g.leader === user._id || g.leaderOnly.getGems !== true); }; schema.methods.isMemberOfGroupPlan = async function isMemberOfGroupPlan () { const groups = await getUserGroupData(this); - return groups.some(g => { - return g.isSubscribed(); - }); + return groups.some(g => g.isSubscribed()); }; schema.methods.isAdmin = function isAdmin () { @@ -408,7 +406,7 @@ schema.methods.toJSONWithInbox = async function userToJSONWithInbox () { const toJSON = user.toJSON(); if (toJSON.inbox) { - toJSON.inbox.messages = await inboxLib.getUserInbox(user, {asArray: false}); + toJSON.inbox.messages = await inboxLib.getUserInbox(user, { asArray: false }); } return toJSON; diff --git a/website/server/models/user/schema.js b/website/server/models/user/schema.js index 7d36bec72c..ac98e37924 100644 --- a/website/server/models/user/schema.js +++ b/website/server/models/user/schema.js @@ -1,6 +1,6 @@ import mongoose from 'mongoose'; -import shared from '../../../common'; import validator from 'validator'; +import shared from '../../../common'; import { schema as TagSchema } from '../tag'; import { schema as PushDeviceSchema } from '../pushDevice'; import { schema as WebhookSchema } from '../webhook'; @@ -14,7 +14,7 @@ import { getDefaultOwnedGear, } from '../../libs/items/utils'; -const Schema = mongoose.Schema; +const { Schema } = mongoose; const RESTRICTED_EMAIL_DOMAINS = Object.freeze(['habitica.com', 'habitrpg.com']); @@ -27,27 +27,27 @@ export default new Schema({ auth: { blocked: Boolean, - facebook: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, - google: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, + facebook: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, + google: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, local: { email: { $type: String, validate: [{ - validator: (v) => validator.isEmail(v), + validator: v => validator.isEmail(v), message: shared.i18n.t('invalidEmail'), }, { validator (email) { - let lowercaseEmail = email.toLowerCase(); + const lowercaseEmail = email.toLowerCase(); - return RESTRICTED_EMAIL_DOMAINS.every((domain) => { - return !lowercaseEmail.endsWith(`@${domain}`); - }); + return RESTRICTED_EMAIL_DOMAINS.every(domain => !lowercaseEmail.endsWith(`@${domain}`)); }, - message: shared.i18n.t('invalidEmailDomain', { domains: RESTRICTED_EMAIL_DOMAINS.join(', ')}), + message: shared.i18n.t('invalidEmailDomain', { domains: RESTRICTED_EMAIL_DOMAINS.join(', ') }), }], }, username: { @@ -66,9 +66,9 @@ export default new Schema({ passwordResetCode: String, }, timestamps: { - created: {$type: Date, default: Date.now}, - loggedin: {$type: Date, default: Date.now}, - updated: {$type: Date, default: Date.now}, + created: { $type: Date, default: Date.now }, + loggedin: { $type: Date, default: Date.now }, + updated: { $type: Date, default: Date.now }, }, }, // We want to know *every* time an object updates. Mongoose uses __v to designate when an object contains arrays which @@ -79,10 +79,10 @@ export default new Schema({ originalUser: Boolean, habitSurveys: Number, ultimateGearSets: { - healer: {$type: Boolean, default: false}, - wizard: {$type: Boolean, default: false}, - rogue: {$type: Boolean, default: false}, - warrior: {$type: Boolean, default: false}, + healer: { $type: Boolean, default: false }, + wizard: { $type: Boolean, default: false }, + rogue: { $type: Boolean, default: false }, + warrior: { $type: Boolean, default: false }, }, beastMaster: Boolean, beastMasterCount: Number, @@ -95,14 +95,15 @@ export default new Schema({ spookySparkles: Number, shinySeed: Number, seafoam: Number, - streak: {$type: Number, default: 0}, + streak: { $type: Number, default: 0 }, challenges: Array, - quests: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, + quests: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, rebirths: Number, rebirthLevel: Number, - perfect: {$type: Number, default: 0}, + perfect: { $type: Number, default: 0 }, habitBirthdays: Number, valentine: Number, nye: Number, @@ -152,108 +153,114 @@ export default new Schema({ critical: String, }, - balance: {$type: Number, default: 0}, + balance: { $type: Number, default: 0 }, purchased: { - ads: {$type: Boolean, default: false}, + ads: { $type: Boolean, default: false }, // eg, {skeleton: true, pumpkin: true, eb052b: true} - skin: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, - hair: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, - shirt: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, - background: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, - txnCount: {$type: Number, default: 0}, + skin: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, + hair: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, + shirt: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, + background: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, + txnCount: { $type: Number, default: 0 }, mobileChat: Boolean, - plan: {$type: SubscriptionPlanSchema, default: () => { - return {}; - }}, + plan: { + $type: SubscriptionPlanSchema, + default: () => ({}), + }, }, flags: { - customizationsNotification: {$type: Boolean, default: false}, - showTour: {$type: Boolean, default: true}, + customizationsNotification: { $type: Boolean, default: false }, + showTour: { $type: Boolean, default: true }, tour: { // -1 indicates "uninitiated", -2 means "complete", any other number is the current tour step (0-index) - intro: {$type: Number, default: -1}, - classes: {$type: Number, default: -1}, - stats: {$type: Number, default: -1}, - tavern: {$type: Number, default: -1}, - party: {$type: Number, default: -1}, - guilds: {$type: Number, default: -1}, - challenges: {$type: Number, default: -1}, - market: {$type: Number, default: -1}, - pets: {$type: Number, default: -1}, - mounts: {$type: Number, default: -1}, - hall: {$type: Number, default: -1}, - equipment: {$type: Number, default: -1}, + intro: { $type: Number, default: -1 }, + classes: { $type: Number, default: -1 }, + stats: { $type: Number, default: -1 }, + tavern: { $type: Number, default: -1 }, + party: { $type: Number, default: -1 }, + guilds: { $type: Number, default: -1 }, + challenges: { $type: Number, default: -1 }, + market: { $type: Number, default: -1 }, + pets: { $type: Number, default: -1 }, + mounts: { $type: Number, default: -1 }, + hall: { $type: Number, default: -1 }, + equipment: { $type: Number, default: -1 }, }, tutorial: { common: { - habits: {$type: Boolean, default: false}, - dailies: {$type: Boolean, default: false}, - todos: {$type: Boolean, default: false}, - rewards: {$type: Boolean, default: false}, - party: {$type: Boolean, default: false}, - pets: {$type: Boolean, default: false}, - gems: {$type: Boolean, default: false}, - skills: {$type: Boolean, default: false}, - classes: {$type: Boolean, default: false}, - tavern: {$type: Boolean, default: false}, - equipment: {$type: Boolean, default: false}, - items: {$type: Boolean, default: false}, - mounts: {$type: Boolean, default: false}, - inbox: {$type: Boolean, default: false}, - stats: {$type: Boolean, default: false}, + habits: { $type: Boolean, default: false }, + dailies: { $type: Boolean, default: false }, + todos: { $type: Boolean, default: false }, + rewards: { $type: Boolean, default: false }, + party: { $type: Boolean, default: false }, + pets: { $type: Boolean, default: false }, + gems: { $type: Boolean, default: false }, + skills: { $type: Boolean, default: false }, + classes: { $type: Boolean, default: false }, + tavern: { $type: Boolean, default: false }, + equipment: { $type: Boolean, default: false }, + items: { $type: Boolean, default: false }, + mounts: { $type: Boolean, default: false }, + inbox: { $type: Boolean, default: false }, + stats: { $type: Boolean, default: false }, }, ios: { - addTask: {$type: Boolean, default: false}, - editTask: {$type: Boolean, default: false}, - deleteTask: {$type: Boolean, default: false}, - filterTask: {$type: Boolean, default: false}, - groupPets: {$type: Boolean, default: false}, - inviteParty: {$type: Boolean, default: false}, - reorderTask: {$type: Boolean, default: false}, + addTask: { $type: Boolean, default: false }, + editTask: { $type: Boolean, default: false }, + deleteTask: { $type: Boolean, default: false }, + filterTask: { $type: Boolean, default: false }, + groupPets: { $type: Boolean, default: false }, + inviteParty: { $type: Boolean, default: false }, + reorderTask: { $type: Boolean, default: false }, }, }, - dropsEnabled: {$type: Boolean, default: false}, - itemsEnabled: {$type: Boolean, default: false}, - newStuff: {$type: Boolean, default: false}, - rewrite: {$type: Boolean, default: true}, - classSelected: {$type: Boolean, default: false}, + dropsEnabled: { $type: Boolean, default: false }, + itemsEnabled: { $type: Boolean, default: false }, + newStuff: { $type: Boolean, default: false }, + rewrite: { $type: Boolean, default: true }, + classSelected: { $type: Boolean, default: false }, mathUpdates: Boolean, - rebirthEnabled: {$type: Boolean, default: false}, + rebirthEnabled: { $type: Boolean, default: false }, lastFreeRebirth: Date, - levelDrops: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, + levelDrops: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, chatRevoked: Boolean, chatShadowMuted: Boolean, // Used to track the status of recapture emails sent to each user, // can be 0 - no email sent - 1, 2, 3 or 4 - 4 means no more email will be sent to the user - recaptureEmailsPhase: {$type: Number, default: 0}, + recaptureEmailsPhase: { $type: Number, default: 0 }, // Needed to track the tip to send inside the email - weeklyRecapEmailsPhase: {$type: Number, default: 0}, + weeklyRecapEmailsPhase: { $type: Number, default: 0 }, // Used to track when the next weekly recap should be sent - lastWeeklyRecap: {$type: Date, default: Date.now}, + lastWeeklyRecap: { $type: Date, default: Date.now }, // Used to enable weekly recap emails as users login lastWeeklyRecapDiscriminator: Boolean, onboardingEmailsPhase: String, // Keep track of the latest onboarding email sent - communityGuidelinesAccepted: {$type: Boolean, default: false}, - cronCount: {$type: Number, default: 0}, - welcomed: {$type: Boolean, default: false}, - armoireEnabled: {$type: Boolean, default: true}, - armoireOpened: {$type: Boolean, default: false}, - armoireEmpty: {$type: Boolean, default: false}, - cardReceived: {$type: Boolean, default: false}, - warnedLowHealth: {$type: Boolean, default: false}, - verifiedUsername: {$type: Boolean, default: false}, + communityGuidelinesAccepted: { $type: Boolean, default: false }, + cronCount: { $type: Number, default: 0 }, + welcomed: { $type: Boolean, default: false }, + armoireEnabled: { $type: Boolean, default: true }, + armoireOpened: { $type: Boolean, default: false }, + armoireEmpty: { $type: Boolean, default: false }, + cardReceived: { $type: Boolean, default: false }, + warnedLowHealth: { $type: Boolean, default: false }, + verifiedUsername: { $type: Boolean, default: false }, }, history: { @@ -265,16 +272,14 @@ export default new Schema({ gear: { owned: { $type: Schema.Types.Mixed, - default: () => { - return getDefaultOwnedGear(); - }, + default: () => getDefaultOwnedGear(), }, equipped: { weapon: String, - armor: {$type: String, default: 'armor_base_0'}, - head: {$type: String, default: 'head_base_0'}, - shield: {$type: String, default: 'shield_base_0'}, + armor: { $type: String, default: 'armor_base_0' }, + head: { $type: String, default: 'head_base_0' }, + shield: { $type: String, default: 'shield_base_0' }, back: String, headAccessory: String, eyewear: String, @@ -282,9 +287,9 @@ export default new Schema({ }, costume: { weapon: String, - armor: {$type: String, default: 'armor_base_0'}, - head: {$type: String, default: 'head_base_0'}, - shield: {$type: String, default: 'shield_base_0'}, + armor: { $type: String, default: 'armor_base_0' }, + head: { $type: String, default: 'head_base_0' }, + shield: { $type: String, default: 'shield_base_0' }, back: String, headAccessory: String, eyewear: String, @@ -293,25 +298,25 @@ export default new Schema({ }, special: { - snowball: {$type: Number, default: 0}, - spookySparkles: {$type: Number, default: 0}, - shinySeed: {$type: Number, default: 0}, - seafoam: {$type: Number, default: 0}, - valentine: {$type: Number, default: 0}, + snowball: { $type: Number, default: 0 }, + spookySparkles: { $type: Number, default: 0 }, + shinySeed: { $type: Number, default: 0 }, + seafoam: { $type: Number, default: 0 }, + valentine: { $type: Number, default: 0 }, valentineReceived: Array, // array of strings, by sender name - nye: {$type: Number, default: 0}, + nye: { $type: Number, default: 0 }, nyeReceived: Array, - greeting: {$type: Number, default: 0}, + greeting: { $type: Number, default: 0 }, greetingReceived: Array, - thankyou: {$type: Number, default: 0}, + thankyou: { $type: Number, default: 0 }, thankyouReceived: Array, - birthday: {$type: Number, default: 0}, + birthday: { $type: Number, default: 0 }, birthdayReceived: Array, - congrats: {$type: Number, default: 0}, + congrats: { $type: Number, default: 0 }, congratsReceived: Array, - getwell: {$type: Number, default: 0}, + getwell: { $type: Number, default: 0 }, getwellReceived: Array, - goodluck: {$type: Number, default: 0}, + goodluck: { $type: Number, default: 0 }, goodluckReceived: Array, }, @@ -322,79 +327,87 @@ export default new Schema({ // 'PandaCub-Red': 10, // Number represents "Growth Points" // etc... // } - pets: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, + pets: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, currentPet: String, // Cactus-Desert // eggs: { // 'PandaCub': 0, // 0 indicates "doesn't own" // 'Wolf': 5 // Number indicates "stacking" // } - eggs: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, + eggs: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, // hatchingPotions: { // 'Desert': 0, // 0 indicates "doesn't own" // 'CottonCandyBlue': 5 // Number indicates "stacking" // } - hatchingPotions: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, + hatchingPotions: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, // Food: { // 'Watermelon': 0, // 0 indicates "doesn't own" // 'RottenMeat': 5 // Number indicates "stacking" // } - food: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, + food: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, // mounts: { // 'Wolf-Desert': true, // 'PandaCub-Red': false, // etc... // } - mounts: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, + mounts: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, currentMount: String, // Quests: { // 'boss_0': 0, // 0 indicates "doesn't own" // 'collection_honey': 5 // Number indicates "stacking" // } - quests: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, + quests: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, lastDrop: { - date: {$type: Date, default: Date.now}, - count: {$type: Number, default: 0}, + date: { $type: Date, default: Date.now }, + count: { $type: Number, default: 0 }, }, }, - lastCron: {$type: Date, default: Date.now}, - _cronSignature: {$type: String, default: 'NOT_RUNNING'}, // Private property used to avoid double cron + lastCron: { $type: Date, default: Date.now }, + _cronSignature: { $type: String, default: 'NOT_RUNNING' }, // Private property used to avoid double cron // {GROUP_ID: Boolean}, represents whether they have unseen chat messages - newMessages: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, + newMessages: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, - challenges: [{$type: String, ref: 'Challenge', validate: [v => validator.isUUID(v), 'Invalid uuid.']}], + challenges: [{ $type: String, ref: 'Challenge', validate: [v => validator.isUUID(v), 'Invalid uuid.'] }], invitations: { // Using an array without validation because otherwise mongoose treat this as a subdocument and applies _id by default // Schema is (id, name, inviter, publicGuild) // TODO one way to fix is http://mongoosejs.com/docs/guide.html#_id - guilds: {$type: Array, default: () => []}, + guilds: { $type: Array, default: () => [] }, // Using a Mixed type because otherwise user.invitations.party = {} // to reset invitation, causes validation to fail TODO // schema is the same as for guild invitations (id, name, inviter) - party: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, + party: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, parties: [{ id: { $type: String, @@ -415,112 +428,116 @@ export default new Schema({ }], }, - guilds: [{$type: String, ref: 'Group', validate: [v => validator.isUUID(v), 'Invalid uuid.']}], + guilds: [{ $type: String, ref: 'Group', validate: [v => validator.isUUID(v), 'Invalid uuid.'] }], party: { - _id: {$type: String, validate: [v => validator.isUUID(v), 'Invalid uuid.'], ref: 'Group'}, - order: {$type: String, default: 'level'}, - orderAscending: {$type: String, default: 'ascending'}, + _id: { $type: String, validate: [v => validator.isUUID(v), 'Invalid uuid.'], ref: 'Group' }, + order: { $type: String, default: 'level' }, + orderAscending: { $type: String, default: 'ascending' }, quest: { key: String, progress: { - up: {$type: Number, default: 0}, - down: {$type: Number, default: 0}, - collect: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, - collectedItems: {$type: Number, default: 0}, + up: { $type: Number, default: 0 }, + down: { $type: Number, default: 0 }, + collect: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, + collectedItems: { $type: Number, default: 0 }, }, completed: String, // When quest is done, we move it from key => completed, and it's a one-time flag (for modal) that they unset by clicking "ok" in browser - RSVPNeeded: {$type: Boolean, default: false}, // Set to true when invite is pending, set to false when quest invite is accepted or rejected, quest starts, or quest is cancelled + RSVPNeeded: { $type: Boolean, default: false }, // Set to true when invite is pending, set to false when quest invite is accepted or rejected, quest starts, or quest is cancelled }, }, preferences: { - dayStart: {$type: Number, default: 0, min: 0, max: 23}, - size: {$type: String, enum: ['broad', 'slim'], default: 'slim'}, - hair: { - color: {$type: String, default: 'red'}, - base: {$type: Number, default: 3}, - bangs: {$type: Number, default: 1}, - beard: {$type: Number, default: 0}, - mustache: {$type: Number, default: 0}, - flower: {$type: Number, default: 1}, + dayStart: { + $type: Number, default: 0, min: 0, max: 23, }, - hideHeader: {$type: Boolean, default: false}, - skin: {$type: String, default: '915533'}, - shirt: {$type: String, default: 'blue'}, - timezoneOffset: {$type: Number, default: 0}, - sound: {$type: String, default: 'rosstavoTheme', enum: ['off', ...shared.content.audioThemes]}, - chair: {$type: String, default: 'none'}, + size: { $type: String, enum: ['broad', 'slim'], default: 'slim' }, + hair: { + color: { $type: String, default: 'red' }, + base: { $type: Number, default: 3 }, + bangs: { $type: Number, default: 1 }, + beard: { $type: Number, default: 0 }, + mustache: { $type: Number, default: 0 }, + flower: { $type: Number, default: 1 }, + }, + hideHeader: { $type: Boolean, default: false }, + skin: { $type: String, default: '915533' }, + shirt: { $type: String, default: 'blue' }, + timezoneOffset: { $type: Number, default: 0 }, + sound: { $type: String, default: 'rosstavoTheme', enum: ['off', ...shared.content.audioThemes] }, + chair: { $type: String, default: 'none' }, timezoneOffsetAtLastCron: Number, language: String, automaticAllocation: Boolean, - allocationMode: {$type: String, enum: ['flat', 'classbased', 'taskbased'], default: 'flat'}, - autoEquip: {$type: Boolean, default: true}, + allocationMode: { $type: String, enum: ['flat', 'classbased', 'taskbased'], default: 'flat' }, + autoEquip: { $type: Boolean, default: true }, costume: Boolean, - dateFormat: {$type: String, enum: ['MM/dd/yyyy', 'dd/MM/yyyy', 'yyyy/MM/dd'], default: 'MM/dd/yyyy'}, - sleep: {$type: Boolean, default: false}, - stickyHeader: {$type: Boolean, default: true}, - disableClasses: {$type: Boolean, default: false}, - newTaskEdit: {$type: Boolean, default: false}, - dailyDueDefaultView: {$type: Boolean, default: false}, - advancedCollapsed: {$type: Boolean, default: false}, - toolbarCollapsed: {$type: Boolean, default: false}, - reverseChatOrder: {$type: Boolean, default: false}, + dateFormat: { $type: String, enum: ['MM/dd/yyyy', 'dd/MM/yyyy', 'yyyy/MM/dd'], default: 'MM/dd/yyyy' }, + sleep: { $type: Boolean, default: false }, + stickyHeader: { $type: Boolean, default: true }, + disableClasses: { $type: Boolean, default: false }, + newTaskEdit: { $type: Boolean, default: false }, + dailyDueDefaultView: { $type: Boolean, default: false }, + advancedCollapsed: { $type: Boolean, default: false }, + toolbarCollapsed: { $type: Boolean, default: false }, + reverseChatOrder: { $type: Boolean, default: false }, background: String, - displayInviteToPartyWhenPartyIs1: {$type: Boolean, default: true}, - webhooks: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, + displayInviteToPartyWhenPartyIs1: { $type: Boolean, default: true }, + webhooks: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, // For the following fields make sure to use strict comparison when searching for falsey values (=== false) // As users who didn't login after these were introduced may have them undefined/null emailNotifications: { - unsubscribeFromAll: {$type: Boolean, default: false}, - newPM: {$type: Boolean, default: true}, - kickedGroup: {$type: Boolean, default: true}, - wonChallenge: {$type: Boolean, default: true}, - giftedGems: {$type: Boolean, default: true}, - giftedSubscription: {$type: Boolean, default: true}, - invitedParty: {$type: Boolean, default: true}, - invitedGuild: {$type: Boolean, default: true}, - questStarted: {$type: Boolean, default: true}, - invitedQuest: {$type: Boolean, default: true}, + unsubscribeFromAll: { $type: Boolean, default: false }, + newPM: { $type: Boolean, default: true }, + kickedGroup: { $type: Boolean, default: true }, + wonChallenge: { $type: Boolean, default: true }, + giftedGems: { $type: Boolean, default: true }, + giftedSubscription: { $type: Boolean, default: true }, + invitedParty: { $type: Boolean, default: true }, + invitedGuild: { $type: Boolean, default: true }, + questStarted: { $type: Boolean, default: true }, + invitedQuest: { $type: Boolean, default: true }, // remindersToLogin: {$type: Boolean, default: true}, // importantAnnouncements are in fact the recapture emails - importantAnnouncements: {$type: Boolean, default: true}, - weeklyRecaps: {$type: Boolean, default: true}, - onboarding: {$type: Boolean, default: true}, - majorUpdates: {$type: Boolean, default: true}, - subscriptionReminders: {$type: Boolean, default: true}, + importantAnnouncements: { $type: Boolean, default: true }, + weeklyRecaps: { $type: Boolean, default: true }, + onboarding: { $type: Boolean, default: true }, + majorUpdates: { $type: Boolean, default: true }, + subscriptionReminders: { $type: Boolean, default: true }, }, pushNotifications: { - unsubscribeFromAll: {$type: Boolean, default: false}, - newPM: {$type: Boolean, default: true}, - wonChallenge: {$type: Boolean, default: true}, - giftedGems: {$type: Boolean, default: true}, - giftedSubscription: {$type: Boolean, default: true}, - invitedParty: {$type: Boolean, default: true}, - invitedGuild: {$type: Boolean, default: true}, - questStarted: {$type: Boolean, default: true}, - invitedQuest: {$type: Boolean, default: true}, - majorUpdates: {$type: Boolean, default: true}, - partyActivity: {$type: Boolean, default: true}, + unsubscribeFromAll: { $type: Boolean, default: false }, + newPM: { $type: Boolean, default: true }, + wonChallenge: { $type: Boolean, default: true }, + giftedGems: { $type: Boolean, default: true }, + giftedSubscription: { $type: Boolean, default: true }, + invitedParty: { $type: Boolean, default: true }, + invitedGuild: { $type: Boolean, default: true }, + questStarted: { $type: Boolean, default: true }, + invitedQuest: { $type: Boolean, default: true }, + majorUpdates: { $type: Boolean, default: true }, + partyActivity: { $type: Boolean, default: true }, }, suppressModals: { - levelUp: {$type: Boolean, default: false}, - hatchPet: {$type: Boolean, default: false}, - raisePet: {$type: Boolean, default: false}, - streak: {$type: Boolean, default: false}, + levelUp: { $type: Boolean, default: false }, + hatchPet: { $type: Boolean, default: false }, + raisePet: { $type: Boolean, default: false }, + streak: { $type: Boolean, default: false }, }, tasks: { - groupByChallenge: {$type: Boolean, default: false}, // @TODO remove? not used - confirmScoreNotes: {$type: Boolean, default: false}, // @TODO remove? not used + groupByChallenge: { $type: Boolean, default: false }, // @TODO remove? not used + confirmScoreNotes: { $type: Boolean, default: false }, // @TODO remove? not used }, improvementCategories: { $type: Array, - validate: (categories) => { + validate: categories => { const validCategories = ['work', 'exercise', 'healthWellness', 'school', 'teams', 'chores', 'creativity']; - let isValidCategory = categories.every(category => validCategories.indexOf(category) !== -1); + const isValidCategory = categories.every(category => validCategories.indexOf(category) !== -1); return isValidCategory; }, }, @@ -535,36 +552,38 @@ export default new Schema({ }, }, stats: { - hp: {$type: Number, default: shared.maxHealth}, - mp: {$type: Number, default: 10}, - exp: {$type: Number, default: 0}, - gp: {$type: Number, default: 0}, - lvl: {$type: Number, default: 1, min: 1}, + hp: { $type: Number, default: shared.maxHealth }, + mp: { $type: Number, default: 10 }, + exp: { $type: Number, default: 0 }, + gp: { $type: Number, default: 0 }, + lvl: { $type: Number, default: 1, min: 1 }, // Class System - class: {$type: String, enum: ['warrior', 'rogue', 'wizard', 'healer'], default: 'warrior', required: true}, - points: {$type: Number, default: 0}, - str: {$type: Number, default: 0}, - con: {$type: Number, default: 0}, - int: {$type: Number, default: 0}, - per: {$type: Number, default: 0}, + class: { + $type: String, enum: ['warrior', 'rogue', 'wizard', 'healer'], default: 'warrior', required: true, + }, + points: { $type: Number, default: 0 }, + str: { $type: Number, default: 0 }, + con: { $type: Number, default: 0 }, + int: { $type: Number, default: 0 }, + per: { $type: Number, default: 0 }, buffs: { - str: {$type: Number, default: 0}, - int: {$type: Number, default: 0}, - per: {$type: Number, default: 0}, - con: {$type: Number, default: 0}, - stealth: {$type: Number, default: 0}, - streaks: {$type: Boolean, default: false}, - snowball: {$type: Boolean, default: false}, - spookySparkles: {$type: Boolean, default: false}, - shinySeed: {$type: Boolean, default: false}, - seafoam: {$type: Boolean, default: false}, + str: { $type: Number, default: 0 }, + int: { $type: Number, default: 0 }, + per: { $type: Number, default: 0 }, + con: { $type: Number, default: 0 }, + stealth: { $type: Number, default: 0 }, + streaks: { $type: Boolean, default: false }, + snowball: { $type: Boolean, default: false }, + spookySparkles: { $type: Boolean, default: false }, + shinySeed: { $type: Boolean, default: false }, + seafoam: { $type: Boolean, default: false }, }, training: { - int: {$type: Number, default: 0}, - per: {$type: Number, default: 0}, - str: {$type: Number, default: 0}, - con: {$type: Number, default: 0}, + int: { $type: Number, default: 0 }, + per: { $type: Number, default: 0 }, + str: { $type: Number, default: 0 }, + con: { $type: Number, default: 0 }, }, }, @@ -573,44 +592,46 @@ export default new Schema({ inbox: { // messages are stored in the Inbox collection - newMessages: {$type: Number, default: 0}, - blocks: {$type: Array, default: () => []}, - optOut: {$type: Boolean, default: false}, + newMessages: { $type: Number, default: 0 }, + blocks: { $type: Array, default: () => [] }, + optOut: { $type: Boolean, default: false }, }, tasksOrder: { - habits: [{$type: String, ref: 'Task'}], - dailys: [{$type: String, ref: 'Task'}], - todos: [{$type: String, ref: 'Task'}], - rewards: [{$type: String, ref: 'Task'}], + habits: [{ $type: String, ref: 'Task' }], + dailys: [{ $type: String, ref: 'Task' }], + todos: [{ $type: String, ref: 'Task' }], + rewards: [{ $type: String, ref: 'Task' }], + }, + extra: { + $type: Schema.Types.Mixed, + default: () => ({}), }, - extra: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, pushDevices: [PushDeviceSchema], - _ABtests: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, + _ABtests: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, webhooks: [WebhookSchema], - loginIncentives: {$type: Number, default: 0}, - invitesSent: {$type: Number, default: 0}, + loginIncentives: { $type: Number, default: 0 }, + invitesSent: { $type: Number, default: 0 }, // Items manually pinned by the user pinnedItems: [{ _id: false, - path: {$type: String}, - type: {$type: String}, + path: { $type: String }, + type: { $type: String }, }], // Ordered array of shown pinned items, necessary for sorting because seasonal items are not stored in pinnedItems - pinnedItemsOrder: [{$type: String}], + pinnedItemsOrder: [{ $type: String }], // Items the user manually unpinned from the ones suggested by Habitica unpinnedItems: [{ _id: false, - path: {$type: String}, - type: {$type: String}, + path: { $type: String }, + type: { $type: String }, }], }, { skipVersioning: { notifications: true }, strict: true, minimize: false, // So empty objects are returned typeKey: '$type', // So that we can use fields named `type` -}); \ No newline at end of file +}); diff --git a/website/server/models/userNotification.js b/website/server/models/userNotification.js index aa3ed7abfc..2e24d99afd 100644 --- a/website/server/models/userNotification.js +++ b/website/server/models/userNotification.js @@ -1,8 +1,8 @@ import mongoose from 'mongoose'; -import baseModel from '../libs/baseModel'; import { v4 as uuid } from 'uuid'; import validator from 'validator'; import _ from 'lodash'; +import baseModel from '../libs/baseModel'; const NOTIFICATION_TYPES = [ 'DROPS_ENABLED', @@ -42,9 +42,9 @@ const NOTIFICATION_TYPES = [ 'ACHIEVEMENT_ARID_AUTHORITY', ]; -const Schema = mongoose.Schema; +const { Schema } = mongoose; -export let schema = new Schema({ +export const schema = new Schema({ id: { $type: String, default: uuid, @@ -60,9 +60,10 @@ export let schema = new Schema({ // required: true, enum: NOTIFICATION_TYPES, }, - data: {$type: Schema.Types.Mixed, default: () => { - return {}; - }}, + data: { + $type: Schema.Types.Mixed, + default: () => ({}), + }, // A field to mark the notification as seen without deleting it, optional use seen: { $type: Boolean, @@ -100,9 +101,7 @@ schema.statics.convertNotificationsToSafeJson = function convertNotificationsToS return false; }); - return filteredNotifications.map(n => { - return n.toJSON(); - }); + return filteredNotifications.map(n => n.toJSON()); }; schema.plugin(baseModel, { @@ -111,4 +110,4 @@ schema.plugin(baseModel, { _id: false, // use id instead of _id }); -export let model = mongoose.model('UserNotification', schema); +export const model = mongoose.model('UserNotification', schema); diff --git a/website/server/models/webhook.js b/website/server/models/webhook.js index f4075faf38..aaa4560a56 100644 --- a/website/server/models/webhook.js +++ b/website/server/models/webhook.js @@ -1,15 +1,15 @@ import mongoose from 'mongoose'; import validator from 'validator'; +import { v4 as uuid } from 'uuid'; +import _ from 'lodash'; +import nconf from 'nconf'; import baseModel from '../libs/baseModel'; import shared from '../../common'; -import {v4 as uuid} from 'uuid'; -import _ from 'lodash'; import { BadRequest } from '../libs/errors'; -import nconf from 'nconf'; import apiError from '../libs/apiError'; const IS_PRODUCTION = nconf.get('IS_PROD'); -const Schema = mongoose.Schema; +const { Schema } = mongoose; const TASK_ACTIVITY_DEFAULT_OPTIONS = Object.freeze({ created: false, @@ -30,7 +30,7 @@ const QUEST_ACTIVITY_DEFAULT_OPTIONS = Object.freeze({ questFinished: false, }); -export let schema = new Schema({ +export const schema = new Schema({ id: { $type: String, required: true, @@ -55,11 +55,9 @@ export let schema = new Schema({ url: { $type: String, required: true, - validate: [(v) => { - return validator.isURL(v, { - require_tld: IS_PRODUCTION ? true : false, // eslint-disable-line camelcase - }); - }, shared.i18n.t('invalidUrl')], + validate: [v => validator.isURL(v, { + require_tld: !!IS_PRODUCTION, // eslint-disable-line camelcase + }), shared.i18n.t('invalidUrl')], }, enabled: { $type: Boolean, required: true, default: true }, options: { @@ -87,7 +85,7 @@ schema.methods.formatOptions = function formatOptions (res) { _.defaults(this.options, TASK_ACTIVITY_DEFAULT_OPTIONS); this.options = _.pick(this.options, Object.keys(TASK_ACTIVITY_DEFAULT_OPTIONS)); - let invalidOption = Object.keys(this.options) + const invalidOption = Object.keys(this.options) .find(option => typeof this.options[option] !== 'boolean'); if (invalidOption) { @@ -103,7 +101,7 @@ schema.methods.formatOptions = function formatOptions (res) { _.defaults(this.options, USER_ACTIVITY_DEFAULT_OPTIONS); this.options = _.pick(this.options, Object.keys(USER_ACTIVITY_DEFAULT_OPTIONS)); - let invalidOption = Object.keys(this.options) + const invalidOption = Object.keys(this.options) .find(option => typeof this.options[option] !== 'boolean'); if (invalidOption) { @@ -113,7 +111,7 @@ schema.methods.formatOptions = function formatOptions (res) { _.defaults(this.options, QUEST_ACTIVITY_DEFAULT_OPTIONS); this.options = _.pick(this.options, Object.keys(QUEST_ACTIVITY_DEFAULT_OPTIONS)); - let invalidOption = Object.keys(this.options) + const invalidOption = Object.keys(this.options) .find(option => typeof this.options[option] !== 'boolean'); if (invalidOption) { @@ -125,4 +123,4 @@ schema.methods.formatOptions = function formatOptions (res) { } }; -export let model = mongoose.model('Webhook', schema); +export const model = mongoose.model('Webhook', schema); diff --git a/website/server/server.js b/website/server/server.js index 72bcf4ffee..c952da4b04 100644 --- a/website/server/server.js +++ b/website/server/server.js @@ -1,12 +1,7 @@ import nconf from 'nconf'; -import logger from './libs/logger'; import express from 'express'; import http from 'http'; - -const server = http.createServer(); -const app = express(); - -app.set('port', nconf.get('PORT')); +import logger from './libs/logger'; // Setup translations // Must come before attach middlewares so Mongoose validations can use translations @@ -23,6 +18,11 @@ import './models/challenge'; import './models/group'; import './models/user'; +const server = http.createServer(); +const app = express(); + +app.set('port', nconf.get('PORT')); + attachMiddlewares(app, server); server.on('request', app);