Compare commits

..

1 Commits

Author SHA1 Message Date
Sabe Jones 4fa689d9af 4.116.2 2019-10-03 14:04:15 -05:00
4912 changed files with 145185 additions and 472183 deletions
+4 -10
View File
@@ -1,12 +1,6 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": true
}
}
]
"plugins": [
"transform-es2015-modules-commonjs",
"syntax-object-rest-spread",
]
}
}
+1
View File
@@ -1,2 +1,3 @@
node_modules
.git
website
-2
View File
@@ -3,8 +3,6 @@ coverage/
database_reports/
website/build/
website/transpiled-babel/
# Has its own linter
website/client/
website/common/transpiled-babel/
dist/
dist-client/
+10
View File
@@ -0,0 +1,10 @@
{
"root": true,
"env": {
"node": true,
},
"extends": [
"habitrpg",
"habitrpg/esnext"
],
}
-6
View File
@@ -1,6 +0,0 @@
module.exports = {
root: true,
extends: [
'habitrpg/lib/node'
],
}
+1 -1
View File
@@ -1,7 +1,7 @@
[//]: # (Note: See http://habitica.fandom.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API for more info)
[//]: # (Put Issue # here, if applicable. This will automatically close the issue if your PR is merged in)
Fixes put_#_and_issue_number_here
Fixes put_#_and_issue_numer_here
### Changes
[//]: # (Describe the changes that were made in detail here. Include pictures if necessary)
-198
View File
@@ -1,198 +0,0 @@
name: Test
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: cp config.json.example config.json
- name: npm install
run: |
npm ci
env:
CI: true
- run: npm run lint-no-fix
apidoc:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: cp config.json.example config.json
- name: npm install
run: |
npm ci
env:
CI: true
- run: npm run apidoc
sanity:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: cp config.json.example config.json
- name: npm install
run: |
npm ci
env:
CI: true
- run: npm run test:sanity
common:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: cp config.json.example config.json
- name: npm install
run: |
npm ci
env:
CI: true
- run: npm run test:common
content:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: cp config.json.example config.json
- name: npm install
run: |
npm ci
env:
CI: true
- run: npm run test:content
api-unit:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: sudo docker run --name mongo -d -p 27017:27017 mongo
- run: cp config.json.example config.json
- name: npm install
run: |
npm ci
env:
CI: true
- run: npm run test:api:unit
env:
REQUIRES_SERVER=true: true
api-v3-integration:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: sudo docker run --name mongo -d -p 27017:27017 mongo
- run: cp config.json.example config.json
- name: npm install
run: |
npm ci
env:
CI: true
- run: npm run test:api-v3:integration
env:
REQUIRES_SERVER=true: true
api-v4-integration:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: sudo docker run --name mongo -d -p 27017:27017 mongo
- run: cp config.json.example config.json
- name: npm install
run: |
npm ci
env:
CI: true
- run: npm run test:api-v4:integration
env:
REQUIRES_SERVER=true: true
client-unit:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: cp config.json.example config.json
- name: npm install
run: |
npm ci
env:
CI: true
- run: npm run test:unit
working-directory: ./website/client
+10 -4
View File
@@ -1,5 +1,10 @@
.DS_Store
website/client-old/gen
website/client-old/common
website/client-old/apidoc
website/build
website/client-old/js/habitrpg-shared.js*
website/client-old/css/habitrpg-shared.css
website/transpiled-babel/
website/common/transpiled-babel/
node_modules
@@ -10,6 +15,8 @@ apidoc_build
config.json
npm-debug.log*
lib
website/client-old/bower_components
website/client-old/new-stuff.html
newrelic_agent.log
.bower-tmp
.bower-registry
@@ -20,13 +27,15 @@ TODO
*.log
src/*/*.map
src/*/*/*.map
test/*.js
test/*.map
website/client-old/docs
*.sublime-workspace
coverage
coverage.html
common/dist/scripts/*
dist
dist-client
website/client/dist
test/client/unit/coverage
test/client/e2e/reports
test/client-old/spec/mocks/translations.js
@@ -37,6 +46,3 @@ yarn.lock
.elasticbeanstalk/*
!.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml
# webstorm fake webpack for path intellisense
webpack.webstorm.config
+11
View File
@@ -1,9 +1,20 @@
node_modules/**
.bower-cache/**
.bower-tmp/**
.bower-registry/**
website/client-old/**
website/client/**
website/client/store/**
website/views/**
website/build/**
dist/**
test/**
.git/**
Gruntfile.js
CHANGELOG.md
.idea*
*.log
newrelic_agent.log
*.swp
*.swx
website/raw_sprites/**
+29
View File
@@ -0,0 +1,29 @@
language: node_js
node_js:
- '12'
services:
- mongodb
cache:
directories:
- 'node_modules'
addons:
chrome: stable
before_script:
- npm run test:build
- cp config.json.example config.json
- sleep 5
script:
- npm run $TEST
env:
global:
- DISABLE_REQUEST_LOGGING=true
matrix:
- TEST="lint"
- TEST="test:api:unit" REQUIRES_SERVER=true COVERAGE=true
- TEST="test:api-v3:integration" REQUIRES_SERVER=true COVERAGE=true
- TEST="test:api-v4:integration" REQUIRES_SERVER=true COVERAGE=true
- TEST="test:sanity"
- TEST="test:content" COVERAGE=true
- TEST="test:common" COVERAGE=true
- TEST="client:unit" COVERAGE=true
- TEST="apidoc"
+5 -2
View File
@@ -19,9 +19,12 @@ RUN npm install -g gulp-cli mocha
RUN mkdir -p /usr/src/habitrpg
WORKDIR /usr/src/habitrpg
RUN git clone --branch release --depth 1 https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
RUN npm set unsafe-perm true
RUN npm install
RUN gulp build:prod --force
# Create Build dir
RUN mkdir -p ./website/build
# Start Habitica
EXPOSE 80 8080 36612
EXPOSE 3000
CMD ["node", "./website/transpiled-babel/index.js"]
+5 -14
View File
@@ -1,14 +1,5 @@
FROM node:12
# Install global packages
RUN npm install -g gulp-cli mocha
# Copy package.json and package-lock.json into image, then install
# dependencies.
WORKDIR /usr/src/habitica
COPY ["package.json", "package-lock.json", "./"]
RUN npm install
# Copy the remaining source files in.
COPY . /usr/src/habitica
RUN npm run postinstall
FROM node:12
WORKDIR /code
COPY package*.json /code/
RUN npm install
RUN npm install -g gulp-cli mocha
+1 -1
View File
@@ -1,4 +1,4 @@
Habitica ![Build Status](https://github.com/HabitRPG/habitica/workflows/Test/badge.svg) [![Code Climate](https://codeclimate.com/github/HabitRPG/habitrpg.svg)](https://codeclimate.com/github/HabitRPG/habitrpg) [![Bountysource](https://api.bountysource.com/badge/tracker?tracker_id=68393)](https://www.bountysource.com/trackers/68393-habitrpg?utm_source=68393&utm_medium=shield&utm_campaign=TRACKER_BADGE) [![Open Source Helpers](https://www.codetriage.com/habitrpg/habitica/badges/users.svg)](https://www.codetriage.com/habitrpg/habitica)
Habitica [![Build Status](https://travis-ci.org/HabitRPG/habitica.svg?branch=develop)](https://travis-ci.org/HabitRPG/habitica) [![Code Climate](https://codeclimate.com/github/HabitRPG/habitrpg.svg)](https://codeclimate.com/github/HabitRPG/habitrpg) [![Bountysource](https://api.bountysource.com/badge/tracker?tracker_id=68393)](https://www.bountysource.com/trackers/68393-habitrpg?utm_source=68393&utm_medium=shield&utm_campaign=TRACKER_BADGE) [![Open Source Helpers](https://www.codetriage.com/habitrpg/habitica/badges/users.svg)](https://www.codetriage.com/habitrpg/habitica)
===============
[![Greenkeeper badge](https://badges.greenkeeper.io/HabitRPG/habitica.svg)](https://greenkeeper.io/)
+11
View File
@@ -0,0 +1,11 @@
# Vagrant #
Vagrant is a system to create reproducible and portable development
environments. Because of the variety of systems used for Habitica
development and the various issues developers may encounter setting up
Habitica on them, vagrant provides a single development enviroment with
minimal dependencies on the developer's local platform. It can be used
on a variety of systems including Windows, Mac OS X, and Linux.
Instructions for using the Habitica Vagrant environment are in
[Setting up Habitica Locally](http://habitica.fandom.com/wiki/Setting_up_Habitica_Locally).
+22
View File
@@ -0,0 +1,22 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provider "virtualbox" do |v|
v.memory = 4096
v.cpus = 1
v.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/vagrant", "1"]
end
config.vm.box = "thepeopleseason/habitrpg"
config.ssh.forward_agent = true
config.vm.hostname = "habitrpg"
config.vm.network "forwarded_port", guest: 3000, host: 3000, auto_correct: true
config.vm.usable_port_range = (3000..3050)
config.vm.network "forwarded_port", guest: 8080, host: 8080, auto_correct: true
config.vm.usable_port_range = (8080..8130)
config.vm.provision :shell, :path => "vagrant_scripts/vagrant.sh"
end
-1
View File
@@ -33,7 +33,6 @@
"LOGGLY_TOKEN": "example-token",
"MAINTENANCE_MODE": "false",
"NODE_DB_URI": "mongodb://localhost/habitrpg",
"MONGODB_POOL_SIZE": "10",
"NODE_ENV": "development",
"PATH": "bin:node_modules/.bin:/usr/local/bin:/usr/bin:/bin",
"PAYPAL_BILLING_PLANS_basic_12mo": "basic_12mo",
+5 -6
View File
@@ -15,9 +15,8 @@ services:
ports:
- "8080:8080"
volumes:
- .:/usr/src/habitica
- /usr/src/habitica/node_modules
- /usr/src/habitica/website/client/node_modules
- .:/code
- /code/node_modules
server:
build:
context: .
@@ -33,10 +32,10 @@ services:
ports:
- "3000:3000"
volumes:
- .:/usr/src/habitica
- /usr/src/habitica/node_modules
- .:/code
- /code/node_modules
mongo:
image: mongo:3.6
image: mongo:3.4
networks:
- habitica
ports:
+1 -1
View File
@@ -25,7 +25,7 @@ services:
- mongo
mongo:
image: mongo:3.6
image: mongo:3.4
ports:
- "27017:27017"
networks:
+6 -4
View File
@@ -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 => {
const result = apidoc.createDoc({
gulp.task('apidoc', gulp.series('apidoc:clean', (done) => {
let result = apidoc.createDoc({
src: APIDOC_SRC_PATH,
dest: APIDOC_DEST_PATH,
});
@@ -21,4 +21,6 @@ gulp.task('apidoc', gulp.series('apidoc:clean', done => {
}
}));
gulp.task('apidoc:watch', gulp.series('apidoc', done => gulp.watch(`${APIDOC_SRC_PATH}/**/*.js`, gulp.series('apidoc', done))));
gulp.task('apidoc:watch', gulp.series('apidoc', (done) => {
return gulp.watch(`${APIDOC_SRC_PATH}/**/*.js`, gulp.series('apidoc', done));
}));
+25 -10
View File
@@ -1,28 +1,43 @@
import gulp from 'gulp';
import babel from 'gulp-babel';
import webpackProductionBuild from '../webpack/build';
gulp.task('build:src', () => gulp.src('website/server/**/*.js')
.pipe(babel())
.pipe(gulp.dest('website/transpiled-babel/')));
gulp.task('build:src', () => {
return gulp.src('website/server/**/*.js')
.pipe(babel())
.pipe(gulp.dest('website/transpiled-babel/'));
});
gulp.task('build:common', () => gulp.src('website/common/script/**/*.js')
.pipe(babel())
.pipe(gulp.dest('website/common/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:server', gulp.series('build:src', 'build:common', done => done()));
// Client Production Build
gulp.task('build:client', (done) => {
webpackProductionBuild((err, output) => {
if (err) return done(err);
console.log(output); // eslint-disable-line no-console
done();
});
});
gulp.task('build:prod', gulp.series(
'build:server',
'build:client',
'apidoc',
done => done(),
done => done()
));
const buildArgs = [];
let 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();
}));
}));
+17 -21
View File
@@ -1,30 +1,26 @@
import mongoose from 'mongoose';
import nconf from 'nconf';
import repl from 'repl';
import gulp from 'gulp';
import logger from '../website/server/libs/logger';
import logger from '../website/server/libs/logger';
import nconf from 'nconf';
import repl from 'repl';
import gulp from 'gulp';
// Add additional properties to the repl's context
const improveRepl = context => {
let improveRepl = (context) => {
// Let "exit" and "quit" terminate the console
['exit', 'quit'].forEach(term => {
Object.defineProperty(context, term, {
get () { // eslint-disable-line getter-return
process.exit();
},
});
['exit', 'quit'].forEach((term) => {
Object.defineProperty(context, term, { get () {
process.exit();
}});
});
// "clear" clears the screen
Object.defineProperty(context, 'clear', {
get () { // eslint-disable-line getter-return
process.stdout.write('\u001B[2J\u001B[0;0f');
},
});
Object.defineProperty(context, 'clear', { get () {
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 ? {} : {
@@ -34,14 +30,14 @@ const 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);
+38 -37
View File
@@ -4,29 +4,29 @@ 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
const MAX_SPRITESHEET_SIZE = 1024 * 1024 * 3;
const IMG_DIST_PATH = 'website/client/src/assets/images/sprites/';
const CSS_DIST_PATH = 'website/client/src/assets/css/sprites/';
const IMG_DIST_PATH = 'website/client/assets/images/sprites/';
const CSS_DIST_PATH = 'website/client/assets/css/sprites/';
function checkForSpecialTreatment (name) {
const regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame|^eyewear_special_\w+HalfMoon/;
let 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);
const requiresSpecialTreatment = checkForSpecialTreatment(img);
let requiresSpecialTreatment = checkForSpecialTreatment(img);
if (requiresSpecialTreatment) {
const newWidth = dims.width < 90 ? 90 : dims.width;
const newHeight = dims.height < 90 ? 90 : dims.height;
let newWidth = dims.width < 90 ? 90 : dims.width;
let 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
const totalPixelSize = dims.width * dims.height + padding;
let totalPixelSize = dims.width * dims.height + padding;
return totalPixelSize;
}
function calculateSpritesheetsSrcIndicies (src) {
let totalPixels = 0;
const slices = [0];
let slices = [0];
each(src, (img, index) => {
const imageSize = calculateImgDimensions(img, true);
let imageSize = calculateImgDimensions(img, true);
totalPixels += imageSize;
if (totalPixels > MAX_SPRITESHEET_SIZE) {
@@ -64,35 +64,37 @@ 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.
const requiresSpecialTreatment = checkForSpecialTreatment(sprite.name);
let 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) {
const styleArray = sprite.name.split('_').slice(2, 3);
if (Number(styleArray[0]) > 14) sprite.custom.px.offsetY = `-${sprite.y}px`; // don't crop updos
let 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) {
const spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src);
const stream = mergeStream();
let spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src);
let stream = mergeStream();
each(spritesheetSliceIndicies, (start, index) => {
const slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]);
let slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]);
const spriteData = gulp.src(slicedSrc)
let spriteData = gulp.src(slicedSrc)
.pipe(spritesmith({
imgName: `spritesmith-${name}-${index}.png`,
cssName: `spritesmith-${name}-${index}.css`,
@@ -102,12 +104,12 @@ function createSpritesStream (name, src) {
cssVarMap,
}));
const imgStream = spriteData.img
let imgStream = spriteData.img
.pipe(vinylBuffer())
.pipe(imagemin())
.pipe(gulp.dest(IMG_DIST_PATH));
const cssStream = spriteData.css
let cssStream = spriteData.css
.pipe(gulp.dest(CSS_DIST_PATH));
stream.add(imgStream);
@@ -118,32 +120,32 @@ function createSpritesStream (name, src) {
}
gulp.task('sprites:main', () => {
const mainSrc = sync('website/raw_sprites/spritesmith/**/*.png');
let mainSrc = sync('website/raw_sprites/spritesmith/**/*.png');
return createSpritesStream('main', mainSrc);
});
gulp.task('sprites:largeSprites', () => {
const largeSrc = sync('website/raw_sprites/spritesmith_large/**/*.png');
let 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;
const distSpritesheets = sync(`${IMG_DIST_PATH}*.png`);
let distSpritesheets = sync(`${IMG_DIST_PATH}*.png`);
each(distSpritesheets, img => {
const spriteSize = calculateImgDimensions(img);
each(distSpritesheets, (img) => {
let spriteSize = calculateImgDimensions(img);
if (spriteSize > MAX_SPRITESHEET_SIZE) {
numberOfSheetsThatAreTooBig += 1;
const name = basename(img, '.png');
numberOfSheetsThatAreTooBig++;
let name = basename(img, '.png');
console.error(`WARNING: ${name} might be too big - ${spriteSize} > ${MAX_SPRITESHEET_SIZE}`); // eslint-disable-line no-console
}
});
@@ -153,8 +155,7 @@ 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
}
+7 -2
View File
@@ -1,11 +1,16 @@
import gulp from 'gulp';
import nodemon from 'gulp-nodemon';
import pkg from '../package.json';
let pkg = require('../package.json');
gulp.task('nodemon', done => {
gulp.task('nodemon', (done) => {
nodemon({
script: pkg.main,
ignore: [
'website/client-old/*',
'website/views/*',
'common/dist/script/content/*',
],
});
done();
});
+85 -76
View File
@@ -1,59 +1,60 @@
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';
} from './taskHelper';
import mongoose from 'mongoose';
import { exec } from 'child_process';
import gulp from 'gulp';
import os from 'os';
import nconf from 'nconf';
// 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 */
const testResults = [];
const testCount = (stdout, regexp) => {
const match = stdout.match(regexp);
return parseInt(match && (match[1] || 0), 10);
let testResults = [];
let testCount = (stdout, regexp) => {
let match = stdout.match(regexp);
return parseInt(match && match[1] || 0, 10);
};
const testBin = (string, additionalEnvVariables = '') => {
let testBin = (string, additionalEnvVariables = '') => {
if (os.platform() === 'win32') {
if (additionalEnvVariables !== '') {
additionalEnvVariables = additionalEnvVariables.split(' ').join('&&set '); // eslint-disable-line no-param-reassign
additionalEnvVariables = `set ${additionalEnvVariables}&&`; // eslint-disable-line no-param-reassign
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(done => {
gulp.task('test:nodemon', gulp.series(function setupNodemon (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}`);
return mongoose.connection.dropDatabase(err2 => {
mongoose.connection.dropDatabase((err2) => {
if (err2) return cb(err2);
return mongoose.connection.close(cb);
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) {
@@ -72,43 +73,45 @@ 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 => {
const runner = exec(
gulp.task('test:sanity', (cb) => {
let 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 => {
const runner = exec(
gulp.task('test:common', gulp.series('test:prepare:build', (cb) => {
let 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', () => gulp.watch(['common/script/**/*', 'test/common/**/*'], gulp.series('test:common:clean', done => done()))));
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:safe', gulp.series('test:prepare:build', cb => {
const runner = exec(
gulp.task('test:common:safe', gulp.series('test:prepare:build', (cb) => {
let runner = exec(
testBin(COMMON_TEST_COMMAND),
(err, stdout) => { // eslint-disable-line handle-callback-err
testResults.push({
@@ -118,36 +121,38 @@ 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 => {
const runner = exec(
gulp.task('test:content', gulp.series('test:prepare:build', (cb) => {
let 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', () => gulp.watch(['common/script/content/**', 'test/**'], gulp.series('test:content:clean', done => done()))));
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:safe', gulp.series('test:prepare:build', cb => {
const runner = exec(
gulp.task('test:content:safe', gulp.series('test:prepare:build', (cb) => {
let 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/),
@@ -155,77 +160,81 @@ 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 => {
const runner = exec(
gulp.task('test:api:unit', (done) => {
let 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', () => gulp.watch(['website/server/libs/*', 'test/api/unit/**/*', 'website/server/controllers/**/*'], gulp.series('test:api:unit', done => done())));
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-v3:integration', done => {
const runner = exec(
gulp.task('test:api-v3:integration', (done) => {
let 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', () => 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', () => {
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:separate-server', done => {
const runner = exec(
gulp.task('test:api-v3:integration:separate-server', (done) => {
let 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 => {
const runner = exec(
gulp.task('test:api-v4:integration', (done) => {
let 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 => {
const runner = exec(
gulp.task('test:api-v4:integration:separate-server', (done) => {
let 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);
@@ -238,11 +247,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()
));
+47 -51
View File
@@ -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 () {
const languages = fs.readdirSync(LOCALES);
let languages = fs.readdirSync(LOCALES);
languages.shift(); // Remove README.md from array of languages
return languages;
@@ -23,16 +23,18 @@ function getArrayOfLanguages () {
const ALL_LANGUAGES = getArrayOfLanguages();
function stripOutNonJsonFiles (collection) {
const onlyJson = _.filter(collection, file => file.match(/[a-zA-Z]*\.json/));
let onlyJson = _.filter(collection, (file) => {
return file.match(/[a-zA-Z]*\.json/);
});
return onlyJson;
}
function eachTranslationFile (languages, cb) {
const jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
let 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}`);
@@ -41,10 +43,10 @@ function eachTranslationFile (languages, cb) {
return cb(err);
}
const englishFile = fs.readFileSync(ENGLISH_LOCALE + filename);
const parsedEnglishFile = JSON.parse(englishFile);
let englishFile = fs.readFileSync(ENGLISH_LOCALE + filename);
let parsedEnglishFile = JSON.parse(englishFile);
return cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile);
cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile);
});
});
}
@@ -69,9 +71,9 @@ function formatMessageForPosting (msg, items) {
}
function getStringsWith (json, interpolationRegex) {
const strings = {};
let strings = {};
_.each(json, fileName => {
_.each(json, (fileName) => {
const rawFile = fs.readFileSync(ENGLISH_LOCALE + fileName);
const parsedJson = JSON.parse(rawFile);
@@ -91,69 +93,66 @@ const malformedStringExceptions = {
feedPet: true,
};
gulp.task('transifex:missingFiles', done => {
const missingStrings = [];
gulp.task('transifex:missingFiles', (done) => {
let missingStrings = [];
eachTranslationFile(ALL_LANGUAGES, error => {
eachTranslationFile(ALL_LANGUAGES, (error) => {
if (error) {
missingStrings.push(error.path);
}
});
if (!_.isEmpty(missingStrings)) {
const message = 'the following files were missing from the translations folder';
const formattedMessage = formatMessageForPosting(message, missingStrings);
let message = 'the following files were missing from the translations folder';
let formattedMessage = formatMessageForPosting(message, missingStrings);
postToSlack(formattedMessage, SLACK_CONFIG);
}
done();
});
gulp.task('transifex:missingStrings', done => {
const missingStrings = [];
gulp.task('transifex:missingStrings', (done) => {
let missingStrings = [];
eachTranslationString(ALL_LANGUAGES, (lang, filename, key, englishString, translationString) => {
eachTranslationString(ALL_LANGUAGES, (language, filename, key, englishString, translationString) => {
if (!translationString) {
const errorString = `${lang} - ${filename} - ${key} - ${englishString}`;
let errorString = `${language} - ${filename} - ${key} - ${englishString}`;
missingStrings.push(errorString);
}
});
if (!_.isEmpty(missingStrings)) {
const message = 'The following strings are not translated';
const formattedMessage = formatMessageForPosting(message, missingStrings);
let message = 'The following strings are not translated';
let formattedMessage = formatMessageForPosting(message, missingStrings);
postToSlack(formattedMessage, SLACK_CONFIG);
}
done();
});
gulp.task('transifex:malformedStrings', done => {
const jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
const interpolationRegex = /<%= [a-zA-Z]* %>/g;
const stringsToLookFor = getStringsWith(jsonFiles, interpolationRegex);
gulp.task('transifex:malformedStrings', (done) => {
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
let interpolationRegex = /<%= [a-zA-Z]* %>/g;
let stringsToLookFor = getStringsWith(jsonFiles, interpolationRegex);
const stringsWithMalformedInterpolations = [];
const stringsWithIncorrectNumberOfInterpolations = [];
let stringsWithMalformedInterpolations = [];
let stringsWithIncorrectNumberOfInterpolations = [];
_.each(ALL_LANGUAGES, lang => {
_.each(ALL_LANGUAGES, (lang) => {
_.each(stringsToLookFor, (strings, filename) => {
const translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`);
const parsedTranslationFile = JSON.parse(translationFile);
let translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`);
let parsedTranslationFile = JSON.parse(translationFile);
_.each(strings, (value, key) => { // eslint-disable-line max-nested-callbacks
const translationString = parsedTranslationFile[key];
let translationString = parsedTranslationFile[key];
if (!translationString) return;
const englishOccurences = stringsToLookFor[filename][key];
const translationOccurences = translationString.match(interpolationRegex);
let englishOccurences = stringsToLookFor[filename][key];
let translationOccurences = translationString.match(interpolationRegex);
if (!translationOccurences) {
const malformedString = `${lang} - ${filename} - ${key} - ${translationString}`;
let malformedString = `${lang} - ${filename} - ${key} - ${translationString}`;
stringsWithMalformedInterpolations.push(malformedString);
} else if (
englishOccurences.length !== translationOccurences.length
&& !malformedStringExceptions[key]
) {
const missingInterpolationString = `${lang} - ${filename} - ${key} - ${translationString}`;
} else if (englishOccurences.length !== translationOccurences.length && !malformedStringExceptions[key]) {
let missingInterpolationString = `${lang} - ${filename} - ${key} - ${translationString}`;
stringsWithIncorrectNumberOfInterpolations.push(missingInterpolationString);
}
});
@@ -161,17 +160,14 @@ gulp.task('transifex:malformedStrings', done => {
});
if (!_.isEmpty(stringsWithMalformedInterpolations)) {
const message = 'The following strings have malformed or missing interpolations';
const formattedMessage = formatMessageForPosting(message, stringsWithMalformedInterpolations);
let message = 'The following strings have malformed or missing interpolations';
let formattedMessage = formatMessageForPosting(message, stringsWithMalformedInterpolations);
postToSlack(formattedMessage, SLACK_CONFIG);
}
if (!_.isEmpty(stringsWithIncorrectNumberOfInterpolations)) {
const message = 'The following strings have a different number of string interpolations';
const formattedMessage = formatMessageForPosting(
message,
stringsWithIncorrectNumberOfInterpolations,
);
let message = 'The following strings have a different number of string interpolations';
let formattedMessage = formatMessageForPosting(message, stringsWithIncorrectNumberOfInterpolations);
postToSlack(formattedMessage, SLACK_CONFIG);
}
done();
@@ -180,5 +176,5 @@ gulp.task('transifex:malformedStrings', done => {
gulp.task(
'transifex',
gulp.series('transifex:missingFiles', 'transifex:missingStrings', 'transifex:malformedStrings'),
done => done(),
);
(done) => done()
);
+23 -22
View File
@@ -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'; // eslint-disable-line import/no-extraneous-dependencies
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';
import { resolve } from 'path';
/*
* Get access to configruable values
@@ -19,15 +19,15 @@ export const conf = nconf;
* its tasks.
*/
export function kill (proc) {
const killProcess = pid => {
let 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,15 +46,16 @@ export function kill (proc) {
export function awaitPort (port, max = 60) {
return new Promise((rej, res) => {
let socket;
let timeout;
let interval;
const timeout = setTimeout(() => {
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();
@@ -70,10 +71,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);
});
}
@@ -82,7 +83,7 @@ export function pipe (child) {
* Post request to notify configured slack channel
*/
export function postToSlack (msg, config = {}) {
const slackUrl = nconf.get('SLACK_URL');
let slackUrl = nconf.get('SLACK_URL');
if (!slackUrl) {
console.error('No slack post url specified. Your message was:'); // eslint-disable-line no-console
@@ -98,7 +99,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
});
}
@@ -106,15 +107,15 @@ export function postToSlack (msg, config = {}) {
export function runMochaTests (files, server, cb) {
require('../test/helpers/globals.helper'); // eslint-disable-line global-require
const mocha = new Mocha({ reporter: 'spec' });
const tests = glob(files);
let mocha = new Mocha({reporter: 'spec'});
let 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);
+1 -2
View File
@@ -6,8 +6,7 @@
* directory, and it will automatically be included.
*/
/* eslint-disable import/no-commonjs */
require('@babel/register');
require('babel-register');
const gulp = require('gulp');
+7
View File
@@ -0,0 +1,7 @@
{
"root": false,
"rules": {
"no-console": 0,
"no-use-before-define": ["error", { "functions": false }]
}
}
-8
View File
@@ -1,8 +0,0 @@
/* eslint-disable import/no-commonjs */
module.exports = {
root: false,
rules: {
'no-console': 0,
'no-use-before-define': ['error', { functions: false }]
}
}
@@ -1,82 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20191022_pet_color_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
let set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['Wolf-Zombie'] > 0
&& pets['TigerCub-Zombie'] > 0
&& pets['PandaCub-Zombie'] > 0
&& pets['LionCub-Zombie'] > 0
&& pets['Fox-Zombie'] > 0
&& pets['FlyingPig-Zombie'] > 0
&& pets['Dragon-Zombie'] > 0
&& pets['Cactus-Zombie'] > 0
&& pets['BearCub-Zombie'] > 0) {
set['achievements.monsterMagus'] = true;
}
}
if (user && user.items && user.items.mounts) {
const mounts = user.items.mounts;
if (mounts['Wolf-Zombie']
&& mounts['TigerCub-Zombie']
&& mounts['PandaCub-Zombie']
&& mounts['LionCub-Zombie']
&& mounts['Fox-Zombie']
&& mounts['FlyingPig-Zombie']
&& mounts['Dragon-Zombie']
&& mounts['Cactus-Zombie']
&& mounts['BearCub-Zombie'] ) {
set['achievements.undeadUndertaker'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
module.exports = async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2019-10-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};
@@ -1,82 +0,0 @@
/*
* Award Habitoween ladder items to participants in this month's Habitoween festivities
*/
/* eslint-disable no-console */
const MIGRATION_NAME = '20191031_habitoween_ladder'; // Update when running in future years
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {};
const inc = {
'items.food.Candy_Skeleton': 1,
'items.food.Candy_Base': 1,
'items.food.Candy_CottonCandyBlue': 1,
'items.food.Candy_CottonCandyPink': 1,
'items.food.Candy_Shade': 1,
'items.food.Candy_White': 1,
'items.food.Candy_Golden': 1,
'items.food.Candy_Zombie': 1,
'items.food.Candy_Desert': 1,
'items.food.Candy_Red': 1,
};
set.migration = MIGRATION_NAME;
if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Glow']) {
set['items.mounts.JackOLantern-Glow'] = true;
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Ghost']) {
set['items.pets.JackOLantern-Glow'] = 5;
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Ghost']) {
set['items.mounts.JackOLantern-Ghost'] = true;
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Base']) {
set['items.pets.JackOLantern-Ghost'] = 5;
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Base']) {
set['items.mounts.JackOLantern-Base'] = true;
} else {
set['items.pets.JackOLantern-Base'] = 5;
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$inc: inc, $set: set}).exec();
}
module.exports = async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2019-10-01')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};
@@ -1,109 +0,0 @@
/*
* Award Onboarding Achievements for existing users
*/
/* eslint-disable no-console */
import { model as User } from '../../website/server/models/user';
const MIGRATION_NAME = '20191218_onboarding_achievements';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count += 1;
const set = {};
set.migration = MIGRATION_NAME;
const hasPet = Object.keys(user.items.pets).find(petKey => {
const pet = user.items.pets[petKey];
if (pet >= 5) return true;
return false;
});
if (hasPet) {
set['achievements.hatchedPet'] = true;
}
const hasFedPet = Object.keys(user.items.pets).find(petKey => {
const pet = user.items.pets[petKey];
if (pet > 5) return true;
return false;
});
if (hasFedPet) {
set['achievements.fedPet'] = true;
}
const hasGear = Object.keys(user.items.gear.owned).find(gearKey => {
const gear = user.items.gear.owned[gearKey];
if (gear === true && gearKey.indexOf('_special_') === -1) return true;
return false;
});
if (hasGear) {
set['achievements.purchasedEquipment'] = true;
}
if (user.tasksOrder) {
const hasTask = Object.keys(user.tasksOrder).find(tasksOrderType => {
const order = user.tasksOrder[tasksOrderType];
if (order && order.length > 0) return true;
return false;
});
if (hasTask) {
set['achievements.createdTask'] = true;
}
const hasExperience = user.stats && user.stats.exp && user.stats.exp > 0;
if (hasTask && hasExperience) {
set['achievements.completedTask'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return User.update({ _id: user._id }, { $set: set }).exec();
}
module.exports = async function processUsers () { // eslint-disable-line import/no-commonjs
const query = {
migration: { $ne: MIGRATION_NAME },
};
const fields = {
_id: 1,
stats: 1,
items: 1,
achievements: 1,
tasksOrder: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(100)
.sort({ _id: 1 })
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};
@@ -1,126 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20191127_harvest_feast';
import { v4 as uuid } from 'uuid';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {};
let inc;
let push;
set.migration = MIGRATION_NAME;
if (typeof user.items.gear.owned.head_special_turkeyHelmGilded !== 'undefined') {
inc = {
'items.food.Pie_Base': 1,
'items.food.Pie_CottonCandyBlue': 1,
'items.food.Pie_CottonCandyPink': 1,
'items.food.Pie_Desert': 1,
'items.food.Pie_Golden': 1,
'items.food.Pie_Red': 1,
'items.food.Pie_Shade': 1,
'items.food.Pie_Skeleton': 1,
'items.food.Pie_Zombie': 1,
'items.food.Pie_White': 1,
}
} else if (typeof user.items.gear.owned.armor_special_turkeyArmorBase !== 'undefined') {
set['items.gear.owned.head_special_turkeyHelmGilded'] = false;
set['items.gear.owned.armor_special_turkeyArmorGilded'] = false;
set['items.gear.owned.back_special_turkeyTailGilded'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_turkeyHelmGilded',
_id: uuid(),
},
{
type: 'marketGear',
path: 'gear.flat.armor_special_turkeyArmorGilded',
_id: uuid(),
},
{
type: 'marketGear',
path: 'gear.flat.back_special_turkeyTailGilded',
_id: uuid(),
},
];
} else if (user.items && user.items.mounts && user.items.mounts['Turkey-Gilded']) {
set['items.gear.owned.head_special_turkeyHelmBase'] = false;
set['items.gear.owned.armor_special_turkeyArmorBase'] = false;
set['items.gear.owned.back_special_turkeyTailBase'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_turkeyHelmBase',
_id: uuid(),
},
{
type: 'marketGear',
path: 'gear.flat.armor_special_turkeyArmorBase',
_id: uuid(),
},
{
type: 'marketGear',
path: 'gear.flat.back_special_turkeyTailBase',
_id: uuid(),
},
];
} else if (user.items && user.items.pets && user.items.pets['Turkey-Gilded']) {
set['items.mounts.Turkey-Gilded'] = true;
} else if (user.items && user.items.mounts && user.items.mounts['Turkey-Base']) {
set['items.pets.Turkey-Gilded'] = 5;
} else if (user.items && user.items.pets && user.items.pets['Turkey-Base']) {
set['items.mounts.Turkey-Base'] = true;
} else {
set['items.pets.Turkey-Base'] = 5;
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
if (inc) {
return await User.update({_id: user._id}, {$inc: inc, $set: set}).exec();
} else if (push) {
return await User.update({_id: user._id}, {$set: set, $push: {pinnedItems: {$each: push}}}).exec();
} else {
return await User.update({_id: user._id}, {$set: set}).exec();
}
}
module.exports = async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2019-11-01')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};
@@ -1,82 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20191210_pet_color_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
let set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['Wolf-White'] > 0
&& pets['TigerCub-White'] > 0
&& pets['PandaCub-White'] > 0
&& pets['LionCub-White'] > 0
&& pets['Fox-White'] > 0
&& pets['FlyingPig-White'] > 0
&& pets['Dragon-White'] > 0
&& pets['Cactus-White'] > 0
&& pets['BearCub-White'] > 0) {
set['achievements.primedForPainting'] = true;
}
}
if (user && user.items && user.items.mounts) {
const mounts = user.items.mounts;
if (mounts['Wolf-White']
&& mounts['TigerCub-White']
&& mounts['PandaCub-White']
&& mounts['LionCub-White']
&& mounts['Fox-White']
&& mounts['FlyingPig-White']
&& mounts['Dragon-White']
&& mounts['Cactus-White']
&& mounts['BearCub-White'] ) {
set['achievements.pearlyPro'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
module.exports = async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2019-12-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};
-118
View File
@@ -1,118 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20191231_nye';
import { model as User } from '../../../website/server/models/user';
import { v4 as uuid } from 'uuid';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {'flags.newStuff': true};
let push;
set.migration = MIGRATION_NAME;
if (typeof user.items.gear.owned.head_special_nye2018 !== 'undefined') {
set['items.gear.owned.head_special_nye2019'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2019',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2017 !== 'undefined') {
set['items.gear.owned.head_special_nye2018'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2018',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2016 !== 'undefined') {
set['items.gear.owned.head_special_nye2017'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2017',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') {
set['items.gear.owned.head_special_nye2016'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2016',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') {
set['items.gear.owned.head_special_nye2015'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2015',
_id: uuid(),
},
];
} else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') {
set['items.gear.owned.head_special_nye2014'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye2014',
_id: uuid(),
},
];
} else {
set['items.gear.owned.head_special_nye'] = false;
push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_nye',
_id: uuid(),
},
];
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$set: set, $push: {pinnedItems: {$each: push}}}).exec();
}
export default async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};
@@ -1,91 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20200131_habit_birthday';
import { v4 as uuid } from 'uuid';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const inc = {
'items.food.Cake_Skeleton': 1,
'items.food.Cake_Base': 1,
'items.food.Cake_CottonCandyBlue': 1,
'items.food.Cake_CottonCandyPink': 1,
'items.food.Cake_Shade': 1,
'items.food.Cake_White': 1,
'items.food.Cake_Golden': 1,
'items.food.Cake_Zombie': 1,
'items.food.Cake_Desert': 1,
'items.food.Cake_Red': 1,
'achievements.habitBirthdays': 1,
};
const set = {};
let push;
set.migration = MIGRATION_NAME;
if (typeof user.items.gear.owned.armor_special_birthday2019 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2020'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2020', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday2018 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2019'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2019', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday2017 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2018'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2018', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday2016 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2017'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2017', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday2015 !== 'undefined') {
set['items.gear.owned.armor_special_birthday2016'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2016', _id: uuid()}};
} else if (typeof user.items.gear.owned.armor_special_birthday !== 'undefined') {
set['items.gear.owned.armor_special_birthday2015'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2015', _id: uuid()}};
} else {
set['items.gear.owned.armor_special_birthday'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday', _id: uuid()}};
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({_id: user._id}, {$inc: inc, $set: set, $push: push}).exec();
}
module.exports = async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2019-01-15')},
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};
@@ -1,82 +0,0 @@
/* eslint-disable no-console */
const MIGRATION_NAME = '20200218_pet_color_achievements';
import { model as User } from '../../../website/server/models/user';
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const set = {
migration: MIGRATION_NAME,
};
if (user && user.items && user.items.pets) {
const pets = user.items.pets;
if (pets['Wolf-CottonCandyPink'] > 0
&& pets['TigerCub-CottonCandyPink'] > 0
&& pets['PandaCub-CottonCandyPink'] > 0
&& pets['LionCub-CottonCandyPink'] > 0
&& pets['Fox-CottonCandyPink'] > 0
&& pets['FlyingPig-CottonCandyPink'] > 0
&& pets['Dragon-CottonCandyPink'] > 0
&& pets['Cactus-CottonCandyPink'] > 0
&& pets['BearCub-CottonCandyPink'] > 0) {
set['achievements.tickledPink'] = true;
}
}
if (user && user.items && user.items.mounts) {
const mounts = user.items.mounts;
if (mounts['Wolf-CottonCandyPink']
&& mounts['TigerCub-CottonCandyPink']
&& mounts['PandaCub-CottonCandyPink']
&& mounts['LionCub-CottonCandyPink']
&& mounts['Fox-CottonCandyPink']
&& mounts['FlyingPig-CottonCandyPink']
&& mounts['Dragon-CottonCandyPink']
&& mounts['Cactus-CottonCandyPink']
&& mounts['BearCub-CottonCandyPink'] ) {
set['achievements.rosyOutlook'] = true;
}
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
return await User.update({ _id: user._id }, { $set: set }).exec();
}
module.exports = async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2020-02-01') },
};
const fields = {
_id: 1,
items: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1]._id,
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};
+110
View File
@@ -0,0 +1,110 @@
import monk from 'monk';
import nconf from 'nconf';
const migrationName = 'mystery-items-201808.js'; // Update per month
const authorName = 'Sabe'; // in case script author needs to know when their ...
const authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
/*
* Award this month's mystery items to subscribers
*/
const MYSTERY_ITEMS = ['armor_mystery_201810', 'head_mystery_201810'];
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
let UserNotification = require('../../website/server/models/userNotification').model;
function processUsers (lastId) {
// specify a query to limit the affected users (empty for all users):
let query = {
migration: {$ne: migrationName},
'purchased.plan.customerId': { $ne: null },
$or: [
{ 'purchased.plan.dateTerminated': { $gte: new Date() } },
{ 'purchased.plan.dateTerminated': { $exists: false } },
{ 'purchased.plan.dateTerminated': { $eq: null } },
],
};
if (lastId) {
query._id = {
$gt: lastId,
};
}
dbUsers.find(query, {
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) => {
console.log(err);
return exiting(1, `ERROR! ${ err}`);
});
}
let progressCount = 1000;
let count = 0;
function updateUsers (users) {
if (!users || users.length === 0) {
console.warn('All appropriate users found and modified.');
displayData();
return;
}
let userPromises = users.map(updateUser);
let lastUser = users[users.length - 1];
return Promise.all(userPromises)
.then(() => {
processUsers(lastUser._id);
});
}
function updateUser (user) {
count++;
const addToSet = {
'purchased.plan.mysteryItems': {
$each: MYSTERY_ITEMS,
},
};
const push = {
notifications: (new UserNotification({
type: 'NEW_MYSTERY_ITEMS',
data: {
MYSTERY_ITEMS,
},
})).toJSON(),
};
dbUsers.update({_id: user._id}, {$addToSet: addToSet, $push: push});
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`);
return exiting(0);
}
function exiting (code, msg) {
code = code || 0; // 0 = success
if (code && !msg) {
msg = 'ERROR!';
}
if (msg) {
if (code) {
console.error(msg);
} else {
console.log(msg);
}
}
process.exit(code);
}
module.exports = processUsers;
+11 -11
View File
@@ -2,15 +2,15 @@ import { model as Challenges } from '../../website/server/models/challenge';
import { model as User } from '../../website/server/models/user';
async function syncChallengeToMembers (challenges) {
const challengSyncPromises = challenges.map(async challenge => {
const users = await User.find({
let challengSyncPromises = challenges.map(async (challenge) => {
let users = await User.find({
// _id: '',
challenges: challenge._id,
}).exec();
const promises = [];
users.forEach(user => {
promises.push(challenge.syncTasksToUser(user));
let promises = [];
users.forEach((user) => {
promises.push(challenge.syncToUser(user));
promises.push(challenge.save());
promises.push(user.save());
});
@@ -18,11 +18,11 @@ async function syncChallengeToMembers (challenges) {
return Promise.all(promises);
});
return Promise.all(challengSyncPromises);
return await Promise.all(challengSyncPromises);
}
async function syncChallenges (lastChallengeDate) {
const query = {
let query = {
// _id: '',
};
@@ -30,16 +30,16 @@ async function syncChallenges (lastChallengeDate) {
query.createdOn = { $lte: lastChallengeDate };
}
const challengesFound = await Challenges.find(query)
let challengesFound = await Challenges.find(query)
.limit(10)
.sort('-createdAt')
.exec();
const syncedChallenges = await syncChallengeToMembers(challengesFound)
let syncedChallenges = await syncChallengeToMembers(challengesFound)
.catch(reason => console.error(reason));
const lastChallenge = challengesFound[challengesFound.length - 1];
let lastChallenge = challengesFound[challengesFound.length - 1];
if (lastChallenge) syncChallenges(lastChallenge.createdAt);
return syncedChallenges;
}
export default syncChallenges;
module.exports = syncChallenges;
+1 -1
View File
@@ -1 +1 @@
db.users.update({ _id: { $in: [''] } }, { $inc: { balance: 0.5 } }, { multi: true });
db.users.update({_id: {$in: ['']}}, {$inc: {balance: 0.5}}, {multi: true});
@@ -1,14 +1,11 @@
// mongo habitrpg ./node_modules/moment/moment.js ./migrations/cancelSubscription.js
// For some reason people often to contact me to cancel their sub,
// rather than do it online. Even when I point them to
// For some reason people often to contact me to cancel their sub, rather than do it online. Even when I point them to
// the FAQ (http://goo.gl/1uoPGQ) they insist...
db.users.update(
{ _id: '' },
{
$set: {
'purchased.plan.dateTerminated': moment().add('month', 1).toDate(),
},
},
);
{_id: ''},
{$set: {
'purchased.plan.dateTerminated': moment().add('month', 1).toDate(),
}}
);
+4 -3
View File
@@ -1,7 +1,8 @@
// Give contrib.level 7+ free subscription for life
db.users.update(
{
'contributor.level': { $gte: 7 },
'contributor.level': {$gte: 7},
'purchased.plan.customerId': null,
},
@@ -17,6 +18,6 @@ db.users.update(
},
},
{ multi: true },
{multi: true}
);
);
@@ -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() } },
);
{_id: ''},
{$set: {'purchased.plan.dateTerminated': moment().add({days: 7}).toDate()}}
);
@@ -39,52 +39,38 @@
// needed. Do not miss any of them!
const uuid = '30fb2640-7121-4968-ace5-f385e60ea6c5';
let 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}`); // eslint-disable-line no-restricted-globals
data.troublesomeIds.forEach((taskid) => {
print(`non-unique task: ${ taskid}`);
db.users.update({
_id: uuid,
todos: { $elemMatch: { id: taskid } },
@@ -95,7 +81,8 @@ db.users.aggregate([
});
db.users.update(
{ _id: uuid },
{ $pull: { todos: { id: 'de666' } } },
{ multi: false },
{_id: uuid},
{$pull: { todos: { id: 'de666' } } },
{multi: false }
);
+7 -7
View File
@@ -1,10 +1,10 @@
const oldId = '';
const newId = '';
const newUser = db.users.findOne({ _id: newId });
let oldId = '';
let newId = '';
let 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,
// 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}});
+4 -4
View File
@@ -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);
const found = _.some(user.tasks, { text: '' });
if (found) printjson({ id: user._id, auth: user.auth });
});
let found = _.some(user.tasks, {text: ''});
if (found) printjson({id: user._id, auth: user.auth});
});
+23 -25
View File
@@ -1,34 +1,32 @@
// 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(
// {_id:''},
// {$set:{'purchased.plan':{
// planId: 'basic_'+m+'mo',
// paymentMethod: 'Paypal',
// customerId: 'Gift',
// dateCreated: new Date(),
// dateTerminated: moment().add('month',m).toDate(),
// dateUpdated: new Date(),
// extraMonths: 0,
// gemsBought: 0,
// mysteryItems: [],
// consecutive: {
// count: 0,
// offset: m,
// gemCapExtra: m/3*5,
// trinkets: m/3
// }
// planId: 'basic_'+m+'mo',
// paymentMethod: 'Paypal',
// customerId: 'Gift',
// dateCreated: new Date(),
// dateTerminated: moment().add('month',m).toDate(),
// dateUpdated: new Date(),
// extraMonths: 0,
// gemsBought: 0,
// mysteryItems: [],
// consecutive: {
// count: 0,
// offset: m,
// gemCapExtra: m/3*5,
// trinkets: m/3
// }
// }}}
// )
// )
+2 -2
View File
@@ -1,5 +1,5 @@
db.users.update(
{},
{ $inc: { 'achievements.habiticaDays': 1 } },
{ multi: 1 },
{$inc: {'achievements.habiticaDays': 1}},
{multi: 1}
);
+1 -1
View File
@@ -1 +1 @@
db.users.update({ _id: '' }, { $inc: { balance: 5 } });
db.users.update({_id: ''}, {$inc: {balance: 5}});
@@ -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) {
const group = await Group.findOne({ _id: groupId });
let 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) {
const dateToEnd = moment(dateTerminated).toDate();
let dateToEnd = moment(dateTerminated).toDate();
group.purchased.plan.dateTerminated = dateToEnd;
}
// group.purchased.plan.owner = ObjectId();
@@ -31,12 +31,12 @@ async function addUnlimitedSubscription (groupId, dateTerminated) {
return group.save();
}
export default async function addUnlimitedSubscriptionCreator () {
const groupId = process.argv[2];
module.exports = async function addUnlimitedSubscriptionCreator () {
let groupId = process.argv[2];
if (!groupId) throw Error('Group ID is required');
const dateTerminated = process.argv[3];
let dateTerminated = process.argv[3];
await addUnlimitedSubscription(groupId, dateTerminated);
}
};
+10 -8
View File
@@ -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) {
const user = await User.findOne({ _id: leaderId });
let user = await User.findOne({_id: leaderId});
const group = new Group({
let group = new Group({
name,
privacy,
type,
@@ -17,11 +17,13 @@ async function createGroup (name, privacy, type, leaderId) {
return Promise.all([group.save(), user.save()]);
}
export default async function groupCreator () {
const name = process.argv[2];
const privacy = process.argv[3];
const type = process.argv[4];
const leaderId = process.argv[5];
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];
await createGroup(name, privacy, type, leaderId);
}
};
+16 -15
View File
@@ -1,43 +1,44 @@
/* let migrationName = 'Jackalopes for Unlimited Subscribers'; */
/*
* This migration will find users with unlimited subscriptions who are also eligible
* for Jackalope mounts, and award them
* This migration will find users with unlimited subscriptions who are also eligible for Jackalope mounts, and award them
*/
import { model as Group } from '../../website/server/models/group';
import { model as User } from '../../website/server/models/user';
async function handOutJackalopes () {
const promises = [];
const cursor = User.find({
let promises = [];
let 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);
const subscribedGroup = await Group.findOne({
_id: { $in: groupList },
'purchased.plan.planId': 'group_monthly',
'purchased.plan.dateTerminated': null,
},
{ _id: 1 });
let 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());
}
});
cursor.on('close', async () => {
console.log('done');
return Promise.all(promises);
return await Promise.all(promises);
});
}
export default handOutJackalopes;
module.exports = handOutJackalopes;
+3 -3
View File
@@ -8,7 +8,7 @@
*/
import { model as Group } from '../../website/server/models/group';
import { chatModel as Chat } from '../../website/server/models/message';
import { model as Chat } from '../../website/server/models/chat';
async function moveGroupChatToModel (skip = 0) {
const groups = await Group.find({})
@@ -40,7 +40,7 @@ async function moveGroupChatToModel (skip = 0) {
const reducedPromises = promises.reduce((acc, curr) => {
acc = acc.concat(curr); // eslint-disable-line no-param-reassign
acc = acc.concat(curr);
return acc;
}, []);
@@ -49,4 +49,4 @@ async function moveGroupChatToModel (skip = 0) {
moveGroupChatToModel(skip + 50);
}
export default moveGroupChatToModel;
module.exports = moveGroupChatToModel;
@@ -1,4 +1,4 @@
import monk from 'monk'; // eslint-disable-line import/no-extraneous-dependencies
import monk from 'monk';
import nconf from 'nconf';
import stripePayments from '../../website/server/libs/payments/stripe';
@@ -12,8 +12,8 @@ import stripePayments from '../../website/server/libs/payments/stripe';
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
const dbGroups = monk(CONNECTION_STRING).get('groups', { castIds: false });
const dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
let dbGroups = monk(CONNECTION_STRING).get('groups', { castIds: false });
let 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,19 +40,19 @@ 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 += 1;
groupPlanCount++;
const canonicalMemberCount = await dbUsers.count(
{
$or:
[
{ 'party._id': group._id },
{ guilds: group._id },
{'party._id': group._id},
{guilds: group._id},
],
},
}
);
const incorrectMemberCount = group.memberCount !== canonicalMemberCount;
@@ -73,24 +73,24 @@ async function fixGroupPlanMembers () {
$set: {
memberCount: canonicalMemberCount,
},
},
}
);
if (!groupUpdate) return;
fixedGroupCount += 1;
fixedGroupCount++;
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,10 +98,10 @@ 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);
});
}
export default fixGroupPlanMembers;
module.exports = fixGroupPlanMembers;
@@ -5,27 +5,29 @@ let authorUuid = ''; // ... own data is done
*/
/*
* This migrations will iterate through all groups with a group plan
* a subscription and resync the free subscription to all members
* This migrations will iterate through all groups with a group plan a subscription and resync the free
* subscription to all members
*/
import { model as Group } from '../../website/server/models/group';
import payments from '../../website/server/libs/payments/payments';
import * as payments from '../../website/server/libs/payments';
async function updateGroupsWithGroupPlans () {
const cursor = Group.find({
let cursor = Group.find({
'purchased.plan.planId': 'group_monthly',
'purchased.plan.dateTerminated': null,
}).cursor();
const promises = [];
let promises = [];
cursor.on('data', group => {
cursor.on('data', (group) => {
promises.push(payments.addSubscriptionToGroupUsers(group));
promises.push(group.save());
});
cursor.on('close', async () => Promise.all(promises));
cursor.on('close', async () => {
return await Promise.all(promises);
});
}
export default updateGroupsWithGroupPlans;
module.exports = updateGroupsWithGroupPlans;
+4 -6
View File
@@ -1,5 +1,4 @@
/* eslint-disable import/no-commonjs */
require('@babel/register'); // eslint-disable-line import/no-extraneous-dependencies
require('babel-register');
// This file must use ES5, everything required can be in ES6
@@ -18,13 +17,12 @@ function setUpServer () {
setUpServer();
// Replace this with your migration
const processUsers = () => {}; // require('').default;
const processUsers = require('');
processUsers()
.then(() => {
.then(function success () {
process.exit(0);
})
.catch(err => {
.catch(function failure (err) {
console.log(err);
process.exit(1);
});
+24 -29
View File
@@ -1,21 +1,19 @@
/* eslint-disable import/no-commonjs */
/* let migrationName = 'new_stuff.js'; */
const authorName = 'Sabe'; // in case script author needs to know when their ...
const authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
let authorName = 'Sabe'; // in case script author needs to know when their ...
let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
/*
* set the newStuff flag in all user accounts so they see a Bailey message
*/
const monk = require('monk'); // eslint-disable-line import/no-extraneous-dependencies
const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
const dbUsers = monk(connectionString).get('users', { castIds: false });
let monk = require('monk');
let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
let dbUsers = monk(connectionString).get('users', { castIds: false });
function processUsers (lastId) {
// specify a query to limit the affected users (empty for all users):
const query = {
'flags.newStuff': { $ne: true },
let query = {
'flags.newStuff': {$ne: true},
};
if (lastId) {
@@ -25,31 +23,29 @@ function processUsers (lastId) {
}
dbUsers.find(query, {
sort: { _id: 1 },
sort: {_id: 1},
limit: 250,
// specify fields we are interested in to limit retrieved data
// (empty if we're not reading data):
fields: [],
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}`);
});
}
const progressCount = 1000;
let progressCount = 1000;
let count = 0;
function updateUsers (users) {
if (!users || users.length === 0) {
console.warn('All appropriate users found and modified.');
displayData();
return null;
return;
}
const userPromises = users.map(updateUser);
const lastUser = users[users.length - 1];
let userPromises = users.map(updateUser);
let lastUser = users[users.length - 1];
return Promise.all(userPromises)
.then(() => {
@@ -58,31 +54,30 @@ function updateUsers (users) {
}
function updateUser (user) {
count += 1;
count++;
const set = { 'flags.newStuff': true };
let 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);
}
function exiting (code, msg) {
// 0 = success
code = code || 0; // eslint-disable-line no-param-reassign
code = code || 0; // 0 = success
if (code && !msg) {
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
msg = 'ERROR!';
}
if (msg) {
if (code) {
console.error(msg);
} else {
} else {
console.log(msg);
}
}
+24 -29
View File
@@ -1,20 +1,18 @@
/* eslint-disable import/no-commonjs */
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
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
/*
* Remove flag stating that the Enchanted Armoire is empty, for when new equipment is added
*/
const monk = require('monk'); // eslint-disable-line import/no-extraneous-dependencies
const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
const dbUsers = monk(connectionString).get('users', { castIds: false });
let monk = require('monk');
let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
let dbUsers = monk(connectionString).get('users', { castIds: false });
function processUsers (lastId) {
// specify a query to limit the affected users (empty for all users):
const query = {
let query = {
'flags.armoireEmpty': true,
};
@@ -25,31 +23,29 @@ function processUsers (lastId) {
}
dbUsers.find(query, {
sort: { _id: 1 },
sort: {_id: 1},
limit: 250,
// specify fields we are interested in to limit retrieved data
// (empty if we're not reading data):
fields: [],
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}`);
});
}
const progressCount = 1000;
let progressCount = 1000;
let count = 0;
function updateUsers (users) {
if (!users || users.length === 0) {
console.warn('All appropriate users found and modified.');
displayData();
return null;
return;
}
const userPromises = users.map(updateUser);
const lastUser = users[users.length - 1];
let userPromises = users.map(updateUser);
let lastUser = users[users.length - 1];
return Promise.all(userPromises)
.then(() => {
@@ -58,31 +54,30 @@ function updateUsers (users) {
}
function updateUser (user) {
count += 1;
count++;
const set = { migration: migrationName, 'flags.armoireEmpty': false };
let 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);
}
function exiting (code, msg) {
// 0 = success
code = code || 0; // eslint-disable-line no-param-reassign
code = code || 0; // 0 = success
if (code && !msg) {
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
msg = 'ERROR!';
}
if (msg) {
if (code) {
console.error(msg);
} else {
} else {
console.log(msg);
}
}
@@ -1,7 +1,6 @@
/* eslint-disable import/no-commonjs */
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
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
/*
* Remove flag stating that the Enchanted Armoire is empty,
@@ -19,17 +18,16 @@ const authorUuid = '3e595299-3d8a-4a10-bfe0-88f555e4aa0c'; // ... own data is do
*
*/
const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
const monk = require('monk'); // eslint-disable-line import/no-extraneous-dependencies
const dbUsers = monk(connectionString).get('users', { castIds: false });
let monk = require('monk');
let dbUsers = monk(connectionString).get('users', { castIds: false });
function processUsers (lastId) {
// specify a query to limit the affected users (empty for all users):
const query = {
'auth.timestamps.loggedin': { $gt: new Date('2016-01-04') },
let query = {
'auth.timestamps.loggedin': {$gt: new Date('2016-01-04')},
// '_id': authorUuid // FOR TESTING
};
@@ -37,7 +35,7 @@ function processUsers (lastId) {
/* let fields = {
'flags.armoireEmpty': 1,
'items.gear.owned': 1,
}; */
};*/
if (lastId) {
query._id = {
@@ -46,34 +44,32 @@ function processUsers (lastId) {
}
dbUsers.find(query, {
sort: { _id: 1 },
sort: {_id: 1},
limit: 250,
// specify fields we are interested in to limit retrieved data
// (empty if we're not reading data):
fields: {
'flags.armoireEmpty': 1,
'items.gear.owned': 1,
},
}, // 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}`);
});
}
const progressCount = 1000;
let progressCount = 1000;
let count = 0;
function updateUsers (users) {
if (!users || users.length === 0) {
console.warn('All appropriate users found and modified.');
displayData();
return null;
return;
}
const userPromises = users.map(updateUser);
const lastUser = users[users.length - 1];
let userPromises = users.map(updateUser);
let lastUser = users[users.length - 1];
return Promise.all(userPromises)
.then(() => {
@@ -82,69 +78,19 @@ function updateUsers (users) {
}
function updateUser (user) {
count += 1;
count++;
const set = { migration: migrationName, 'flags.armoireEmpty': false };
let set = {migration: migrationName, 'flags.armoireEmpty': false};
if (user.flags.armoireEmpty) {
// this user believes their armoire has no more items in it
if (
user.items.gear.owned.weapon_armoire_barristerGavel
&& user.items.gear.owned.armor_armoire_barristerRobes
&& user.items.gear.owned.head_armoire_jesterCap
&& user.items.gear.owned.armor_armoire_jesterCostume
&& user.items.gear.owned.head_armoire_barristerWig
&& user.items.gear.owned.weapon_armoire_jesterBaton
&& user.items.gear.owned.weapon_armoire_lunarSceptre
&& user.items.gear.owned.armor_armoire_gladiatorArmor
&& user.items.gear.owned.weapon_armoire_basicCrossbow
&& user.items.gear.owned.head_armoire_gladiatorHelm
&& user.items.gear.owned.armor_armoire_lunarArmor
&& user.items.gear.owned.head_armoire_redHairbow
&& user.items.gear.owned.head_armoire_violetFloppyHat
&& user.items.gear.owned.head_armoire_rancherHat
&& user.items.gear.owned.shield_armoire_gladiatorShield
&& user.items.gear.owned.head_armoire_blueHairbow
&& user.items.gear.owned.weapon_armoire_mythmakerSword
&& user.items.gear.owned.head_armoire_royalCrown
&& user.items.gear.owned.head_armoire_hornedIronHelm
&& user.items.gear.owned.weapon_armoire_rancherLasso
&& user.items.gear.owned.armor_armoire_rancherRobes
&& user.items.gear.owned.armor_armoire_hornedIronArmor
&& user.items.gear.owned.armor_armoire_goldenToga
&& user.items.gear.owned.weapon_armoire_ironCrook
&& user.items.gear.owned.head_armoire_goldenLaurels
&& user.items.gear.owned.head_armoire_redFloppyHat
&& user.items.gear.owned.armor_armoire_plagueDoctorOvercoat
&& user.items.gear.owned.head_armoire_plagueDoctorHat
&& user.items.gear.owned.weapon_armoire_goldWingStaff
&& user.items.gear.owned.head_armoire_yellowHairbow
&& user.items.gear.owned.eyewear_armoire_plagueDoctorMask
&& user.items.gear.owned.head_armoire_blackCat
&& user.items.gear.owned.weapon_armoire_batWand
&& user.items.gear.owned.head_armoire_orangeCat
&& user.items.gear.owned.shield_armoire_midnightShield
&& user.items.gear.owned.armor_armoire_royalRobes
&& user.items.gear.owned.head_armoire_blueFloppyHat
&& user.items.gear.owned.shield_armoire_royalCane
&& user.items.gear.owned.weapon_armoire_shepherdsCrook
&& user.items.gear.owned.armor_armoire_shepherdRobes
&& user.items.gear.owned.head_armoire_shepherdHeaddress
&& user.items.gear.owned.weapon_armoire_blueLongbow
&& user.items.gear.owned.weapon_armoire_crystalCrescentStaff
&& user.items.gear.owned.head_armoire_crystalCrescentHat
&& user.items.gear.owned.armor_armoire_dragonTamerArmor
&& user.items.gear.owned.head_armoire_dragonTamerHelm
&& user.items.gear.owned.armor_armoire_crystalCrescentRobes
&& user.items.gear.owned.shield_armoire_dragonTamerShield
&& user.items.gear.owned.weapon_armoire_glowingSpear
) {
if (user.items.gear.owned.weapon_armoire_barristerGavel && user.items.gear.owned.armor_armoire_barristerRobes && user.items.gear.owned.head_armoire_jesterCap && user.items.gear.owned.armor_armoire_jesterCostume && user.items.gear.owned.head_armoire_barristerWig && user.items.gear.owned.weapon_armoire_jesterBaton && user.items.gear.owned.weapon_armoire_lunarSceptre && user.items.gear.owned.armor_armoire_gladiatorArmor && user.items.gear.owned.weapon_armoire_basicCrossbow && user.items.gear.owned.head_armoire_gladiatorHelm && user.items.gear.owned.armor_armoire_lunarArmor && user.items.gear.owned.head_armoire_redHairbow && user.items.gear.owned.head_armoire_violetFloppyHat && user.items.gear.owned.head_armoire_rancherHat && user.items.gear.owned.shield_armoire_gladiatorShield && user.items.gear.owned.head_armoire_blueHairbow && user.items.gear.owned.weapon_armoire_mythmakerSword && user.items.gear.owned.head_armoire_royalCrown && user.items.gear.owned.head_armoire_hornedIronHelm && user.items.gear.owned.weapon_armoire_rancherLasso && user.items.gear.owned.armor_armoire_rancherRobes && user.items.gear.owned.armor_armoire_hornedIronArmor && user.items.gear.owned.armor_armoire_goldenToga && user.items.gear.owned.weapon_armoire_ironCrook && user.items.gear.owned.head_armoire_goldenLaurels && user.items.gear.owned.head_armoire_redFloppyHat && user.items.gear.owned.armor_armoire_plagueDoctorOvercoat && user.items.gear.owned.head_armoire_plagueDoctorHat && user.items.gear.owned.weapon_armoire_goldWingStaff && user.items.gear.owned.head_armoire_yellowHairbow && user.items.gear.owned.eyewear_armoire_plagueDoctorMask && user.items.gear.owned.head_armoire_blackCat && user.items.gear.owned.weapon_armoire_batWand && user.items.gear.owned.head_armoire_orangeCat && user.items.gear.owned.shield_armoire_midnightShield && user.items.gear.owned.armor_armoire_royalRobes && user.items.gear.owned.head_armoire_blueFloppyHat && user.items.gear.owned.shield_armoire_royalCane && user.items.gear.owned.weapon_armoire_shepherdsCrook && user.items.gear.owned.armor_armoire_shepherdRobes && user.items.gear.owned.head_armoire_shepherdHeaddress && user.items.gear.owned.weapon_armoire_blueLongbow && user.items.gear.owned.weapon_armoire_crystalCrescentStaff && user.items.gear.owned.head_armoire_crystalCrescentHat && user.items.gear.owned.armor_armoire_dragonTamerArmor && user.items.gear.owned.head_armoire_dragonTamerHelm && user.items.gear.owned.armor_armoire_crystalCrescentRobes && user.items.gear.owned.shield_armoire_dragonTamerShield && user.items.gear.owned.weapon_armoire_glowingSpear) {
// this user does have all the armoire items so we don't change the flag
// 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
@@ -152,25 +98,24 @@ 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);
}
function exiting (code, msg) {
// 0 = success
code = code || 0; // eslint-disable-line no-param-reassign
code = code || 0; // 0 = success
if (code && !msg) {
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
msg = 'ERROR!';
}
if (msg) {
if (code) {
console.error(msg);
} else {
} else {
console.log(msg);
}
}
+33 -39
View File
@@ -1,25 +1,22 @@
/* eslint-disable import/no-commonjs */
/* let migrationName = 'restore_profile_data.js'; */
const authorName = 'ThehollidayInn'; // in case script author needs to know when their ...
const authorUuid = ''; // ... own data is done
let authorName = 'ThehollidayInn'; // in case script author needs to know when their ...
let authorUuid = ''; // ... own data is done
/*
* Check if users have empty profile data in new database and update it with old database info
*/
const monk = require('monk'); // eslint-disable-line import/no-extraneous-dependencies
let monk = require('monk');
let connectionString = ''; // FOR TEST DATABASE
let dbUsers = monk(connectionString).get('users', { castIds: false });
const connectionString = ''; // FOR TEST DATABASE
const dbUsers = monk(connectionString).get('users', { castIds: false });
const monk2 = require('monk'); // eslint-disable-line import/no-extraneous-dependencies
const oldDbConnectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
const olDbUsers = monk2(oldDbConnectionString).get('users', { castIds: false });
let monk2 = require('monk');
let oldDbConnectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
let olDbUsers = monk2(oldDbConnectionString).get('users', { castIds: false });
function processUsers (lastId) {
// specify a query to limit the affected users (empty for all users):
const query = {
let query = {
// 'profile.name': 'profile name not found',
'profile.blurb': null,
// 'auth.timestamps.loggedin': {$gt: new Date('11/30/2016')},
@@ -32,47 +29,49 @@ 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}`);
});
}
const progressCount = 1000;
let progressCount = 1000;
let count = 0;
function updateUsers (users) {
if (!users || users.length === 0) {
console.warn('All appropriate users found and modified.');
setTimeout(displayData, 300000);
return null;
return;
}
const userPaymentPromises = users.map(updateUser);
const lastUser = users[users.length - 1];
let userPaymentPromises = users.map(updateUser);
let lastUser = users[users.length - 1];
return Promise.all(userPaymentPromises)
.then(() => processUsers(lastUser._id));
.then(() => {
return processUsers(lastUser._id);
});
}
function updateUser (user) {
count += 1;
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 => {
if (!oldUserData) return null;
return olDbUsers.findOne({_id: user._id}, '_id profile')
.then((oldUserData) => {
if (!oldUserData) return;
// specify user data to change:
const set = {};
let set = {};
if (oldUserData.profile.name === 'profile name not found') return null;
if (oldUserData.profile.name === 'profile name not found') return;
const userNeedsProfileName = !user.profile.name || user.profile.name === 'profile name not found';
let userNeedsProfileName = !user.profile.name || user.profile.name === 'profile name not found';
if (userNeedsProfileName && oldUserData.profile.name) {
set['profile.name'] = oldUserData.profile.name;
}
@@ -87,34 +86,29 @@ 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});
}
return null;
});
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
if (user._id === authorUuid) console.warn(`${authorName} processed`);
return null;
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);
}
function exiting (code, msg) {
// 0 = success
code = code || 0; // eslint-disable-line no-param-reassign
code = code || 0; // 0 = success
if (code && !msg) {
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
msg = 'ERROR!';
}
if (msg) {
if (code) {
console.error(msg);
} else {
} else {
console.log(msg);
}
}
+26 -22
View File
@@ -1,10 +1,8 @@
/* eslint-disable import/no-commonjs */
const request = require('superagent');
const last = require('lodash/last');
const AWS = require('aws-sdk');
const config = require('../config');
let request = require('superagent');
let last = require('lodash/last');
let AWS = require('aws-sdk');
let config = require('../config');
const S3_DIRECTORY = 'mobileApp/images'; // config.S3.SPRITES_DIRECTORY;
AWS.config.update({
@@ -13,8 +11,8 @@ AWS.config.update({
// region: config.get('S3_REGION'),
});
const BUCKET_NAME = config.S3.bucket;
const s3 = new AWS.S3();
let BUCKET_NAME = config.S3.bucket;
let s3 = new AWS.S3();
// Adapted from http://stackoverflow.com/a/22210077/2601552
function uploadFile (buffer, fileName) {
@@ -23,11 +21,11 @@ function uploadFile (buffer, fileName) {
Body: buffer,
Key: fileName,
Bucket: BUCKET_NAME,
}, error => {
}, (error) => {
if (error) {
reject(error);
} else {
// console.info(`${fileName} uploaded to ${BUCKET_NAME} successfully.`);
// console.info(`${fileName} uploaded to ${BUCKET_NAME} succesfully.`);
resolve(fileName);
}
});
@@ -35,9 +33,9 @@ function uploadFile (buffer, fileName) {
}
function getFileName (file) {
const piecesOfPath = file.split('/');
const name = last(piecesOfPath);
const fullName = S3_DIRECTORY + name;
let piecesOfPath = file.split('/');
let name = last(piecesOfPath);
let fullName = S3_DIRECTORY + name;
return fullName;
}
@@ -46,32 +44,36 @@ function getFileFromUrl (url) {
return new Promise((resolve, reject) => {
request.get(url).end((err, res) => {
if (err) return reject(err);
const file = res.body;
return resolve(file);
let file = res.body;
resolve(file);
});
});
}
let commit = '78f94e365c72cc58f66857d5941105638db7d35c';
commit = 'df0dbaba636c9ce424cc7040f7bd7fc1aa311015';
const gihuburl = `https://api.github.com/repos/HabitRPG/habitica/commits/${commit}`;
let gihuburl = `https://api.github.com/repos/HabitRPG/habitica/commits/${commit}`;
let currentIndex = 0;
function uploadToS3 (start, end, filesUrls) {
const urls = filesUrls.slice(start, end);
let urls = filesUrls.slice(start, end);
if (urls.length === 0) {
console.log('done');
return;
}
const promises = urls.map(fullUrl => getFileFromUrl(fullUrl)
.then(buffer => uploadFile(buffer, getFileName(fullUrl))));
let promises = urls.map(fullUrl => {
return getFileFromUrl(fullUrl)
.then((buffer) => {
return uploadFile(buffer, getFileName(fullUrl));
});
});
console.log(promises.length);
Promise.all(promises)
return Promise.all(promises)
.then(() => {
currentIndex += 50;
uploadToS3(currentIndex, currentIndex + 50, filesUrls);
@@ -84,10 +86,12 @@ function uploadToS3 (start, end, filesUrls) {
request.get(gihuburl)
.end((err, res) => {
console.log(err);
const { files } = res.body;
let files = res.body.files;
let filesUrls = [''];
filesUrls = files.map(file => file.raw_url);
filesUrls = files.map(file => {
return file.raw_url;
});
uploadToS3(currentIndex, currentIndex + 50, filesUrls);
});
@@ -1,23 +1,21 @@
/* eslint-disable import/no-commonjs */
// const migrationName = 'habits-one-history-entry-per-day';
// const authorName = 'paglias'; // in case script author needs to know when their ...
// const authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; // ... own data is done
/*
* Iterates over all habits and condense multiple history entries for the same day into a single one
* Iterates over all habits and condense multiple history entries for the same day into a single entry
*/
const monk = require('monk'); // eslint-disable-line import/no-extraneous-dependencies
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) {
const query = {
'challenge.id': { $exists: true },
userId: { $exists: false },
let query = {
'challenge.id': {$exists: true},
userId: {$exists: false},
type: 'habit',
};
@@ -28,35 +26,37 @@ 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}`);
});
}
const progressCount = 1000;
let progressCount = 1000;
let count = 0;
function updateChallengeHabits (habits) {
if (!habits || habits.length === 0) {
console.warn('All appropriate challenge habits found and modified.');
displayData();
return null;
return;
}
const habitsPromises = habits.map(updateChallengeHabit);
const lastHabit = habits[habits.length - 1];
let habitsPromises = habits.map(updateChallengeHabit);
let lastHabit = habits[habits.length - 1];
return Promise.all(habitsPromises)
.then(() => processChallengeHabits(lastHabit._id));
.then(() => {
return processChallengeHabits(lastHabit._id);
});
}
function updateChallengeHabit (habit) {
count += 1;
count++;
if (habit && habit.history && habit.history.length > 0) {
// First remove missing entries
@@ -76,12 +76,13 @@ function updateChallengeHabit (habit) {
entry.scoreDirection = entry.value > previousValue ? 'up' : 'down';
}
})
// group entries by aggregateBy
.groupBy(entry => moment(entry.date).format('YYYYMMDD'))
.groupBy(entry => { // group entries by aggregateBy
return moment(entry.date).format('YYYYMMDD');
})
.toPairs() // [key, entry]
.sortBy(([key]) => key) // sort by date
.map(keyEntryPair => {
const entries = keyEntryPair[1]; // 1 is entry, 0 is key
let entries = keyEntryPair[1]; // 1 is entry, 0 is key
let scoredUp = 0;
let scoredDown = 0;
@@ -106,34 +107,32 @@ 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`);
return null;
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);
}
function exiting (code, msg) {
// 0 = success
code = code || 0; // eslint-disable-line no-param-reassign
code = code || 0; // 0 = success
if (code && !msg) {
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
msg = 'ERROR!';
}
if (msg) {
if (code) {
console.error(msg);
} else {
} else {
console.log(msg);
}
}
process.exit(code);
}
export default processChallengeHabits;
module.exports = processChallengeHabits;
@@ -1,23 +1,21 @@
/* eslint-disable import/no-commonjs */
const migrationName = 'habits-one-history-entry-per-day';
const authorName = 'paglias'; // in case script author needs to know when their ...
const authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; // ... own data is done
/*
* Iterates over all habits and condense multiple history entries for the same day into a single one
* Iterates over all habits and condense multiple history entries for the same day into a single entry
*/
const monk = require('monk'); // eslint-disable-line import/no-extraneous-dependencies
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) {
const query = {
migration: { $ne: migrationName },
let query = {
migration: {$ne: migrationName},
};
if (lastId) {
@@ -27,32 +25,34 @@ 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}`);
});
}
const progressCount = 1000;
let progressCount = 1000;
let count = 0;
function updateUsers (users) {
if (!users || users.length === 0) {
console.warn('All appropriate users and their tasks found and modified.');
displayData();
return null;
return;
}
const usersPromises = users.map(updateUser);
const lastUser = users[users.length - 1];
let usersPromises = users.map(updateUser);
let lastUser = users[users.length - 1];
return Promise.all(usersPromises)
.then(() => processUsers(lastUser._id));
.then(() => {
return processUsers(lastUser._id);
});
}
function updateHabit (habit, timezoneOffset, dayStart) {
@@ -82,7 +82,7 @@ function updateHabit (habit, timezoneOffset, dayStart) {
.toPairs() // [key, entry]
.sortBy(([key]) => key) // sort by date
.map(keyEntryPair => {
const entries = keyEntryPair[1]; // 1 is entry, 0 is key
let entries = keyEntryPair[1]; // 1 is entry, 0 is key
let scoredUp = 0;
let scoredDown = 0;
@@ -107,56 +107,57 @@ 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},
});
}
return null;
}
function updateUser (user) {
count += 1;
count++;
const { timezoneOffset } = user.preferences;
const { dayStart } = user.preferences;
const timezoneOffset = user.preferences.timezoneOffset;
const dayStart = user.preferences.dayStart;
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 => Promise.all(habits.map(habit => updateHabit(habit, timezoneOffset, dayStart))))
.then(() => dbUsers.update({ _id: user._id }, {
$set: { migration: migrationName },
}))
.catch(err => {
.then(habits => {
return Promise.all(habits.map(habit => updateHabit(habit, timezoneOffset, dayStart)));
})
.then(() => {
return 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);
}
function exiting (code, msg) {
// 0 = success
code = code || 0; // eslint-disable-line no-param-reassign
code = code || 0; // 0 = success
if (code && !msg) {
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
msg = 'ERROR!';
}
if (msg) {
if (code) {
console.error(msg);
} else {
} else {
console.log(msg);
}
}
process.exit(code);
}
export default processUsers;
module.exports = processUsers;
+27 -27
View File
@@ -1,19 +1,18 @@
import monk from 'monk'; // eslint-disable-line import/no-extraneous-dependencies
/* let migrationName = 'tasks-set-everyX'; */
const authorName = 'Sabe'; // in case script author needs to know when their ...
const authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
let authorName = 'Sabe'; // in case script author needs to know when their ...
let 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
* Iterates over all tasks and sets invalid everyX values (less than 0 or more than 9999 or not an int) field to 0
*/
const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true';
const dbTasks = monk(connectionString).get('tasks', { castIds: false });
let monk = require('monk');
let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true';
let dbTasks = monk(connectionString).get('tasks', { castIds: false });
function processTasks (lastId) {
// specify a query to limit the affected tasks (empty for all tasks):
const query = {
let query = {
type: 'daily',
everyX: {
$not: {
@@ -31,63 +30,64 @@ 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}`);
});
}
const progressCount = 1000;
let progressCount = 1000;
let count = 0;
function updateTasks (tasks) {
if (!tasks || tasks.length === 0) {
console.warn('All appropriate tasks found and modified.');
displayData();
return null;
return;
}
const taskPromises = tasks.map(updatetask);
const lasttask = tasks[tasks.length - 1];
let taskPromises = tasks.map(updatetask);
let lasttask = tasks[tasks.length - 1];
return Promise.all(taskPromises)
.then(() => processTasks(lasttask._id));
.then(() => {
return processTasks(lasttask._id);
});
}
function updatetask (task) {
count += 1;
const set = { everyX: 0 };
count++;
let 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);
}
function exiting (code, msg) {
// 0 = success
code = code || 0; // eslint-disable-line no-param-reassign
code = code || 0; // 0 = success
if (code && !msg) {
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
msg = 'ERROR!';
}
if (msg) {
if (code) {
console.error(msg);
} else {
} else {
console.log(msg);
}
}
process.exit(code);
}
export default processTasks;
module.exports = processTasks;
+27 -30
View File
@@ -1,31 +1,28 @@
/* let migrationName = 'tasks-set-yesterdaily'; */
// ... own data is done
let authorName = 'TheHollidayInn'; // in case script author needs to know when their ...
let authorUuid = ''; // ... own data is done
/*
* Iterates over all tasks and sets the yseterDaily field to True
*/
import monk from 'monk'; // eslint-disable-line import/no-extraneous-dependencies
import monk from 'monk';
const authorName = 'TheHollidayInn'; // in case script author needs to know when their ...
const authorUuid = '';
let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
let dbTasks = monk(connectionString).get('tasks', { castIds: false });
const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
const dbTasks = monk(connectionString).get('tasks', { castIds: false });
const progressCount = 1000;
let progressCount = 1000;
let count = 0;
function exiting (code, msg) {
// 0 = success
code = code || 0; // eslint-disable-line no-param-reassign
code = code || 0; // 0 = success
if (code && !msg) {
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
msg = 'ERROR!';
}
if (msg) {
if (code) {
console.error(msg);
} else {
} else {
console.log(msg);
}
}
@@ -33,37 +30,39 @@ 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 += 1;
const set = { yesterDaily: true };
count++;
let 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) {
if (!tasks || tasks.length === 0) {
console.warn('All appropriate tasks found and modified.');
displayData();
return null;
return;
}
const taskPromises = tasks.map(updatetask);
const lasttask = tasks[tasks.length - 1];
let taskPromises = tasks.map(updatetask);
let lasttask = tasks[tasks.length - 1];
return Promise.all(taskPromises)
.then(() => processTasks(lasttask._id)); // eslint-disable-line no-use-before-define
.then(() => {
return 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):
const query = {
let query = {
yesterDaily: false,
};
@@ -74,18 +73,16 @@ function processTasks (lastId) {
}
dbTasks.find(query, {
sort: { _id: 1 },
sort: {_id: 1},
limit: 250,
// specify fields we are interested in to limit retrieved data
// (empty if we're not reading data):
fields: [
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}`);
});
}
export default processTasks;
module.exports = processTasks;
+10 -11
View File
@@ -8,30 +8,29 @@ let authorUuid = ''; // ... own data is done
* This migraition will copy user data from prod to test
*/
import monk from 'monk'; // eslint-disable-line import/no-extraneous-dependencies
const monk = require('monk');
const connectionString = '';
const Users = monk(connectionString).get('users', { castIds: false });
export default async function accountTransfer () {
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 = { ...fromAccount.items.mounts, ...toAccount.items.mounts };
const newPets = { ...fromAccount.items.pets, ...toAccount.items.pets };
const newBackgrounds = { ...fromAccount.purchased.background, ...toAccount.purchased.background };
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);
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);
});
}
};
+24 -27
View File
@@ -8,8 +8,7 @@ const authorUuid = ''; // ... own data is done
* This migraition will copy user data from prod to test
*/
import monk from 'monk'; // eslint-disable-line import/no-extraneous-dependencies
const monk = require('monk');
const connectionString = 'mongodb://localhost/new-habit';
const Users = monk(connectionString).get('users', { castIds: false });
@@ -20,17 +19,15 @@ function getAchievementUpdate (newUser, oldUser) {
const oldAchievements = oldUser.achievements;
const newAchievements = newUser.achievements;
const achievementsUpdate = { ...newAchievements };
let achievementsUpdate = Object.assign({}, newAchievements);
// ultimateGearSets
if (!achievementsUpdate.ultimateGearSets && oldAchievements.ultimateGearSets) {
achievementsUpdate.ultimateGearSets = oldAchievements.ultimateGearSets;
} else if (oldAchievements.ultimateGearSets) {
Object.keys(oldAchievements.ultimateGearSets).forEach(index => {
if (oldAchievements.ultimateGearSets[index]) {
achievementsUpdate.ultimateGearSets[index] = true;
}
});
for (let index in oldAchievements.ultimateGearSets) {
if (oldAchievements.ultimateGearSets[index]) achievementsUpdate.ultimateGearSets[index] = true;
}
}
// challenges
@@ -40,57 +37,57 @@ function getAchievementUpdate (newUser, oldUser) {
// Quests
if (!achievementsUpdate.quests) achievementsUpdate.quests = {};
Object.keys(oldAchievements.quests).forEach(index => {
for (let index in oldAchievements.quests) {
if (!achievementsUpdate.quests[index]) {
achievementsUpdate.quests[index] = oldAchievements.quests[index];
} else {
achievementsUpdate.quests[index] += oldAchievements.quests[index];
}
});
}
// Rebirth level
if (achievementsUpdate.rebirthLevel) {
achievementsUpdate.rebirthLevel = Math.max(
achievementsUpdate.rebirthLevel, oldAchievements.rebirthLevel,
);
achievementsUpdate.rebirthLevel = Math.max(achievementsUpdate.rebirthLevel, oldAchievements.rebirthLevel);
} else if (oldAchievements.rebirthLevel) {
achievementsUpdate.rebirthLevel = oldAchievements.rebirthLevel;
}
// All others
const indexsToIgnore = ['ultimateGearSets', 'challenges', 'quests', 'rebirthLevel'];
Object.keys(oldAchievements).forEach(index => {
if (indexsToIgnore.indexOf(index) !== -1) return;
for (let index in oldAchievements) {
if (indexsToIgnore.indexOf(index) !== -1) continue; // eslint-disable-line no-continue
if (!achievementsUpdate[index]) {
if (!achievementsUpdate[index]) {
achievementsUpdate[index] = oldAchievements[index];
return;
continue; // eslint-disable-line no-continue
}
if (Number.isInteger(oldAchievements[index])) {
achievementsUpdate[index] += oldAchievements[index];
} else if (oldAchievements[index] === true) achievementsUpdate[index] = true;
});
}
return achievementsUpdate;
}
export default async function achievementRestore () {
module.exports = async function achievementRestore () {
const userIds = [
];
await Promise.all(userIds.map(userId => (async () => {
const oldUser = await UsersOld.findOne({ _id: userId }, 'achievements');
const newUser = await Users.findOne({ _id: userId }, 'achievements');
/* eslint-disable no-await-in-loop */
for (let index in userIds) {
const userId = userIds[index];
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 */
}
};
+14 -18
View File
@@ -1,37 +1,33 @@
/* eslint-disable no-console */
import moment from 'moment';
import nconf from 'nconf';
import { sendTxn } from '../../website/server/libs/email';
import { model as User } from '../../website/server/models/user';
import moment from 'moment';
import nconf from 'nconf';
const BASE_URL = nconf.get('BASE_URL');
const EMAIL_SLUG = 'mandrill-email-slug'; // Set email template to send
const MIGRATION_NAME = 'bulk-email';
const progressCount = 250;
const progressCount = 1000;
let count = 0;
async function updateUser (user) {
count += 1;
count++;
if (count % progressCount === 0) {
console.warn(`${count} ${user._id}`);
await new Promise(resolve => setTimeout(resolve, 5000));
}
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
await sendTxn(
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 User.update({ _id: user._id }, { $set: { migration: MIGRATION_NAME } }).exec();
return await User.update({_id: user._id}, {$set: {migration: MIGRATION_NAME}}).exec();
}
export default async function processUsers () {
const query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: moment().subtract(2, 'weeks').toDate() }, // customize or remove to target different populations
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 fields = {
@@ -45,7 +41,7 @@ export default 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();
@@ -62,4 +58,4 @@ export default async function processUsers () {
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
}
};
+16 -17
View File
@@ -1,12 +1,11 @@
/* 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;
@@ -15,45 +14,45 @@ let count = 0;
*/
async function updateUser (user) {
count += 1;
count++;
const set = {};
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 User.update({ _id: user._id }, { $set: set }).exec();
return await User.update({_id: user._id}, {$set: set}).exec();
}
export default async function processUsers () {
const query = {
migration: { $ne: MIGRATION_NAME },
module.exports = async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.local.username': 'olson22',
};
@@ -65,7 +64,7 @@ export default 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();
@@ -82,4 +81,4 @@ export default async function processUsers () {
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
}
};
+71
View File
@@ -0,0 +1,71 @@
/* 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 progressCount = 1000;
let count = 0;
async function updateUser (user) {
count++;
const addToSet = {
'purchased.plan.mysteryItems': {
$each: MYSTERY_ITEMS,
},
};
const push = {
notifications: (new UserNotification({
type: 'NEW_MYSTERY_ITEMS',
data: {
MYSTERY_ITEMS,
},
})).toJSON(),
};
const set = {
migration: MIGRATION_NAME,
};
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
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},
'purchased.plan.customerId': { $ne: null },
$or: [
{ 'purchased.plan.dateTerminated': { $gte: new Date() } },
{ 'purchased.plan.dateTerminated': { $exists: false } },
{ 'purchased.plan.dateTerminated': { $eq: null } },
],
};
const fields = {
_id: 1,
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({_id: 1})
.select(fields)
.lean()
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
};
+11 -14
View File
@@ -1,15 +1,14 @@
/* 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;
async function updateUser (user) {
count *= 1;
count++;
const inc = {
'items.food.Pie_Skeleton': 1,
@@ -30,21 +29,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 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();
}
export default async function processUsers () {
const query = {
migration: { $ne: MIGRATION_NAME },
'auth.timestamps.loggedin': { $gt: new Date('2019-02-15') },
module.exports = async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
'auth.timestamps.loggedin': {$gt: new Date('2019-02-15')},
};
const fields = {
@@ -56,7 +53,7 @@ export default 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();
@@ -73,4 +70,4 @@ export default async function processUsers () {
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
}
};
@@ -1,5 +1,3 @@
import monk from 'monk'; // eslint-disable-line import/no-extraneous-dependencies
const migrationName = 'remove-social-users-extra-data.js';
const authorName = 'paglias'; // in case script author needs to know when their ...
const authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; // ... own data is done
@@ -9,13 +7,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):
const query = {
migration: { $ne: migrationName },
let query = {
migration: {$ne: migrationName},
$or: [
{ 'auth.facebook.id': { $exists: true } },
{ 'auth.google.id': { $exists: true } },
@@ -29,28 +27,28 @@ 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}`);
});
}
const progressCount = 1000;
let progressCount = 1000;
let count = 0;
function updateUsers (users) {
if (!users || users.length === 0) {
console.warn('All appropriate users found and modified.');
displayData();
return null;
return;
}
const userPromises = users.map(updateUser);
const lastUser = users[users.length - 1];
let userPromises = users.map(updateUser);
let lastUser = users[users.length - 1];
return Promise.all(userPromises)
.then(() => {
@@ -59,7 +57,7 @@ function updateUsers (users) {
}
function updateUser (user) {
count *= 1;
count++;
const isFacebook = user.auth.facebook && user.auth.facebook.id;
const isGoogle = user.auth.google && user.auth.google.id;
@@ -84,29 +82,28 @@ 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);
}
function exiting (code, msg) {
// 0 = success
code = code || 0; // eslint-disable-line no-param-reassign
code = code || 0; // 0 = success
if (code && !msg) {
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
msg = 'ERROR!';
}
if (msg) {
if (code) {
console.error(msg);
} else {
} else {
console.log(msg);
}
}
process.exit(code);
}
export default processUsers;
module.exports = processUsers;
+16 -16
View File
@@ -1,15 +1,14 @@
/* 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;
async function updateUser (user) {
count += 1;
count++;
const set = {};
let push;
@@ -20,35 +19,36 @@ 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 User.update({ _id: user._id }, { $set: set, $push: push }).exec();
return await User.update({_id: user._id}, {$set: set, $push: push}).exec();
} else {
return await User.update({_id: user._id}, {$set: set}).exec();
}
return User.update({ _id: user._id }, { $set: set }).exec();
}
export default async function processUsers () {
const query = {
migration: { $ne: MIGRATION_NAME },
module.exports = async function processUsers () {
let query = {
migration: {$ne: MIGRATION_NAME},
challenges: '00708425-d477-41a5-bf27-6270466e7976',
};
@@ -61,7 +61,7 @@ export default 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();
@@ -78,4 +78,4 @@ export default async function processUsers () {
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
}
}
};
+47 -42
View File
@@ -1,4 +1,3 @@
/* eslint-disable import/no-commonjs */
/*
let migrationName = 'UserFromProdToTest';
let authorName = 'TheHollidayInn'; // in case script author needs to know when their ...
@@ -9,24 +8,22 @@ 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'); // eslint-disable-line import/no-extraneous-dependencies
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'); // eslint-disable-line import/no-extraneous-dependencies
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 = [
@@ -39,11 +36,11 @@ let challengeIds = [];
let tasksIds = [];
async function processUsers () {
const userPromises = [];
let 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);
@@ -52,56 +49,64 @@ 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);
const userPromise = usersTest.update({ _id: user._id }, user, { upsert: true });
let userPromise = usersTest.update({_id: user._id}, user, {upsert: true});
userPromises.push(userPromise);
}).then(() => Promise.all(userPromises))
}).then(() => {
return Promise.all(userPromises);
})
.then(() => {
console.log('Done User');
});
}
function processGroups () {
const promises = [];
const groupsToQuery = uniq(groupIds);
return groupsLive.find({ _id: { $in: groupsToQuery } })
.each(group => {
const promise = groupsTest.update({ _id: group._id }, group, { upsert: true });
let promises = [];
let groupsToQuery = uniq(groupIds);
return groupsLive.find({_id: {$in: groupsToQuery}})
.each((group) => {
let promise = groupsTest.update({_id: group._id}, group, {upsert: true});
promises.push(promise);
}).then(() => Promise.all(promises))
}).then(() => {
return Promise.all(promises);
})
.then(() => {
console.log('Done Group');
});
}
function processChallenges () {
const promises = [];
const challengesToQuery = uniq(challengeIds);
return challengesLive.find({ _id: { $in: challengesToQuery } })
.each(challenge => {
const promise = challengesTest.update({ _id: challenge._id }, challenge, { upsert: true });
let promises = [];
let challengesToQuery = uniq(challengeIds);
return challengesLive.find({_id: {$in: challengesToQuery}})
.each((challenge) => {
let promise = challengesTest.update({_id: challenge._id}, challenge, {upsert: true});
promises.push(promise);
}).then(() => Promise.all(promises))
}).then(() => {
return Promise.all(promises);
})
.then(() => {
console.log('Done Challenge');
});
}
function processTasks () {
const promises = [];
const tasksToQuery = uniq(tasksIds);
return tasksLive.find({ _id: { $in: tasksToQuery } })
.each(task => {
const promise = tasksTest.update({ _id: task._id }, task, { upsert: true });
let promises = [];
let tasksToQuery = uniq(tasksIds);
return tasksLive.find({_id: {$in: tasksToQuery}})
.each((task) => {
let promise = tasksTest.update({_id: task._id}, task, {upsert: true});
promises.push(promise);
}).then(() => Promise.all(promises))
}).then(() => {
return Promise.all(promises);
})
.then(() => {
console.log('Done Tasks');
});
}
export default async function prodToTest () {
module.exports = async function prodToTest () {
await processUsers();
await processGroups();
await processChallenges();
await processTasks();
}
};
+4 -3
View File
@@ -1,5 +1,6 @@
/* eslint-disable import/no-commonjs */
const { MongoClient } = require('mongodb'); // eslint-disable-line import/no-extraneous-dependencies
'use strict';
const MongoClient = require('mongodb').MongoClient;
const logger = require('./logger');
let dbConnection;
@@ -16,7 +17,7 @@ function connectToDb (dbUri) {
logger.success(`Connected to ${dbUri}`);
return resolve(database);
resolve(database);
});
});
}
+4 -5
View File
@@ -1,11 +1,10 @@
/* eslint-disable import/no-commonjs */
const chalk = require('chalk'); // eslint-disable-line import/no-extraneous-dependencies
'use strict';
const chalk = require('chalk');
function loggerGenerator (type, color) {
return function logger () {
const args = Array
.from(arguments) // eslint-disable-line prefer-rest-params
.map(arg => chalk[color](arg));
let args = Array.from(arguments).map(arg => chalk[color](arg));
console[type].apply(null, args);
};
}
+10 -10
View File
@@ -1,30 +1,30 @@
/* eslint-disable import/no-commonjs */
const logger = require('./logger');
'use strict';
let logger = require('./logger');
class Timer {
constructor (options = {}) {
const warningThreshold = options.minutesWarningThreshold || 10;
constructor (options) {
options = options || {};
let warningThreshold = options.minutesWarningThreshold || 10;
this.count = 0;
this._minutesWarningThreshold = warningThreshold * 60;
if (!options.disableAutoStart) this.start();
}
start () {
this._internalTimer = setInterval(() => {
this.count *= 1;
this.count++;
const shouldWarn = this._minutesWarningThreshold < this.count;
const logStyle = shouldWarn ? 'error' : 'warn';
const dangerMessage = shouldWarn ? 'DANGER: ' : '';
let shouldWarn = this._minutesWarningThreshold < this.count;
let logStyle = shouldWarn ? 'error' : 'warn';
let 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');
+2 -1
View File
@@ -1,4 +1,5 @@
/* eslint-disable import/no-commonjs */
'use strict';
function unique (array) {
return Array.from(new Set(array));
}
+16853 -2849
View File
File diff suppressed because it is too large Load Diff
+116 -40
View File
@@ -1,77 +1,120 @@
{
"name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
"version": "4.136.0",
"version": "4.116.2",
"main": "./website/server/index.js",
"dependencies": {
"@babel/core": "^7.8.4",
"@babel/preset-env": "^7.8.4",
"@babel/register": "^7.8.3",
"@google-cloud/trace-agent": "^4.2.5",
"@google-cloud/trace-agent": "^4.0.0",
"@slack/client": "^3.8.1",
"accepts": "^1.3.5",
"amazon-payments": "^0.2.8",
"amazon-payments": "^0.2.7",
"amplitude": "^3.5.0",
"amplitude-js": "^5.2.2",
"apidoc": "^0.17.5",
"apn": "^2.2.0",
"aws-sdk": "^2.624.0",
"bcrypt": "^3.0.8",
"autoprefixer": "^9.4.0",
"aws-sdk": "^2.432.0",
"axios": "^0.19.0",
"axios-progress-bar": "^1.2.0",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.3",
"babel-loader": "^7.1.4",
"babel-plugin-syntax-async-functions": "^6.13.0",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
"babel-plugin-transform-object-rest-spread": "^6.16.0",
"babel-plugin-transform-regenerator": "^6.16.1",
"babel-polyfill": "^6.6.1",
"babel-preset-es2015": "^6.6.0",
"babel-register": "^6.6.0",
"babel-runtime": "^6.11.6",
"bcrypt": "^3.0.6",
"body-parser": "^1.18.3",
"bootstrap": "^4.1.1",
"bootstrap-vue": "^2.0.2",
"compression": "^1.7.4",
"cookie-session": "^1.4.0",
"cookie-session": "^1.3.3",
"coupon-code": "^0.4.5",
"csv-stringify": "^5.3.6",
"cross-env": "^6.0.0",
"css-loader": "^0.28.11",
"csv-stringify": "^5.1.0",
"cwait": "^1.1.1",
"domain-middleware": "~0.1.0",
"eslint": "^6.8.0",
"eslint-config-habitrpg": "^6.2.0",
"eslint-plugin-mocha": "^5.0.0",
"express": "^4.16.3",
"express-basic-auth": "^1.1.5",
"express-validator": "^5.2.0",
"glob": "^7.1.6",
"extract-text-webpack-plugin": "^3.0.2",
"glob": "^7.1.2",
"got": "^9.0.0",
"gulp": "^4.0.0",
"gulp-babel": "^8.0.0",
"gulp-imagemin": "^6.2.0",
"gulp-babel": "^7.0.1",
"gulp-imagemin": "^6.0.0",
"gulp-nodemon": "^2.4.1",
"gulp.spritesmith": "^6.9.0",
"habitica-markdown": "^1.3.2",
"helmet": "^3.21.2",
"image-size": "^0.8.3",
"habitica-markdown": "^1.3.0",
"hellojs": "^1.18.1",
"helmet": "^3.21.0",
"html-webpack-plugin": "^3.2.0",
"image-size": "^0.8.0",
"in-app-purchase": "^1.11.3",
"js2xmlparser": "^4.0.1",
"lodash": "^4.17.15",
"intro.js": "^2.9.3",
"jquery": ">=3.0.0",
"js2xmlparser": "^4.0.0",
"lodash": "^4.17.10",
"merge-stream": "^2.0.0",
"method-override": "^3.0.0",
"moment": "^2.24.0",
"moment": "^2.22.1",
"moment-recur": "^1.0.7",
"mongoose": "^5.9.2",
"mongoose": "^5.6.9",
"morgan": "^1.7.0",
"nconf": "^0.10.0",
"node-gcm": "^1.0.2",
"node-sass": "^4.12.0",
"ora": "^3.2.0",
"pageres": "^5.1.0",
"passport": "^0.4.1",
"passport": "^0.4.0",
"passport-facebook": "^3.0.0",
"passport-google-oauth2": "^0.2.0",
"passport-google-oauth20": "1.0.0",
"paypal-ipn": "3.0.0",
"paypal-rest-sdk": "^1.8.1",
"popper.js": "^1.14.3",
"postcss-easy-import": "^3.0.0",
"ps-tree": "^1.0.0",
"pug": "^2.0.3",
"regenerator-runtime": "^0.13.3",
"remove-markdown": "^0.3.0",
"rimraf": "^3.0.2",
"rimraf": "^2.4.3",
"sass-loader": "^7.0.3",
"shelljs": "^0.8.2",
"short-uuid": "^3.0.0",
"stripe": "^7.15.0",
"superagent": "^5.2.2",
"smartbanner.js": "^1.11.0",
"stripe": "^7.9.0",
"superagent": "^5.0.2",
"svg-inline-loader": "^0.8.0",
"svg-url-loader": "^3.0.0",
"svgo": "^1.2.0",
"svgo-loader": "^2.1.0",
"universal-analytics": "^0.4.17",
"update": "^0.7.4",
"upgrade": "^1.1.0",
"url-loader": "^1.0.0",
"useragent": "^2.1.9",
"uuid": "^3.4.0",
"uuid": "^3.0.1",
"validator": "^11.0.0",
"vinyl-buffer": "^1.0.1",
"vue": "^2.6.10",
"vue-loader": "^14.2.2",
"vue-mugen-scroll": "^0.2.1",
"vue-router": "^3.0.0",
"vue-style-loader": "^4.1.0",
"vue-template-compiler": "^2.6.10",
"vuedraggable": "^2.20.0",
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#5d237615463a84a23dd6f3f77c6ab577d68593ec",
"webpack": "^3.12.0",
"webpack-merge": "^4.1.3",
"winston": "^2.4.3",
"winston-loggly-bulk": "^2.0.2",
"xml2js": "^0.4.23"
"xml2js": "^0.4.4"
},
"private": true,
"engines": {
@@ -79,8 +122,7 @@
"npm": "^6"
},
"scripts": {
"lint": "eslint --ext .js --fix . && cd website/client && npm run lint",
"lint-no-fix": "eslint --ext .js . && cd website/client && npm run lint-no-fix",
"lint": "eslint --ext .js,.vue .",
"test": "npm run lint && gulp test && gulp apidoc",
"test:build": "gulp test:prepare:build",
"test:api-v3": "gulp test:api-v3",
@@ -95,26 +137,60 @@
"test:nodemon": "gulp test:nodemon",
"coverage": "COVERAGE=true mocha --require register-handlers.js --reporter html-cov > coverage.html; open coverage.html",
"sprites": "gulp sprites:compile",
"client:dev": "cd website/client && npm run serve",
"client:build": "cd website/client && npm run build",
"client:unit": "cd website/client && npm run test:unit",
"client:dev": "node webpack/dev-server.js",
"client:build": "gulp build:client",
"client:unit": "cross-env NODE_ENV=test karma start test/client/unit/karma.conf.js --single-run",
"client:unit:watch": "cross-env NODE_ENV=test karma start test/client/unit/karma.conf.js",
"client:e2e": "node test/client/e2e/runner.js",
"client:test": "npm run client:unit && npm run client:e2e",
"start": "gulp nodemon",
"postinstall": "gulp build && cd website/client && npm install",
"postinstall": "gulp build",
"apidoc": "gulp apidoc"
},
"devDependencies": {
"axios": "^0.19.2",
"@vue/test-utils": "^1.0.0-beta.29",
"babel-plugin-istanbul": "^4.1.6",
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"chalk": "^2.4.1",
"chromedriver": "^77.0.0",
"connect-history-api-fallback": "^1.1.0",
"cross-spawn": "^7.0.0",
"eslint": "^4.19.1",
"eslint-config-habitrpg": "^4.0.0",
"eslint-friendly-formatter": "^4.0.1",
"eslint-loader": "^2.0.0",
"eslint-plugin-html": "^4.0.3",
"eslint-plugin-mocha": "^5.0.0",
"eventsource-polyfill": "^0.9.6",
"expect.js": "^0.3.1",
"http-proxy-middleware": "^0.20.0",
"istanbul": "^1.1.0-alpha.1",
"karma": "^4.0.1",
"karma-babel-preprocessor": "^7.0.0",
"karma-chai-plugins": "^0.9.0",
"karma-chrome-launcher": "^3.0.0",
"karma-coverage": "^1.1.2",
"karma-mocha": "^1.3.0",
"karma-mocha-reporter": "^2.2.5",
"karma-sinon-chai": "^2.0.0",
"karma-sinon-stub-promise": "^1.0.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-spec-reporter": "0.0.32",
"karma-webpack": "^3.0.0",
"mocha": "^5.1.1",
"monk": "^7.1.2",
"monk": "^6.0.6",
"nightwatch": "^1.0.16",
"puppeteer": "^1.14.0",
"require-again": "^2.0.0",
"selenium-server": "^3.12.0",
"sinon": "^7.2.4",
"sinon-chai": "^3.5.0",
"sinon-stub-promise": "^4.0.0"
"sinon-chai": "^3.0.0",
"sinon-stub-promise": "^4.0.0",
"webpack-bundle-analyzer": "^2.12.0",
"webpack-dev-middleware": "^2.0.5",
"webpack-hot-middleware": "^2.22.2"
},
"optionalDependencies": {}
}
+45 -62
View File
@@ -1,13 +1,13 @@
/* eslint-disable no-console, import/no-commonjs */
import axios from 'axios'; // eslint-disable-line import/no-extraneous-dependencies
import nconf from 'nconf';
/* eslint-disable no-console */
import axios from 'axios';
import { model as User } from '../website/server/models/user';
import nconf from 'nconf';
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,31 +19,22 @@ async function deleteAmplitudeData (userId, email) {
username: AMPLITUDE_KEY,
password: AMPLITUDE_SECRET,
},
},
).catch(err => {
}
).catch((err) => {
console.log(err.response.data);
});
if (response) {
if (response.status === 200) {
console.log(`${userId} (${email}) Amplitude deletion request OK.`);
} else {
console.log(`${userId} (${email}) Amplitude response: ${response.status} ${response.statusText}`);
}
}
if (response) console.log(`${response.status} ${response.statusText}`);
}
async function deleteHabiticaData (user, email) {
const truncatedEmail = email.slice(0, email.indexOf('@'));
async function _deleteHabiticaData (user, email) {
await User.update(
{ _id: user._id },
{
$set: {
'auth.local.email': user.auth.local.email ? email : `${truncatedEmail}@example.com`,
'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`,
@@ -55,52 +46,44 @@ async function deleteHabiticaData (user, email) {
'x-api-user': user._id,
'x-api-key': user.apiToken,
},
},
).catch(err => {
}
).catch((err) => {
console.log(err.response.data);
});
if (response) {
if (response.status === 200) {
console.log(`${user._id} (${email}) removed from Habitica. Last login: ${user.auth.timestamps.loggedin}`);
} else {
console.log(`${user._id} (${email}) Habitica response: ${response.status} ${response.statusText}`);
console.log(`${response.status} ${response.statusText}`);
if (response.status === 200) console.log(`${user._id} (${email}) removed. Last login: ${user.auth.timestamps.loggedin}`);
}
}
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},
]},
{
_id: 1,
apiToken: 1,
auth: 1,
}).exec();
if (users.length < 1) {
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
}
}
}
async function processEmailAddress (email) {
const emailRegex = new RegExp(`^${email}$`, 'i');
const localUsers = await User.find(
{ 'auth.local.email': emailRegex },
{ _id: 1, apiToken: 1, auth: 1 },
).exec();
const socialUsers = await User.find(
{
$or: [
{ 'auth.facebook.emails.value': email },
{ 'auth.google.emails.value': email },
],
},
{ _id: 1, apiToken: 1, auth: 1 },
).collation(
{ locale: 'en', strength: 1 },
).exec();
const users = localUsers.concat(socialUsers);
if (users.length < 1) {
return console.log(`No users found with email address ${email}`);
}
return Promise.all(users.map(user => (async () => {
await deleteAmplitudeData(user._id, email); // eslint-disable-line no-await-in-loop
await deleteHabiticaData(user, email); // eslint-disable-line no-await-in-loop
})()));
}
export default function deleteUserData (emails) {
const emailPromises = emails.map(processEmailAddress);
function deleteUserData (emails) {
const emailPromises = emails.map(_processEmailAddress);
return Promise.all(emailPromises);
}
module.exports = deleteUserData;
+17 -26
View File
@@ -1,12 +1,8 @@
/* eslint-disable import/no-commonjs */
require('@babel/register'); // eslint-disable-line import/no-extraneous-dependencies
require('babel-register');
// This file is used for creating paypal billing plans.
// PayPal doesn't have a web interface for setting up recurring
// payment plan definitions, instead you have to create it
// via their REST SDK and keep it updated the same way. So this
// file will be used once for initing your billing plan
// (then you get the resultant plan.id to store in config.json),
// This file is used for creating paypal billing plans. PayPal doesn't have a web interface for setting up recurring
// payment plan definitions, instead you have to create it via their REST SDK and keep it updated the same way. So this
// file will be used once for initing your billing plan (then you get the resultant plan.id to store in config.json),
// and once for any time you need to edit the plan thereafter
/* eslint-disable no-console, camelcase, no-case-declarations */
@@ -16,12 +12,11 @@ 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')));
const OP = 'create'; // list get update create create-webprofile
let OP = 'create'; // list get update create create-webprofile
paypal.configure({
mode: nconf.get('PAYPAL_MODE'), // sandbox or live
@@ -30,8 +25,8 @@ paypal.configure({
});
// https://developer.paypal.com/docs/api/#billing-plans-and-agreements
const billingPlanTitle = 'Habitica Subscription';
const billingPlanAttributes = {
let billingPlanTitle = 'Habitica Subscription';
let billingPlanAttributes = {
description: billingPlanTitle,
type: 'INFINITE',
merchant_preferences: {
@@ -46,7 +41,7 @@ const 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)`,
@@ -62,17 +57,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':
const updatePayload = {
let updatePayload = {
op: 'replace',
path: '/merchant_preferences',
value: {
@@ -80,7 +75,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':
@@ -88,10 +83,10 @@ switch (OP) {
if (err) return console.log(err);
if (plan.state === 'ACTIVE') {
return console.log({ err, plan });
return console.log({err, plan});
}
const billingPlanUpdateAttributes = [{
let billingPlanUpdateAttributes = [{
op: 'replace',
path: '/',
value: {
@@ -101,14 +96,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});
});
return null;
});
break;
case 'create-webprofile':
const webexpinfo = {
let webexpinfo = {
name: 'HabiticaProfile',
input_fields: {
no_shipping: 1,
@@ -119,6 +112,4 @@ switch (OP) {
console.log(error, result);
});
break;
default:
throw new Error('Invalid op');
}
+12
View File
@@ -0,0 +1,12 @@
{
"extends": [
"habitrpg/mocha",
"habitrpg/esnext"
],
"env": {
"node": true,
},
"globals": {
"_": true,
}
}
-12
View File
@@ -1,12 +0,0 @@
module.exports = {
extends: [
'habitrpg/lib/mocha',
],
globals: {
_: true,
chai: true,
expect: true,
sinon: true,
sandbox: true
},
}
+99 -89
View File
@@ -1,7 +1,7 @@
/* eslint-disable camelcase */
import analyticsService from '../../../../website/server/libs/analyticsService';
import Amplitude from 'amplitude';
import { Visitor } from 'universal-analytics';
import * as analyticsService from '../../../../website/server/libs/analyticsService';
describe('analyticsService', () => {
beforeEach(() => {
@@ -16,8 +16,7 @@ describe('analyticsService', () => {
});
describe('#track', () => {
let eventType; let
data;
let eventType, data;
beforeEach(() => {
Visitor.prototype.event.yields();
@@ -36,10 +35,12 @@ describe('analyticsService', () => {
});
context('Amplitude', () => {
it('calls out to amplitude', () => analyticsService.track(eventType, data)
.then(() => {
expect(Amplitude.prototype.track).to.be.calledOnce;
}));
it('calls out to amplitude', () => {
return analyticsService.track(eventType, data)
.then(() => {
expect(Amplitude.prototype.track).to.be.calledOnce;
});
});
it('uses a dummy user id if none is provided', () => {
delete data.uuid;
@@ -54,7 +55,7 @@ describe('analyticsService', () => {
context('platform', () => {
it('logs web platform', () => {
data.headers = { 'x-client': 'habitica-web' };
data.headers = {'x-client': 'habitica-web'};
return analyticsService.track(eventType, data)
.then(() => {
@@ -65,7 +66,7 @@ describe('analyticsService', () => {
});
it('logs iOS platform', () => {
data.headers = { 'x-client': 'habitica-ios' };
data.headers = {'x-client': 'habitica-ios'};
return analyticsService.track(eventType, data)
.then(() => {
@@ -76,7 +77,7 @@ describe('analyticsService', () => {
});
it('logs Android platform', () => {
data.headers = { 'x-client': 'habitica-android' };
data.headers = {'x-client': 'habitica-android'};
return analyticsService.track(eventType, data)
.then(() => {
@@ -87,7 +88,7 @@ describe('analyticsService', () => {
});
it('logs 3rd Party platform', () => {
data.headers = { 'x-client': 'some-third-party' };
data.headers = {'x-client': 'some-third-party'};
return analyticsService.track(eventType, data)
.then(() => {
@@ -155,7 +156,7 @@ describe('analyticsService', () => {
});
});
it('sets Unknown if headers are not passed in', () => {
it('sets Unkown if headers are not passed in', () => {
delete data.headers;
return analyticsService.track(eventType, data)
@@ -168,16 +169,18 @@ describe('analyticsService', () => {
});
});
it('sends details about event', () => analyticsService.track(eventType, data)
.then(() => {
expect(Amplitude.prototype.track).to.be.calledWithMatch({
event_properties: {
category: 'behavior',
resting: true,
cronCount: 5,
},
it('sends details about event', () => {
return analyticsService.track(eventType, data)
.then(() => {
expect(Amplitude.prototype.track).to.be.calledWithMatch({
event_properties: {
category: 'behavior',
resting: true,
cronCount: 5,
},
});
});
}));
});
it('sends english item name for gear if itemKey is provided', () => {
data.itemKey = 'headAccessory_special_foxEars';
@@ -264,18 +267,16 @@ describe('analyticsService', () => {
});
it('sends user data if provided', () => {
const stats = {
class: 'wizard', exp: 5, gp: 23, hp: 10, lvl: 4, mp: 30,
};
const user = {
let stats = { class: 'wizard', exp: 5, gp: 23, hp: 10, lvl: 4, mp: 30 };
let user = {
stats,
contributor: { level: 1 },
purchased: { plan: { planId: 'foo-plan' } },
flags: { tour: { intro: -2 } },
habits: [{ _id: 'habit' }],
dailys: [{ _id: 'daily' }],
todos: [{ _id: 'todo' }],
rewards: [{ _id: 'reward' }],
flags: {tour: {intro: -2}},
habits: [{_id: 'habit'}],
dailys: [{_id: 'daily'}],
todos: [{_id: 'todo'}],
rewards: [{_id: 'reward'}],
balance: 12,
loginIncentives: 1,
};
@@ -311,24 +312,27 @@ describe('analyticsService', () => {
});
context('GA', () => {
it('calls out to GA', () => analyticsService.track(eventType, data)
.then(() => {
expect(Visitor.prototype.event).to.be.calledOnce;
}));
it('sends details about event', () => analyticsService.track(eventType, data)
.then(() => {
expect(Visitor.prototype.event).to.be.calledWith({
ea: 'Cron',
ec: 'behavior',
it('calls out to GA', () => {
return analyticsService.track(eventType, data)
.then(() => {
expect(Visitor.prototype.event).to.be.calledOnce;
});
}));
});
it('sends details about event', () => {
return analyticsService.track(eventType, data)
.then(() => {
expect(Visitor.prototype.event).to.be.calledWith({
ea: 'Cron',
ec: 'behavior',
});
});
});
});
});
describe('#trackPurchase', () => {
let data; let
itemSpy;
let data, itemSpy;
beforeEach(() => {
Visitor.prototype.event.yields();
@@ -357,10 +361,12 @@ describe('analyticsService', () => {
});
context('Amplitude', () => {
it('calls out to amplitude', () => analyticsService.trackPurchase(data)
.then(() => {
expect(Amplitude.prototype.track).to.be.calledOnce;
}));
it('calls out to amplitude', () => {
return analyticsService.trackPurchase(data)
.then(() => {
expect(Amplitude.prototype.track).to.be.calledOnce;
});
});
it('uses a dummy user id if none is provided', () => {
delete data.uuid;
@@ -375,7 +381,7 @@ describe('analyticsService', () => {
context('platform', () => {
it('logs web platform', () => {
data.headers = { 'x-client': 'habitica-web' };
data.headers = {'x-client': 'habitica-web'};
return analyticsService.trackPurchase(data)
.then(() => {
@@ -386,7 +392,7 @@ describe('analyticsService', () => {
});
it('logs iOS platform', () => {
data.headers = { 'x-client': 'habitica-ios' };
data.headers = {'x-client': 'habitica-ios'};
return analyticsService.trackPurchase(data)
.then(() => {
@@ -397,7 +403,7 @@ describe('analyticsService', () => {
});
it('logs Android platform', () => {
data.headers = { 'x-client': 'habitica-android' };
data.headers = {'x-client': 'habitica-android'};
return analyticsService.trackPurchase(data)
.then(() => {
@@ -408,7 +414,7 @@ describe('analyticsService', () => {
});
it('logs 3rd Party platform', () => {
data.headers = { 'x-client': 'some-third-party' };
data.headers = {'x-client': 'some-third-party'};
return analyticsService.trackPurchase(data)
.then(() => {
@@ -476,7 +482,7 @@ describe('analyticsService', () => {
});
});
it('sets Unknown if headers are not passed in', () => {
it('sets Unkown if headers are not passed in', () => {
delete data.headers;
return analyticsService.trackPurchase(data)
@@ -489,33 +495,33 @@ describe('analyticsService', () => {
});
});
it('sends details about purchase', () => analyticsService.trackPurchase(data)
.then(() => {
expect(Amplitude.prototype.track).to.be.calledWithMatch({
event_properties: {
gift: false,
itemPurchased: 'Gems',
paymentMethod: 'PayPal',
purchaseType: 'checkout',
quantity: 1,
sku: 'paypal-checkout',
},
it('sends details about purchase', () => {
return analyticsService.trackPurchase(data)
.then(() => {
expect(Amplitude.prototype.track).to.be.calledWithMatch({
event_properties: {
gift: false,
itemPurchased: 'Gems',
paymentMethod: 'PayPal',
purchaseType: 'checkout',
quantity: 1,
sku: 'paypal-checkout',
},
});
});
}));
});
it('sends user data if provided', () => {
const stats = {
class: 'wizard', exp: 5, gp: 23, hp: 10, lvl: 4, mp: 30,
};
const user = {
let stats = { class: 'wizard', exp: 5, gp: 23, hp: 10, lvl: 4, mp: 30 };
let user = {
stats,
contributor: { level: 1 },
purchased: { plan: { planId: 'foo-plan' } },
flags: { tour: { intro: -2 } },
habits: [{ _id: 'habit' }],
dailys: [{ _id: 'daily' }],
todos: [{ _id: 'todo' }],
rewards: [{ _id: 'reward' }],
flags: {tour: {intro: -2}},
habits: [{_id: 'habit'}],
dailys: [{_id: 'daily'}],
todos: [{_id: 'todo'}],
rewards: [{_id: 'reward'}],
};
data.user = user;
@@ -546,23 +552,27 @@ describe('analyticsService', () => {
});
context('GA', () => {
it('calls out to GA', () => analyticsService.trackPurchase(data)
.then(() => {
expect(Visitor.prototype.event).to.be.calledOnce;
expect(Visitor.prototype.transaction).to.be.calledOnce;
}));
it('sends details about purchase', () => analyticsService.trackPurchase(data)
.then(() => {
expect(Visitor.prototype.event).to.be.calledWith({
ea: 'checkout',
ec: 'commerce',
el: 'PayPal',
ev: 8,
it('calls out to GA', () => {
return analyticsService.trackPurchase(data)
.then(() => {
expect(Visitor.prototype.event).to.be.calledOnce;
expect(Visitor.prototype.transaction).to.be.calledOnce;
});
expect(Visitor.prototype.transaction).to.be.calledWith('user-id', 8);
expect(itemSpy).to.be.calledWith(8, 1, 'paypal-checkout', 'Gems', 'checkout');
}));
});
it('sends details about purchase', () => {
return analyticsService.trackPurchase(data)
.then(() => {
expect(Visitor.prototype.event).to.be.calledWith({
ea: 'checkout',
ec: 'commerce',
el: 'PayPal',
ev: 8,
});
expect(Visitor.prototype.transaction).to.be.calledWith('user-id', 8);
expect(itemSpy).to.be.calledWith(8, 1, 'paypal-checkout', 'Gems', 'checkout');
});
});
});
});
+3 -3
View File
@@ -11,7 +11,7 @@ describe('API Messages', () => {
});
it('clones the passed variables', () => {
const vars = { a: 1 };
let vars = {a: 1};
sandbox.stub(_, 'clone').returns({});
apiError('guildsOnlyPaginate', vars);
expect(_.clone).to.have.been.calledOnce;
@@ -19,8 +19,8 @@ describe('API Messages', () => {
});
it('pass the message through _.template', () => {
const vars = { a: 1 };
const stub = sinon.stub().returns('string');
let vars = {a: 1};
let stub = sinon.stub().returns('string');
sandbox.stub(_, 'template').returns(stub);
apiError('guildsOnlyPaginate', vars);
expect(_.template).to.have.been.calledOnce;
+13 -13
View File
@@ -1,5 +1,5 @@
import mongoose from 'mongoose';
import baseModel from '../../../../website/server/libs/baseModel';
import mongoose from 'mongoose';
describe('Base model plugin', () => {
let schema;
@@ -25,7 +25,7 @@ describe('Base model plugin', () => {
});
it('can add timestamps fields', () => {
schema.plugin(baseModel, { timestamps: true });
schema.plugin(baseModel, {timestamps: true});
expect(schema.add).to.be.calledTwice;
});
@@ -36,7 +36,7 @@ describe('Base model plugin', () => {
});
expect(schema.statics.sanitize).to.exist;
const sanitized = schema.statics.sanitize({ ok: true, noUpdateForMe: true });
let sanitized = schema.statics.sanitize({ok: true, noUpdateForMe: true});
expect(sanitized).to.have.property('ok');
expect(sanitized).not.to.have.property('noUpdateForMe');
@@ -49,7 +49,7 @@ describe('Base model plugin', () => {
});
expect(schema.statics.sanitize).to.exist;
const sanitized = schema.statics.sanitize({ ok: true, noUpdateForMe: true, usuallySettable: true }, ['usuallySettable']);
let sanitized = schema.statics.sanitize({ok: true, noUpdateForMe: true, usuallySettable: true}, ['usuallySettable']);
expect(sanitized).to.have.property('ok');
expect(sanitized).not.to.have.property('noUpdateForMe');
@@ -63,31 +63,31 @@ describe('Base model plugin', () => {
});
expect(schema.options.toJSON.transform).to.exist;
const objToTransform = { ok: true, amPrivate: true };
const privatized = schema.options.toJSON.transform({}, objToTransform);
let objToTransform = {ok: true, amPrivate: true};
let privatized = schema.options.toJSON.transform({}, objToTransform);
expect(privatized).to.have.property('ok');
expect(privatized).not.to.have.property('amPrivate');
});
it('accepts a further transform function for toJSON', () => {
const options = {
let options = {
private: ['amPrivate'],
toJSONTransform: sandbox.stub().returns(true),
};
schema.plugin(baseModel, options);
const objToTransform = { ok: true, amPrivate: true };
const doc = { doc: true };
const privatized = schema.options.toJSON.transform(doc, objToTransform);
let objToTransform = {ok: true, amPrivate: true};
let doc = {doc: true};
let privatized = schema.options.toJSON.transform(doc, objToTransform);
expect(privatized).to.equals(true);
expect(options.toJSONTransform).to.be.calledWith(objToTransform, doc);
});
it('accepts a transform function for sanitize', () => {
const options = {
let options = {
private: ['amPrivate'],
sanitizeTransform: sandbox.stub().returns(true),
};
@@ -95,8 +95,8 @@ describe('Base model plugin', () => {
schema.plugin(baseModel, options);
expect(schema.options.toJSON.transform).to.exist;
const objToSanitize = { ok: true, noUpdateForMe: true };
const sanitized = schema.statics.sanitize(objToSanitize);
let objToSanitize = {ok: true, noUpdateForMe: true};
let sanitized = schema.statics.sanitize(objToSanitize);
expect(sanitized).to.equals(true);
expect(options.sanitizeTransform).to.be.calledWith(objToSanitize);
@@ -6,7 +6,7 @@ import {
describe('Collection Manipulators', () => {
describe('removeFromArray', () => {
it('removes element from array', () => {
const array = ['a', 'b', 'c', 'd'];
let array = ['a', 'b', 'c', 'd'];
removeFromArray(array, 'c');
@@ -14,7 +14,7 @@ describe('Collection Manipulators', () => {
});
it('removes object from array', () => {
const array = [
let array = [
{ id: 'a', foo: 'bar' },
{ id: 'b', foo: 'bar' },
{ id: 'c', foo: 'bar' },
@@ -28,7 +28,7 @@ describe('Collection Manipulators', () => {
});
it('does not change array if value is not found', () => {
const array = ['a', 'b', 'c', 'd'];
let array = ['a', 'b', 'c', 'd'];
removeFromArray(array, 'z');
@@ -40,15 +40,15 @@ describe('Collection Manipulators', () => {
});
it('returns the removed element', () => {
const array = ['a', 'b', 'c'];
let array = ['a', 'b', 'c'];
const result = removeFromArray(array, 'b');
let result = removeFromArray(array, 'b');
expect(result).to.eql('b');
});
it('returns the removed object element', () => {
const array = [
let array = [
{ id: 'a', foo: 'bar' },
{ id: 'b', foo: 'bar' },
{ id: 'c', foo: 'bar' },
@@ -56,31 +56,31 @@ describe('Collection Manipulators', () => {
{ id: 'e', foo: 'bar' },
];
const result = removeFromArray(array, { id: 'c' });
let result = removeFromArray(array, { id: 'c' });
expect(result).to.eql({ id: 'c', foo: 'bar' });
});
it('returns false if item is not found', () => {
const array = ['a', 'b', 'c'];
let array = ['a', 'b', 'c'];
const result = removeFromArray(array, 'z');
let result = removeFromArray(array, 'z');
expect(result).to.eql(false);
});
it('persists removal of element when mongoose document is saved', async () => {
const schema = new mongoose.Schema({
let schema = new mongoose.Schema({
array: Array,
});
const Model = mongoose.model('ModelToTestRemoveFromArray', schema);
const model = await new Model({
let Model = mongoose.model('ModelToTestRemoveFromArray', schema);
let model = await new Model({
array: ['a', 'b', 'c'],
}).save(); // Initial creation
removeFromArray(model.array, 'b');
const savedModel = await model.save();
let savedModel = await model.save();
expect(savedModel.array).to.not.include('b');
});
File diff suppressed because it is too large Load Diff
+49 -43
View File
@@ -31,20 +31,20 @@ function getUser () {
}
describe('emails', () => {
const pathToEmailLib = '../../../../website/server/libs/email';
let pathToEmailLib = '../../../../website/server/libs/email';
describe('getUserInfo', () => {
it('returns an empty object if no field request', () => {
const attachEmail = requireAgain(pathToEmailLib);
const { getUserInfo } = attachEmail;
let attachEmail = requireAgain(pathToEmailLib);
let getUserInfo = attachEmail.getUserInfo;
expect(getUserInfo({}, [])).to.be.empty;
});
it('returns correct user data', () => {
const attachEmail = requireAgain(pathToEmailLib);
const { getUserInfo } = attachEmail;
const user = getUser();
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
let attachEmail = requireAgain(pathToEmailLib);
let getUserInfo = attachEmail.getUserInfo;
let user = getUser();
let data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
expect(data).to.have.property('name', user.auth.local.username);
expect(data).to.have.property('email', user.auth.local.email);
@@ -53,13 +53,13 @@ describe('emails', () => {
});
it('returns correct user data [facebook users]', () => {
const attachEmail = requireAgain(pathToEmailLib);
const { getUserInfo } = attachEmail;
const user = getUser();
let attachEmail = requireAgain(pathToEmailLib);
let getUserInfo = attachEmail.getUserInfo;
let user = getUser();
delete user.profile.name;
delete user.auth.local.email;
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
let data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
expect(data).to.have.property('name', user.auth.local.username);
expect(data).to.have.property('email', user.auth.facebook.emails[0].value);
@@ -68,13 +68,13 @@ describe('emails', () => {
});
it('has fallbacks for missing data', () => {
const attachEmail = requireAgain(pathToEmailLib);
const { getUserInfo } = attachEmail;
const user = getUser();
let attachEmail = requireAgain(pathToEmailLib);
let getUserInfo = attachEmail.getUserInfo;
let user = getUser();
delete user.auth.local.email;
delete user.auth.facebook;
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
let data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
expect(data).to.have.property('name', user.auth.local.username);
expect(data).not.to.have.property('email');
@@ -85,18 +85,18 @@ describe('emails', () => {
describe('getGroupUrl', () => {
it('returns correct url if group is the tavern', () => {
const { getGroupUrl } = require(pathToEmailLib); // eslint-disable-line import/no-dynamic-require, max-len
expect(getGroupUrl({ _id: TAVERN_ID, type: 'guild' })).to.eql('/groups/tavern');
let getGroupUrl = require(pathToEmailLib).getGroupUrl;
expect(getGroupUrl({_id: TAVERN_ID, type: 'guild'})).to.eql('/groups/tavern');
});
it('returns correct url if group is a guild', () => {
const { getGroupUrl } = require(pathToEmailLib); // eslint-disable-line import/no-dynamic-require, max-len
expect(getGroupUrl({ _id: 'random _id', type: 'guild' })).to.eql('/groups/guild/random _id');
let getGroupUrl = require(pathToEmailLib).getGroupUrl;
expect(getGroupUrl({_id: 'random _id', type: 'guild'})).to.eql('/groups/guild/random _id');
});
it('returns correct url if group is a party', () => {
const { getGroupUrl } = require(pathToEmailLib); // eslint-disable-line import/no-dynamic-require, max-len
expect(getGroupUrl({ _id: 'random _id', type: 'party' })).to.eql('party');
let getGroupUrl = require(pathToEmailLib).getGroupUrl;
expect(getGroupUrl({_id: 'random _id', type: 'party'})).to.eql('party');
});
});
@@ -111,10 +111,10 @@ describe('emails', () => {
it('can send a txn email to one recipient', () => {
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(true);
const attachEmail = requireAgain(pathToEmailLib);
const sendTxnEmail = attachEmail.sendTxn;
const emailType = 'an email type';
const mailingInfo = {
let attachEmail = requireAgain(pathToEmailLib);
let sendTxnEmail = attachEmail.sendTxn;
let emailType = 'an email type';
let mailingInfo = {
name: 'my name',
email: 'my@email',
};
@@ -125,7 +125,9 @@ describe('emails', () => {
body: {
data: {
emailType: sinon.match.same(emailType),
to: sinon.match(value => Array.isArray(value) && value[0].name === mailingInfo.name, 'matches mailing info array'),
to: sinon.match((value) => {
return Array.isArray(value) && value[0].name === mailingInfo.name;
}, 'matches mailing info array'),
},
},
}));
@@ -133,10 +135,10 @@ describe('emails', () => {
it('does not send email if address is missing', () => {
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(true);
const attachEmail = requireAgain(pathToEmailLib);
const sendTxnEmail = attachEmail.sendTxn;
const emailType = 'an email type';
const mailingInfo = {
let attachEmail = requireAgain(pathToEmailLib);
let sendTxnEmail = attachEmail.sendTxn;
let emailType = 'an email type';
let mailingInfo = {
name: 'my name',
// email: 'my@email',
};
@@ -147,10 +149,10 @@ describe('emails', () => {
it('uses getUserInfo in case of user data', () => {
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(true);
const attachEmail = requireAgain(pathToEmailLib);
const sendTxnEmail = attachEmail.sendTxn;
const emailType = 'an email type';
const mailingInfo = getUser();
let attachEmail = requireAgain(pathToEmailLib);
let sendTxnEmail = attachEmail.sendTxn;
let emailType = 'an email type';
let mailingInfo = getUser();
sendTxnEmail(mailingInfo, emailType);
expect(got.post).to.be.calledWith('undefined/job', sinon.match({
@@ -166,24 +168,28 @@ describe('emails', () => {
it('sends email with some default variables', () => {
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(true);
const attachEmail = requireAgain(pathToEmailLib);
const sendTxnEmail = attachEmail.sendTxn;
const emailType = 'an email type';
const mailingInfo = {
let attachEmail = requireAgain(pathToEmailLib);
let sendTxnEmail = attachEmail.sendTxn;
let emailType = 'an email type';
let mailingInfo = {
name: 'my name',
email: 'my@email',
};
const variables = [1, 2, 3];
let variables = [1, 2, 3];
sendTxnEmail(mailingInfo, emailType, variables);
expect(got.post).to.be.calledWith('undefined/job', sinon.match({
json: true,
body: {
data: {
variables: sinon.match(value => value[0].name === 'BASE_URL', 'matches variables'),
personalVariables: sinon.match(value => value[0].rcpt === mailingInfo.email
&& value[0].vars[0].name === 'RECIPIENT_NAME'
&& value[0].vars[1].name === 'RECIPIENT_UNSUB_URL', 'matches personal variables'),
variables: sinon.match((value) => {
return value[0].name === 'BASE_URL';
}, 'matches variables'),
personalVariables: sinon.match((value) => {
return value[0].rcpt === mailingInfo.email &&
value[0].vars[0].name === 'RECIPIENT_NAME' &&
value[0].vars[1].name === 'RECIPIENT_UNSUB_URL';
}, 'matches personal variables'),
},
},
}));
+3 -3
View File
@@ -5,9 +5,9 @@ import {
describe('encryption', () => {
it('can encrypt and decrypt', () => {
const data = 'some secret text';
const encrypted = encrypt(data);
const decrypted = decrypt(encrypted);
let data = 'some secret text';
let encrypted = encrypt(data);
let decrypted = decrypt(encrypted);
expect(encrypted).not.to.equal(data);
expect(data).to.equal(decrypted);
+17 -17
View File
@@ -12,7 +12,7 @@ import i18n from '../../../../website/common/script/i18n';
describe('Custom Errors', () => {
describe('CustomError', () => {
it('is an instance of Error', () => {
const customError = new CustomError();
let customError = new CustomError();
expect(customError).to.be.an.instanceOf(Error);
});
@@ -20,25 +20,25 @@ describe('Custom Errors', () => {
describe('NotAuthorized', () => {
it('is an instance of CustomError', () => {
const notAuthorizedError = new NotAuthorized();
let notAuthorizedError = new NotAuthorized();
expect(notAuthorizedError).to.be.an.instanceOf(CustomError);
});
it('it returns an http code of 401', () => {
const notAuthorizedError = new NotAuthorized();
let notAuthorizedError = new NotAuthorized();
expect(notAuthorizedError.httpCode).to.eql(401);
});
it('returns a default message', () => {
const notAuthorizedError = new NotAuthorized();
let notAuthorizedError = new NotAuthorized();
expect(notAuthorizedError.message).to.eql('Not authorized.');
});
it('allows a custom message', () => {
const notAuthorizedError = new NotAuthorized('Custom Error Message');
let notAuthorizedError = new NotAuthorized('Custom Error Message');
expect(notAuthorizedError.message).to.eql('Custom Error Message');
});
@@ -46,25 +46,25 @@ describe('Custom Errors', () => {
describe('NotFound', () => {
it('is an instance of CustomError', () => {
const notAuthorizedError = new NotFound();
let notAuthorizedError = new NotFound();
expect(notAuthorizedError).to.be.an.instanceOf(CustomError);
});
it('it returns an http code of 404', () => {
const notAuthorizedError = new NotFound();
let notAuthorizedError = new NotFound();
expect(notAuthorizedError.httpCode).to.eql(404);
});
it('returns a default message', () => {
const notAuthorizedError = new NotFound();
let notAuthorizedError = new NotFound();
expect(notAuthorizedError.message).to.eql('Not found.');
});
it('allows a custom message', () => {
const notAuthorizedError = new NotFound('Custom Error Message');
let notAuthorizedError = new NotFound('Custom Error Message');
expect(notAuthorizedError.message).to.eql('Custom Error Message');
});
@@ -89,25 +89,25 @@ describe('Custom Errors', () => {
describe('BadRequest', () => {
it('is an instance of CustomError', () => {
const badRequestError = new BadRequest();
let badRequestError = new BadRequest();
expect(badRequestError).to.be.an.instanceOf(CustomError);
});
it('it returns an http code of 401', () => {
const badRequestError = new BadRequest();
let badRequestError = new BadRequest();
expect(badRequestError.httpCode).to.eql(400);
});
it('returns a default message', () => {
const badRequestError = new BadRequest();
let badRequestError = new BadRequest();
expect(badRequestError.message).to.eql('Bad request.');
});
it('allows a custom message', () => {
const badRequestError = new BadRequest('Custom Error Message');
let badRequestError = new BadRequest('Custom Error Message');
expect(badRequestError.message).to.eql('Custom Error Message');
});
@@ -115,25 +115,25 @@ describe('Custom Errors', () => {
describe('InternalServerError', () => {
it('is an instance of CustomError', () => {
const internalServerError = new InternalServerError();
let internalServerError = new InternalServerError();
expect(internalServerError).to.be.an.instanceOf(CustomError);
});
it('it returns an http code of 500', () => {
const internalServerError = new InternalServerError();
let internalServerError = new InternalServerError();
expect(internalServerError.httpCode).to.eql(500);
});
it('returns a default message', () => {
const internalServerError = new InternalServerError();
let internalServerError = new InternalServerError();
expect(internalServerError.message).to.eql('An unexpected error occurred.');
});
it('allows a custom message', () => {
const internalServerError = new InternalServerError('Custom Error Message');
let internalServerError = new InternalServerError('Custom Error Message');
expect(internalServerError.message).to.eql('Custom Error Message');
});
-60
View File
@@ -1,60 +0,0 @@
import mongoose from 'mongoose';
import {
highlightMentions,
} from '../../../../website/server/libs/highlightMentions';
describe('highlightMentions', () => {
beforeEach(() => {
const mockFind = {
select () {
return this;
},
lean () {
return this;
},
exec () {
return Promise.resolve([{
auth: { local: { username: 'user' } }, _id: '111',
}, { auth: { local: { username: 'user2' } }, _id: '222' }, { auth: { local: { username: 'user3' } }, _id: '333' }, { auth: { local: { username: 'user-dash' } }, _id: '444' }, { auth: { local: { username: 'user_underscore' } }, _id: '555' },
]);
},
};
sinon.stub(mongoose.Model, 'find').returns(mockFind);
});
afterEach(() => {
sinon.restore();
});
it('doesn\'t change text without mentions', async () => {
const text = 'some chat text';
const result = await highlightMentions(text);
expect(result[0]).to.equal(text);
});
it('highlights existing users', async () => {
const text = '@user: message';
const result = await highlightMentions(text);
expect(result[0]).to.equal('[@user](/profile/111): message');
});
it('highlights special characters', async () => {
const text = '@user-dash: message @user_underscore';
const result = await highlightMentions(text);
expect(result[0]).to.equal('[@user-dash](/profile/444): message [@user_underscore](/profile/555)');
});
it('doesn\'t highlight nonexisting users', async () => {
const text = '@nouser message';
const result = await highlightMentions(text);
expect(result[0]).to.equal('@nouser message');
});
it('highlights multiple existing users', async () => {
const text = '@user message (@user2) @user3 @user';
const result = await highlightMentions(text);
expect(result[0]).to.equal('[@user](/profile/111) message ([@user2](/profile/222)) [@user3](/profile/333) [@user](/profile/111)');
});
it('doesn\'t highlight more than 5 users', async () => {
const text = '@user @user2 @user3 @user4 @user5 @user6';
const result = await highlightMentions(text);
expect(result[0]).to.equal(text);
});
});
+19 -3
View File
@@ -1,15 +1,31 @@
import {
translations,
localePath,
langCodes,
approvedLanguages,
} from '../../../../website/server/libs/i18n';
import fs from 'fs';
import path from 'path';
describe('i18n', () => {
const listOfLocales = approvedLanguages.sort();
let listOfLocales = [];
before((done) => {
fs.readdir(localePath, (err, files) => {
if (err) return done(err);
files.forEach((file) => {
if (fs.statSync(path.join(localePath, file)).isDirectory() === false) return;
listOfLocales.push(file);
});
listOfLocales = listOfLocales.sort();
done();
});
});
describe('translations', () => {
it('includes a translation object for each locale', () => {
listOfLocales.forEach(locale => {
listOfLocales.forEach((locale) => {
expect(translations[locale]).to.be.an('object');
});
});
+3 -12
View File
@@ -100,23 +100,14 @@ describe('Items Utils', () => {
});
it('converts values for mounts paths to numbers', () => {
expect(castItemVal('items.mounts.Cactus-Base', 'true')).to.equal(true);
expect(castItemVal('items.mounts.Aether-Invisible', 'false')).to.equal(false);
expect(castItemVal('items.mounts.Aether-Invalid', 'true')).to.equal(true);
expect(castItemVal('items.mounts.Aether-Invalid', 'truish')).to.equal(true);
expect(castItemVal('items.mounts.Aether-Invalid', 0)).to.equal(false);
expect(castItemVal('items.mounts.Cactus-Base', '5')).to.equal(5);
expect(castItemVal('items.mounts.Aether-Invisible', '5')).to.equal(5);
expect(castItemVal('items.mounts.Aether-Invalid', '5')).to.equal(5);
});
it('converts values for quests paths to numbers', () => {
expect(castItemVal('items.quests.atom3', '5')).to.equal(5);
expect(castItemVal('items.quests.invalid', '5')).to.equal(5);
});
it('converts values for owned gear', () => {
expect(castItemVal('items.gear.owned.shield_warrior_0', 'true')).to.equal(true);
expect(castItemVal('items.gear.owned.invalid', 'false')).to.equal(false);
expect(castItemVal('items.gear.owned.invalid', 'thruthy')).to.equal(true);
expect(castItemVal('items.gear.owned.invalid', 0)).to.equal(false);
});
});
});
+18 -18
View File
@@ -2,7 +2,7 @@ import winston from 'winston';
import logger from '../../../../website/server/libs/logger';
import {
NotFound,
} from '../../../../website/server/libs/errors';
} from '../../../../website/server/libs//errors';
describe('logger', () => {
let logSpy;
@@ -34,7 +34,7 @@ describe('logger', () => {
context('error object', () => {
it('logs the stack and the err data', () => {
const errInstance = new Error('An error.');
let errInstance = new Error('An error.');
logger.error(errInstance, {
data: 1,
}, 2, 3);
@@ -45,13 +45,13 @@ describe('logger', () => {
errInstance.stack,
{ data: 1, fullError: errInstance },
2,
3,
3
);
});
it('logs the stack and the err data with it\'s own fullError property', () => {
const errInstance = new Error('An error.');
const anotherError = new Error('another error');
let errInstance = new Error('An error.');
let anotherError = new Error('another error');
logger.error(errInstance, {
data: 1,
@@ -64,12 +64,12 @@ describe('logger', () => {
errInstance.stack,
{ data: 1, fullError: anotherError },
2,
3,
3
);
});
it('logs the error when errorData is null', () => {
const errInstance = new Error('An error.');
let errInstance = new Error('An error.');
logger.error(errInstance, null, 2, 3);
@@ -79,12 +79,12 @@ describe('logger', () => {
errInstance.stack,
null,
2,
3,
3
);
});
it('logs the error when errorData is not an object', () => {
const errInstance = new Error('An error.');
let errInstance = new Error('An error.');
logger.error(errInstance, true, 2, 3);
@@ -94,12 +94,12 @@ describe('logger', () => {
errInstance.stack,
true,
2,
3,
3
);
});
it('logs the error when errorData does not include isHandledError property', () => {
const errInstance = new Error('An error.');
let errInstance = new Error('An error.');
logger.error(errInstance, { httpCode: 400 }, 2, 3);
@@ -109,12 +109,12 @@ describe('logger', () => {
errInstance.stack,
{ httpCode: 400, fullError: errInstance },
2,
3,
3
);
});
it('logs the error when errorData includes isHandledError property but is a 500 error', () => {
const errInstance = new Error('An error.');
let errInstance = new Error('An error.');
logger.error(errInstance, {
isHandledError: true,
@@ -127,12 +127,12 @@ describe('logger', () => {
errInstance.stack,
{ httpCode: 502, isHandledError: true, fullError: errInstance },
2,
3,
3
);
});
it('logs a warning when errorData includes isHandledError property and is not a 500 error', () => {
const errInstance = new Error('An error.');
let errInstance = new Error('An error.');
logger.error(errInstance, {
isHandledError: true,
@@ -145,12 +145,12 @@ describe('logger', () => {
errInstance.stack,
{ httpCode: 403, isHandledError: true, fullError: errInstance },
2,
3,
3
);
});
it('logs additional data from a CustomError', () => {
const errInstance = new NotFound('An error.');
let errInstance = new NotFound('An error.');
errInstance.customField = 'Some interesting data';
@@ -166,7 +166,7 @@ describe('logger', () => {
},
},
2,
3,
3
);
});
});
+77 -77
View File
@@ -1,9 +1,9 @@
/* eslint-disable camelcase */
import moment from 'moment';
import {
encrypt,
} from '../../../../website/server/libs/encryption';
import moment from 'moment';
import {
generateUser,
} from '../../../helpers/api-integration/v3';
@@ -20,11 +20,11 @@ import {
describe('Password Utilities', () => {
describe('compare', () => {
it('can compare a correct password hashed with SHA1', async () => {
const textPassword = 'mySecretPassword';
const salt = sha1MakeSalt();
const hashedPassword = sha1EncryptPassword(textPassword, salt);
let textPassword = 'mySecretPassword';
let salt = sha1MakeSalt();
let hashedPassword = sha1EncryptPassword(textPassword, salt);
const user = {
let user = {
auth: {
local: {
hashed_password: hashedPassword,
@@ -34,16 +34,16 @@ describe('Password Utilities', () => {
},
};
const isValidPassword = await compare(user, textPassword);
let isValidPassword = await compare(user, textPassword);
expect(isValidPassword).to.eql(true);
});
it('can compare an invalid password hashed with SHA1', async () => {
const textPassword = 'mySecretPassword';
const salt = sha1MakeSalt();
const hashedPassword = sha1EncryptPassword(textPassword, salt);
let textPassword = 'mySecretPassword';
let salt = sha1MakeSalt();
let hashedPassword = sha1EncryptPassword(textPassword, salt);
const user = {
let user = {
auth: {
local: {
hashed_password: hashedPassword,
@@ -53,15 +53,15 @@ describe('Password Utilities', () => {
},
};
const isValidPassword = await compare(user, 'wrongPassword');
let isValidPassword = await compare(user, 'wrongPassword');
expect(isValidPassword).to.eql(false);
});
it('can compare a correct password hashed with bcrypt', async () => {
const textPassword = 'mySecretPassword';
const hashedPassword = await bcryptHash(textPassword);
let textPassword = 'mySecretPassword';
let hashedPassword = await bcryptHash(textPassword);
const user = {
let user = {
auth: {
local: {
hashed_password: hashedPassword,
@@ -70,15 +70,15 @@ describe('Password Utilities', () => {
},
};
const isValidPassword = await compare(user, textPassword);
let isValidPassword = await compare(user, textPassword);
expect(isValidPassword).to.eql(true);
});
it('can compare an invalid password hashed with bcrypt', async () => {
const textPassword = 'mySecretPassword';
const hashedPassword = await bcryptHash(textPassword);
let textPassword = 'mySecretPassword';
let hashedPassword = await bcryptHash(textPassword);
const user = {
let user = {
auth: {
local: {
hashed_password: hashedPassword,
@@ -87,7 +87,7 @@ describe('Password Utilities', () => {
},
};
const isValidPassword = await compare(user, 'wrongPassword');
let isValidPassword = await compare(user, 'wrongPassword');
expect(isValidPassword).to.eql(false);
});
@@ -101,18 +101,18 @@ describe('Password Utilities', () => {
it('throws an error if passwordToCheck is missing', async () => {
try {
await compare({ a: true });
await compare({a: true});
} catch (e) {
expect(e.toString()).to.equal('Error: user and passwordToCheck are required parameters.');
}
});
it('defaults to SHA1 encryption if salt is provided', async () => {
const textPassword = 'mySecretPassword';
const salt = sha1MakeSalt();
const hashedPassword = sha1EncryptPassword(textPassword, salt);
let textPassword = 'mySecretPassword';
let salt = sha1MakeSalt();
let hashedPassword = sha1EncryptPassword(textPassword, salt);
const user = {
let user = {
auth: {
local: {
hashed_password: hashedPassword,
@@ -122,7 +122,7 @@ describe('Password Utilities', () => {
},
};
const isValidPassword = await compare(user, textPassword);
let isValidPassword = await compare(user, textPassword);
expect(isValidPassword).to.eql(true);
});
@@ -141,29 +141,29 @@ describe('Password Utilities', () => {
});
it('returns true if comparing the same password', async () => {
const textPassword = 'mySecretPassword';
const hashedPassword = await bcryptHash(textPassword);
let textPassword = 'mySecretPassword';
let hashedPassword = await bcryptHash(textPassword);
const isValidPassword = await bcryptCompare(textPassword, hashedPassword);
let isValidPassword = await bcryptCompare(textPassword, hashedPassword);
expect(isValidPassword).to.eql(true);
});
it('returns true if comparing a different password', async () => {
const textPassword = 'mySecretPassword';
const hashedPassword = await bcryptHash(textPassword);
let textPassword = 'mySecretPassword';
let hashedPassword = await bcryptHash(textPassword);
const isValidPassword = await bcryptCompare('anotherPassword', hashedPassword);
let isValidPassword = await bcryptCompare('anotherPassword', hashedPassword);
expect(isValidPassword).to.eql(false);
});
});
describe('convertToBcrypt', () => {
it('converts an user password hashed with sha1 to bcrypt', async () => {
const textPassword = 'mySecretPassword';
const salt = sha1MakeSalt();
const hashedPassword = sha1EncryptPassword(textPassword, salt);
let textPassword = 'mySecretPassword';
let salt = sha1MakeSalt();
let hashedPassword = sha1EncryptPassword(textPassword, salt);
const user = {
let user = {
auth: {
local: {
hashed_password: hashedPassword,
@@ -178,7 +178,7 @@ describe('Password Utilities', () => {
expect(user.auth.local.passwordHashMethod).to.equal('bcrypt');
expect(user.auth.local.hashed_password).to.be.a.string;
const isValidPassword = await compare(user, textPassword);
let isValidPassword = await compare(user, textPassword);
expect(isValidPassword).to.eql(true);
});
@@ -192,7 +192,7 @@ describe('Password Utilities', () => {
it('throws an error if plainTextPassword is missing', async () => {
try {
await convertToBcrypt({ a: true });
await convertToBcrypt({a: true});
} catch (e) {
expect(e.toString()).to.equal('Error: user and plainTextPassword are required parameters.');
}
@@ -201,18 +201,18 @@ describe('Password Utilities', () => {
describe('validatePasswordResetCodeAndFindUser', () => {
it('returns false if the code is missing', async () => {
const res = await validatePasswordResetCodeAndFindUser();
let res = await validatePasswordResetCodeAndFindUser();
expect(res).to.equal(false);
});
it('returns false if the code is invalid json', async () => {
const res = await validatePasswordResetCodeAndFindUser('invalid json');
let res = await validatePasswordResetCodeAndFindUser('invalid json');
expect(res).to.equal(false);
});
it('returns false if the code cannot be decrypted', async () => {
const user = await generateUser();
const res = await validatePasswordResetCodeAndFindUser(JSON.stringify({ // not encrypted
let user = await generateUser();
let res = await validatePasswordResetCodeAndFindUser(JSON.stringify({ // not encrypted
userId: user._id,
expiresAt: new Date(),
}));
@@ -220,71 +220,71 @@ describe('Password Utilities', () => {
});
it('returns false if the code is expired', async () => {
const user = await generateUser();
let user = await generateUser();
const code = encrypt(JSON.stringify({
let code = encrypt(JSON.stringify({
userId: user._id,
expiresAt: moment().subtract({ minutes: 1 }),
expiresAt: moment().subtract({minutes: 1}),
}));
await user.update({
'auth.local.passwordResetCode': code,
});
const res = await validatePasswordResetCodeAndFindUser(code);
let res = await validatePasswordResetCodeAndFindUser(code);
expect(res).to.equal(false);
});
it('returns false if the user does not exist', async () => {
const res = await validatePasswordResetCodeAndFindUser(encrypt(JSON.stringify({
let res = await validatePasswordResetCodeAndFindUser(encrypt(JSON.stringify({
userId: Date.now().toString(),
expiresAt: moment().add({ days: 1 }),
expiresAt: moment().add({days: 1}),
})));
expect(res).to.equal(false);
});
it('returns false if the user has no local auth', async () => {
const user = await generateUser({
let user = await generateUser({
auth: {
facebook: {},
},
});
const res = await validatePasswordResetCodeAndFindUser(encrypt(JSON.stringify({
let res = await validatePasswordResetCodeAndFindUser(encrypt(JSON.stringify({
userId: user._id,
expiresAt: moment().add({ days: 1 }),
expiresAt: moment().add({days: 1}),
})));
expect(res).to.equal(false);
});
it('returns false if the code doesn\'t match the one saved at user.auth.passwordResetCode', async () => {
const user = await generateUser();
let user = await generateUser();
const code = encrypt(JSON.stringify({
let code = encrypt(JSON.stringify({
userId: user._id,
expiresAt: moment().add({ days: 1 }),
expiresAt: moment().add({days: 1}),
}));
await user.update({
'auth.local.passwordResetCode': 'invalid',
});
const res = await validatePasswordResetCodeAndFindUser(code);
let res = await validatePasswordResetCodeAndFindUser(code);
expect(res).to.equal(false);
});
it('returns the user if the password reset code is valid', async () => {
const user = await generateUser();
let user = await generateUser();
const code = encrypt(JSON.stringify({
let code = encrypt(JSON.stringify({
userId: user._id,
expiresAt: moment().add({ days: 1 }),
expiresAt: moment().add({days: 1}),
}));
await user.update({
'auth.local.passwordResetCode': code,
});
const res = await validatePasswordResetCodeAndFindUser(code);
let res = await validatePasswordResetCodeAndFindUser(code);
expect(res).not.to.equal(false);
expect(res._id).to.equal(user._id);
});
@@ -293,8 +293,8 @@ describe('Password Utilities', () => {
describe('bcrypt', () => {
describe('Hash', () => {
it('returns a hashed string', async () => {
const textPassword = 'mySecretPassword';
const hashedPassword = await bcryptHash(textPassword);
let textPassword = 'mySecretPassword';
let hashedPassword = await bcryptHash(textPassword);
expect(hashedPassword).to.be.a.string;
});
@@ -302,18 +302,18 @@ describe('Password Utilities', () => {
describe('Compare', () => {
it('returns true if comparing the same password', async () => {
const textPassword = 'mySecretPassword';
const hashedPassword = await bcryptHash(textPassword);
let textPassword = 'mySecretPassword';
let hashedPassword = await bcryptHash(textPassword);
const isValidPassword = await bcryptCompare(textPassword, hashedPassword);
let isValidPassword = await bcryptCompare(textPassword, hashedPassword);
expect(isValidPassword).to.eql(true);
});
it('returns true if comparing a different password', async () => {
const textPassword = 'mySecretPassword';
const hashedPassword = await bcryptHash(textPassword);
let textPassword = 'mySecretPassword';
let hashedPassword = await bcryptHash(textPassword);
const isValidPassword = await bcryptCompare('anotherPassword', hashedPassword);
let isValidPassword = await bcryptCompare('anotherPassword', hashedPassword);
expect(isValidPassword).to.eql(false);
});
});
@@ -322,19 +322,19 @@ describe('Password Utilities', () => {
describe('SHA1', () => {
describe('Encrypt', () => {
it('always encrypt the same password to the same value when using the same salt', () => {
const textPassword = 'mySecretPassword';
const salt = sha1MakeSalt();
const encryptedPassword = sha1EncryptPassword(textPassword, salt);
let textPassword = 'mySecretPassword';
let salt = sha1MakeSalt();
let encryptedPassword = sha1EncryptPassword(textPassword, salt);
expect(sha1EncryptPassword(textPassword, salt)).to.eql(encryptedPassword);
});
it('never encrypt the same password to the same value when using a different salt', () => {
const textPassword = 'mySecretPassword';
const aSalt = sha1MakeSalt();
const anotherSalt = sha1MakeSalt();
const anEncryptedPassword = sha1EncryptPassword(textPassword, aSalt);
const anotherEncryptedPassword = sha1EncryptPassword(textPassword, anotherSalt);
let textPassword = 'mySecretPassword';
let aSalt = sha1MakeSalt();
let anotherSalt = sha1MakeSalt();
let anEncryptedPassword = sha1EncryptPassword(textPassword, aSalt);
let anotherEncryptedPassword = sha1EncryptPassword(textPassword, anotherSalt);
expect(anEncryptedPassword).not.to.eql(anotherEncryptedPassword);
});
@@ -342,14 +342,14 @@ describe('Password Utilities', () => {
describe('Make Salt', () => {
it('creates a salt with length 10 by default', () => {
const salt = sha1MakeSalt();
let salt = sha1MakeSalt();
expect(salt.length).to.eql(10);
});
it('can create a salt of any length', () => {
const length = 24;
const salt = sha1MakeSalt(length);
let length = 24;
let salt = sha1MakeSalt(length);
expect(salt.length).to.eql(length);
});
@@ -2,20 +2,19 @@ import moment from 'moment';
import {
generateGroup,
} from '../../../../../helpers/api-unit.helper';
} from '../../../../../helpers/api-unit.helper.js';
import { model as User } from '../../../../../../website/server/models/user';
import amzLib from '../../../../../../website/server/libs/payments/amazon';
import payments from '../../../../../../website/server/libs/payments/payments';
import common from '../../../../../../website/common';
import { createNonLeaderGroupMember } from '../paymentHelpers';
const { i18n } = common;
const i18n = common.i18n;
describe('Amazon Payments - Cancel Subscription', () => {
const subKey = 'basic_3mo';
let user; let group; let headers; let billingAgreementId; let subscriptionBlock; let
subscriptionLength;
let user, group, headers, billingAgreementId, subscriptionBlock, subscriptionLength;
let getBillingAgreementDetailsSpy;
let paymentCancelSubscriptionSpy;
@@ -51,7 +50,7 @@ describe('Amazon Payments - Cancel Subscription', () => {
getBillingAgreementDetailsSpy = sinon.stub(amzLib, 'getBillingAgreementDetails')
.resolves({
BillingAgreementDetails: {
BillingAgreementStatus: { State: 'Open' },
BillingAgreementStatus: {State: 'Open'},
},
});
}
@@ -82,7 +81,7 @@ describe('Amazon Payments - Cancel Subscription', () => {
getBillingAgreementDetailsSpy = sinon.stub(amzLib, 'getBillingAgreementDetails');
getBillingAgreementDetailsSpy.resolves({
BillingAgreementDetails: {
BillingAgreementStatus: { State: 'Closed' },
BillingAgreementStatus: {State: 'Closed'},
},
});
@@ -90,7 +89,7 @@ describe('Amazon Payments - Cancel Subscription', () => {
paymentCancelSubscriptionSpy.resolves({});
});
afterEach(() => {
afterEach(function () {
amzLib.getBillingAgreementDetails.restore();
payments.cancelSubscription.restore();
});
@@ -98,7 +97,7 @@ describe('Amazon Payments - Cancel Subscription', () => {
it('should throw an error if we are missing a subscription', async () => {
user.purchased.plan.customerId = undefined;
await expect(amzLib.cancelSubscription({ user }))
await expect(amzLib.cancelSubscription({user}))
.to.eventually.be.rejected.and.to.eql({
httpCode: 401,
name: 'NotAuthorized',
@@ -109,7 +108,7 @@ describe('Amazon Payments - Cancel Subscription', () => {
it('should cancel a user subscription', async () => {
billingAgreementId = user.purchased.plan.customerId;
await amzLib.cancelSubscription({ user, headers });
await amzLib.cancelSubscription({user, headers});
expectAmazonCancelUserSubscriptionSpy();
expectAmazonStubs();
@@ -118,10 +117,10 @@ describe('Amazon Payments - Cancel Subscription', () => {
it('should close a user subscription if amazon not closed', async () => {
amzLib.getBillingAgreementDetails.restore();
expectBillingAggreementDetailSpy();
const closeBillingAgreementSpy = sinon.stub(amzLib, 'closeBillingAgreement').resolves({});
let closeBillingAgreementSpy = sinon.stub(amzLib, 'closeBillingAgreement').resolves({});
billingAgreementId = user.purchased.plan.customerId;
await amzLib.cancelSubscription({ user, headers });
await amzLib.cancelSubscription({user, headers});
expectAmazonStubs();
expect(closeBillingAgreementSpy).to.be.calledOnce;
@@ -133,7 +132,7 @@ describe('Amazon Payments - Cancel Subscription', () => {
});
it('should throw an error if group is not found', async () => {
await expect(amzLib.cancelSubscription({ user, groupId: 'fake-id' }))
await expect(amzLib.cancelSubscription({user, groupId: 'fake-id'}))
.to.eventually.be.rejected.and.to.eql({
httpCode: 404,
name: 'NotFound',
@@ -142,9 +141,9 @@ describe('Amazon Payments - Cancel Subscription', () => {
});
it('should throw an error if user is not group leader', async () => {
const nonLeader = await createNonLeaderGroupMember(group);
let nonLeader = await createNonLeaderGroupMember(group);
await expect(amzLib.cancelSubscription({ user: nonLeader, groupId: group._id }))
await expect(amzLib.cancelSubscription({user: nonLeader, groupId: group._id}))
.to.eventually.be.rejected.and.to.eql({
httpCode: 401,
name: 'NotAuthorized',
@@ -155,7 +154,7 @@ describe('Amazon Payments - Cancel Subscription', () => {
it('should cancel a group subscription', async () => {
billingAgreementId = group.purchased.plan.customerId;
await amzLib.cancelSubscription({ user, groupId: group._id, headers });
await amzLib.cancelSubscription({user, groupId: group._id, headers});
expectAmazonCancelGroupSubscriptionSpy(group._id);
expectAmazonStubs();
@@ -164,10 +163,10 @@ describe('Amazon Payments - Cancel Subscription', () => {
it('should close a group subscription if amazon not closed', async () => {
amzLib.getBillingAgreementDetails.restore();
expectBillingAggreementDetailSpy();
const closeBillingAgreementSpy = sinon.stub(amzLib, 'closeBillingAgreement').resolves({});
let closeBillingAgreementSpy = sinon.stub(amzLib, 'closeBillingAgreement').resolves({});
billingAgreementId = group.purchased.plan.customerId;
await amzLib.cancelSubscription({ user, groupId: group._id, headers });
await amzLib.cancelSubscription({user, groupId: group._id, headers});
expectAmazonStubs();
expect(closeBillingAgreementSpy).to.be.calledOnce;
@@ -3,12 +3,11 @@ import amzLib from '../../../../../../website/server/libs/payments/amazon';
import payments from '../../../../../../website/server/libs/payments/payments';
import common from '../../../../../../website/common';
const { i18n } = common;
const i18n = common.i18n;
describe('Amazon Payments - Checkout', () => {
const subKey = 'basic_3mo';
let user; let orderReferenceId; let
headers;
let user, orderReferenceId, headers;
let setOrderReferenceDetailsSpy;
let confirmOrderReferenceSpy;
let authorizeSpy;
@@ -63,7 +62,7 @@ describe('Amazon Payments - Checkout', () => {
expect(closeOrderReferenceSpy).to.be.calledWith({ AmazonOrderReferenceId: orderReferenceId });
}
beforeEach(() => {
beforeEach(function () {
user = new User();
headers = {};
orderReferenceId = 'orderReferenceId';
@@ -89,7 +88,7 @@ describe('Amazon Payments - Checkout', () => {
sinon.stub(common, 'uuid').returns('uuid-generated');
});
afterEach(() => {
afterEach(function () {
amzLib.setOrderReferenceDetails.restore();
amzLib.confirmOrderReference.restore();
amzLib.authorize.restore();
@@ -102,7 +101,7 @@ describe('Amazon Payments - Checkout', () => {
function expectBuyGemsStub (paymentMethod, gift) {
expect(paymentBuyGemsStub).to.be.calledOnce;
const expectedArgs = {
let expectedArgs = {
user,
paymentMethod,
headers,
@@ -113,7 +112,7 @@ describe('Amazon Payments - Checkout', () => {
it('should purchase gems', async () => {
sinon.stub(user, 'canGetGems').resolves(true);
await amzLib.checkout({ user, orderReferenceId, headers });
await amzLib.checkout({user, orderReferenceId, headers});
expectBuyGemsStub(amzLib.constants.PAYMENT_METHOD);
expectAmazonStubs();
@@ -122,9 +121,9 @@ describe('Amazon Payments - Checkout', () => {
});
it('should error if gem amount is too low', async () => {
const receivingUser = new User();
let receivingUser = new User();
receivingUser.save();
const gift = {
let gift = {
type: 'gems',
gems: {
amount: 0,
@@ -132,9 +131,7 @@ describe('Amazon Payments - Checkout', () => {
},
};
await expect(amzLib.checkout({
gift, user, orderReferenceId, headers,
}))
await expect(amzLib.checkout({gift, user, orderReferenceId, headers}))
.to.eventually.be.rejected.and.to.eql({
httpCode: 400,
message: 'Amount must be at least 1.',
@@ -144,19 +141,18 @@ describe('Amazon Payments - Checkout', () => {
it('should error if user cannot get gems gems', async () => {
sinon.stub(user, 'canGetGems').resolves(false);
await expect(amzLib.checkout({ user, orderReferenceId, headers }))
.to.eventually.be.rejected.and.to.eql({
httpCode: 401,
message: i18n.t('groupPolicyCannotGetGems'),
name: 'NotAuthorized',
});
await expect(amzLib.checkout({user, orderReferenceId, headers})).to.eventually.be.rejected.and.to.eql({
httpCode: 401,
message: i18n.t('groupPolicyCannotGetGems'),
name: 'NotAuthorized',
});
user.canGetGems.restore();
});
it('should gift gems', async () => {
const receivingUser = new User();
let receivingUser = new User();
await receivingUser.save();
const gift = {
let gift = {
type: 'gems',
uuid: receivingUser._id,
gems: {
@@ -164,18 +160,16 @@ describe('Amazon Payments - Checkout', () => {
},
};
amount = 16 / 4;
await amzLib.checkout({
gift, user, orderReferenceId, headers,
});
await amzLib.checkout({gift, user, orderReferenceId, headers});
expectBuyGemsStub(amzLib.constants.PAYMENT_METHOD_GIFT, gift);
expectAmazonStubs();
});
it('should gift a subscription', async () => {
const receivingUser = new User();
let receivingUser = new User();
receivingUser.save();
const gift = {
let gift = {
type: 'subscription',
subscription: {
key: subKey,
@@ -184,9 +178,7 @@ describe('Amazon Payments - Checkout', () => {
};
amount = common.content.subscriptionBlocks[subKey].price;
await amzLib.checkout({
user, orderReferenceId, headers, gift,
});
await amzLib.checkout({user, orderReferenceId, headers, gift});
gift.member = receivingUser;
expect(paymentCreateSubscritionStub).to.be.calledOnce;
@@ -2,19 +2,18 @@ import cc from 'coupon-code';
import {
generateGroup,
} from '../../../../../helpers/api-unit.helper';
} from '../../../../../helpers/api-unit.helper.js';
import { model as User } from '../../../../../../website/server/models/user';
import { model as Coupon } from '../../../../../../website/server/models/coupon';
import amzLib from '../../../../../../website/server/libs/payments/amazon';
import payments from '../../../../../../website/server/libs/payments/payments';
import common from '../../../../../../website/common';
const { i18n } = common;
const i18n = common.i18n;
describe('Amazon Payments - Subscribe', () => {
const subKey = 'basic_3mo';
let user; let group; let amount; let billingAgreementId; let sub; let coupon; let groupId; let
headers;
let user, group, amount, billingAgreementId, sub, coupon, groupId, headers;
let amazonSetBillingAgreementDetailsSpy;
let amazonConfirmBillingAgreementSpy;
let amazonAuthorizeOnBillingAgreementSpy;
@@ -61,7 +60,7 @@ describe('Amazon Payments - Subscribe', () => {
sinon.stub(common, 'uuid').returns('uuid-generated');
});
afterEach(() => {
afterEach(function () {
amzLib.setBillingAgreementDetails.restore();
amzLib.confirmBillingAgreement.restore();
amzLib.authorizeOnBillingAgreement.restore();
@@ -169,7 +168,7 @@ describe('Amazon Payments - Subscribe', () => {
sub.key = 'google_6mo';
coupon = 'example-coupon';
const couponModel = new Coupon();
let couponModel = new Coupon();
couponModel.event = 'google_6mo';
await couponModel.save();
@@ -196,9 +195,9 @@ describe('Amazon Payments - Subscribe', () => {
sub.key = 'google_6mo';
coupon = 'example-coupon';
const couponModel = new Coupon();
let couponModel = new Coupon();
couponModel.event = 'google_6mo';
const updatedCouponModel = await couponModel.save();
let updatedCouponModel = await couponModel.save();
sinon.stub(cc, 'validate').returns(updatedCouponModel._id);

Some files were not shown because too many files have changed in this diff Show More