Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 93437a00dd | |||
| 31afa0345e | |||
| b2c278b00a | |||
| b54037f230 | |||
| 488dcb916a | |||
| 10792b65be |
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"presets": ["es2015"],
|
||||
"plugins": [
|
||||
"transform-object-rest-spread",
|
||||
["transform-async-to-module-method", {
|
||||
"module": "bluebird",
|
||||
"method": "coroutine"
|
||||
|
||||
@@ -14,7 +14,7 @@ files:
|
||||
owner: root
|
||||
group: users
|
||||
content: |
|
||||
$(ls -td /opt/elasticbeanstalk/node-install/node-* | head -1)/bin/npm install -g npm@5
|
||||
$(ls -td /opt/elasticbeanstalk/node-install/node-* | head -1)/bin/npm install -g npm@4
|
||||
container_commands:
|
||||
01_makeBabel:
|
||||
command: "touch /tmp/.babel.json"
|
||||
|
||||
+5
-2
@@ -8,13 +8,16 @@ dist/
|
||||
dist-client/
|
||||
|
||||
# Not linted
|
||||
migrations/*
|
||||
website/client-old/
|
||||
scripts/*
|
||||
test/server_side/**/*
|
||||
test/client-old/spec/**/*
|
||||
|
||||
# Temporarilly disabled. These should be removed when the linting errors are fixed TODO
|
||||
migrations/*
|
||||
scripts/*
|
||||
website/common/script/content/index.js
|
||||
website/common/browserify.js
|
||||
test/content/**/*
|
||||
Gruntfile.js
|
||||
gulpfile.js
|
||||
gulp
|
||||
@@ -1,6 +1,6 @@
|
||||
# Reporting Bugs
|
||||
|
||||
[Please see these instructions for reporting bugs](https://github.com/HabitRPG/habitica/issues/2760)
|
||||
[Please see these instructions for reporting bugs](https://github.com/HabitRPG/habitrpg/issues/2760)
|
||||
|
||||
# Pull Request
|
||||
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
[//]: # (Before logging this issue, please post to the Report a Bug guild from the Habitica website's Help menu. Most bugs can be handled quickly there. If a GitHub issue is needed, you will be advised of that by a moderator or staff member -- a player with a dark blue or purple name. It is recommended that you don't create a new issue unless advised to.)
|
||||
[//]: # (Before logging this issue, look through common problems at https://github.com/HabitRPG/habitrpg/issues If you find your issue there, read at least the first post to see if there is a workaround for you)
|
||||
|
||||
[//]: # (Bugs in the mobile apps can also be reported there.)
|
||||
[//]: # (Github is primarily used for reporting bugs. If you have a feature request, use "Help > Request a Feature" so that the feature request can be vetted by the larger Habitica community)
|
||||
|
||||
[//]: # (If you have a feature request, use "Help > Request a Feature", not GitHub or the Report a Bug guild.)
|
||||
[//]: # (To report a bug in one of the mobile apps, please report it in the correct repository. Android: https://github.com/HabitRPG/habitrpg-android, iOS: https://github.com/HabitRPG/habitrpg-ios)
|
||||
|
||||
[//]: # (For more guidelines see https://github.com/HabitRPG/habitica/issues/2760)
|
||||
[//]: # (For more guidelines see https://github.com/HabitRPG/habitrpg/issues/2760)
|
||||
|
||||
[//]: # (Fill out relevant information - UUID is found in Settings -> API)
|
||||
### General Info
|
||||
General Info
|
||||
* UUID:
|
||||
* Browser:
|
||||
* OS:
|
||||
|
||||
### Description
|
||||
[//]: # (Describe bug in detail here. Include screenshots if helpful.)
|
||||
[//]: # (Describe bug in detail here. Include pictures if helpful.)
|
||||
|
||||
|
||||
|
||||
#### Console Errors
|
||||
[//]: # (Include any JavaScript console errors here.)
|
||||
|
||||
+10
-15
@@ -2,9 +2,6 @@ language: node_js
|
||||
node_js:
|
||||
- '6'
|
||||
sudo: required
|
||||
dist: precise
|
||||
services:
|
||||
- mongodb
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
@@ -13,26 +10,24 @@ addons:
|
||||
- g++-4.8
|
||||
before_install:
|
||||
- $CXX --version
|
||||
- npm install -g npm@5
|
||||
- npm install -g npm@4
|
||||
- if [ $REQUIRES_SERVER ]; then sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10; echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list; sudo apt-get update; sudo apt-get install mongodb-org-server; fi
|
||||
install:
|
||||
- npm install &> npm.install.log || (cat npm.install.log; false)
|
||||
before_script:
|
||||
- npm run test:build
|
||||
- cp config.json.example config.json
|
||||
- sleep 15
|
||||
script:
|
||||
- npm run $TEST
|
||||
- if [ $COVERAGE ]; then ./node_modules/.bin/lcov-result-merger 'coverage/**/*.info' | ./node_modules/coveralls/bin/coveralls.js; fi
|
||||
- if [ $REQUIRES_SERVER ]; then until nc -z localhost 27017; do echo Waiting for MongoDB; sleep 1; done; export DISPLAY=:99; fi
|
||||
after_script:
|
||||
- ./node_modules/.bin/lcov-result-merger 'coverage/**/*.info' | ./node_modules/coveralls/bin/coveralls.js
|
||||
script: npm run $TEST
|
||||
env:
|
||||
global:
|
||||
- CXX=g++-4.8
|
||||
- DISABLE_REQUEST_LOGGING=true
|
||||
matrix:
|
||||
- TEST="lint"
|
||||
- TEST="test:api-v3" REQUIRES_SERVER=true COVERAGE=true
|
||||
- TEST="test:api-v3" REQUIRES_SERVER=true
|
||||
- TEST="test:sanity"
|
||||
- TEST="test:content" COVERAGE=true
|
||||
- TEST="test:common" COVERAGE=true
|
||||
- TEST="client:unit" COVERAGE=true
|
||||
- TEST="apidoc"
|
||||
- TEST="test:content"
|
||||
- TEST="test:common"
|
||||
- TEST="test:karma"
|
||||
- TEST="client:unit"
|
||||
|
||||
+45
-22
@@ -1,22 +1,45 @@
|
||||
FROM node:boron
|
||||
|
||||
# Upgrade NPM to v5 (Yarn is needed because of this bug https://github.com/npm/npm/issues/16807)
|
||||
# The used solution is suggested here https://github.com/npm/npm/issues/16807#issuecomment-313591975
|
||||
RUN yarn global add npm@5
|
||||
# Install global packages
|
||||
RUN npm install -g gulp grunt-cli bower mocha
|
||||
|
||||
# Clone Habitica repo and install dependencies
|
||||
RUN mkdir -p /usr/src/habitrpg
|
||||
WORKDIR /usr/src/habitrpg
|
||||
RUN git clone https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
|
||||
RUN cp config.json.example config.json
|
||||
RUN npm install
|
||||
RUN bower install --allow-root
|
||||
|
||||
# Create Build dir
|
||||
RUN mkdir -p ./website/build
|
||||
|
||||
# Start Habitica
|
||||
EXPOSE 3000
|
||||
CMD ["npm", "start"]
|
||||
FROM ubuntu:trusty
|
||||
|
||||
MAINTAINER Sabe Jones <sabe@habitica.com>
|
||||
|
||||
# Avoid ERROR: invoke-rc.d: policy-rc.d denied execution of start.
|
||||
RUN echo -e '#!/bin/sh\nexit 0' > /usr/sbin/policy-rc.d
|
||||
|
||||
# Install prerequisites
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y \
|
||||
build-essential \
|
||||
curl \
|
||||
git \
|
||||
libfontconfig1 \
|
||||
libfreetype6 \
|
||||
libkrb5-dev \
|
||||
python
|
||||
|
||||
# Install NodeJS
|
||||
RUN curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
|
||||
RUN apt-get install -y nodejs
|
||||
|
||||
# Install npm@latest
|
||||
RUN curl -sL https://www.npmjs.org/install.sh | sh
|
||||
|
||||
# Clean up package management
|
||||
RUN apt-get clean
|
||||
RUN rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install global packages
|
||||
RUN npm install -g gulp grunt-cli bower mocha
|
||||
|
||||
# Clone Habitica repo and install dependencies
|
||||
WORKDIR /habitrpg
|
||||
RUN git clone https://github.com/HabitRPG/habitica.git /habitrpg
|
||||
RUN npm install
|
||||
RUN bower install --allow-root
|
||||
|
||||
# Create environment config file and build directory
|
||||
RUN cp config.json.example config.json
|
||||
RUN mkdir -p ./website/build
|
||||
|
||||
# Start Habitica
|
||||
EXPOSE 3000
|
||||
CMD ["npm", "start"]
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
FROM node:boron
|
||||
|
||||
# Upgrade NPM to v5 (Yarn is needed because of this bug https://github.com/npm/npm/issues/16807)
|
||||
# The used solution is suggested here https://github.com/npm/npm/issues/16807#issuecomment-313591975
|
||||
RUN yarn global add npm@5
|
||||
# Install global packages
|
||||
RUN npm install -g gulp grunt-cli bower mocha
|
||||
|
||||
# Clone Habitica repo and install dependencies
|
||||
RUN mkdir -p /usr/src/habitrpg
|
||||
WORKDIR /usr/src/habitrpg
|
||||
RUN git clone --branch release https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
|
||||
RUN npm install
|
||||
RUN bower install --allow-root
|
||||
RUN gulp build:prod --force
|
||||
|
||||
# Create Build dir
|
||||
RUN mkdir -p ./website/build
|
||||
|
||||
# Start Habitica
|
||||
EXPOSE 3000
|
||||
CMD ["node", "./website/transpiled-babel/index.js"]
|
||||
@@ -5,7 +5,8 @@ Habitica [.
|
||||
For an introduction to the technologies used and how the software is organized, refer to [Contributing to Habitica](http://habitica.wikia.com/wiki/Contributing_to_Habitica#Coders_.28Web_.26_Mobile.29) - "Coders (Web & Mobile)" section.
|
||||
|
||||
To set up a local install of Habitica for development and testing on various platforms, see [Setting up Habitica Locally](http://habitica.wikia.com/wiki/Setting_up_Habitica_Locally).
|
||||
To set up a local install of Habitica for development and testing, see [Setting up Habitica Locally](http://habitica.wikia.com/wiki/Setting_up_Habitica_Locally), which contains instructions for Windows, *nix / Mac OS, and Vagrant.
|
||||
|
||||
Then read [Guidance for Blacksmiths](http://habitica.wikia.com/wiki/Guidance_for_Blacksmiths) for additional instructions and useful tips.
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@
|
||||
"bootstrap-tour": "0.10.1",
|
||||
"css-social-buttons": "samcollins/css-social-buttons#v1.1.1 ",
|
||||
"github-buttons": "mdo/github-buttons#v3.0.0",
|
||||
"hello": "1.14.1",
|
||||
"hello": "1.13.4",
|
||||
"jquery": "2.1.0",
|
||||
"jquery-colorbox": "1.4.36",
|
||||
"jquery-ui": "1.10.3",
|
||||
|
||||
+3
-18
@@ -66,22 +66,16 @@
|
||||
},
|
||||
"mode":"sandbox",
|
||||
"client_id":"client_id",
|
||||
"client_secret":"client_secret",
|
||||
"experience_profile_id": ""
|
||||
"client_secret":"client_secret"
|
||||
},
|
||||
"IAP_GOOGLE_KEYDIR": "/path/to/google/public/key/dir/",
|
||||
"LOGGLY_TOKEN": "token",
|
||||
"LOGGLY_ACCOUNT": "account",
|
||||
"PUSH_CONFIGS": {
|
||||
"GCM_SERVER_API_KEY": "",
|
||||
"APN_ENABLED": "false",
|
||||
"APN_ENABLED": "true",
|
||||
"FCM_SERVER_API_KEY": ""
|
||||
},
|
||||
"SITE_HTTP_AUTH": {
|
||||
"ENABLED": "false",
|
||||
"USERNAME": "admin",
|
||||
"PASSWORD": "password"
|
||||
},
|
||||
"PUSHER": {
|
||||
"ENABLED": "false",
|
||||
"APP_ID": "appId",
|
||||
@@ -93,14 +87,5 @@
|
||||
"FLAGGING_FOOTER_LINK": "https://habitrpg.github.io/flag-o-rama/",
|
||||
"SUBSCRIPTIONS_URL": "https://hooks.slack.com/services/id/id/id"
|
||||
},
|
||||
"ITUNES_SHARED_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
||||
"EMAILS" : {
|
||||
"COMMUNITY_MANAGER_EMAIL" : "leslie@habitica.com",
|
||||
"TECH_ASSISTANCE_EMAIL" : "admin@habitica.com",
|
||||
"PRESS_ENQUIRY_EMAIL" : "leslie@habitica.com"
|
||||
},
|
||||
"LOGGLY" : {
|
||||
"TOKEN" : "example-token",
|
||||
"SUBDOMAIN" : "exmaple-subdomain"
|
||||
}
|
||||
"ITUNES_SHARED_SECRET": "aaaabbbbccccddddeeeeffff00001111"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done
|
||||
|
||||
/**
|
||||
* database_reports/count_users_who_own_specified_gear.js
|
||||
* https://github.com/HabitRPG/habitica/pull/3884
|
||||
* https://github.com/HabitRPG/habitrpg/pull/3884
|
||||
*/
|
||||
|
||||
var thingsOfInterest = {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
web:
|
||||
volumes:
|
||||
- '.:/usr/src/habitrpg'
|
||||
- '.:/habitrpg'
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import gulp from 'gulp';
|
||||
import fs from 'fs';
|
||||
|
||||
// Copy Bootstrap 4 config variables from /website /node_modules so we can check
|
||||
// them into Git
|
||||
|
||||
const BOOSTRAP_NEW_CONFIG_PATH = 'website/client/assets/scss/bootstrap_config.scss';
|
||||
const BOOTSTRAP_ORIGINAL_CONFIG_PATH = 'node_modules/bootstrap/scss/_custom.scss';
|
||||
|
||||
// https://stackoverflow.com/a/14387791/969528
|
||||
function copyFile(source, target, cb) {
|
||||
let cbCalled = false;
|
||||
|
||||
function done(err) {
|
||||
if (!cbCalled) {
|
||||
cb(err);
|
||||
cbCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
let rd = fs.createReadStream(source);
|
||||
rd.on('error', done);
|
||||
let wr = fs.createWriteStream(target);
|
||||
wr.on('error', done);
|
||||
wr.on('close', () => done());
|
||||
rd.pipe(wr);
|
||||
}
|
||||
|
||||
gulp.task('bootstrap', (done) => {
|
||||
// use new config
|
||||
copyFile(
|
||||
BOOSTRAP_NEW_CONFIG_PATH,
|
||||
BOOTSTRAP_ORIGINAL_CONFIG_PATH,
|
||||
done,
|
||||
);
|
||||
});
|
||||
+2
-16
@@ -1,7 +1,6 @@
|
||||
import gulp from 'gulp';
|
||||
import runSequence from 'run-sequence';
|
||||
import babel from 'gulp-babel';
|
||||
import webpackProductionBuild from '../webpack/build';
|
||||
require('gulp-grunt')(gulp);
|
||||
|
||||
gulp.task('build', () => {
|
||||
@@ -26,15 +25,7 @@ gulp.task('build:common', () => {
|
||||
|
||||
gulp.task('build:server', ['build:src', 'build:common']);
|
||||
|
||||
// Client Production Build
|
||||
gulp.task('build:client', ['bootstrap'], (done) => {
|
||||
webpackProductionBuild((err, output) => {
|
||||
if (err) return done(err);
|
||||
console.log(output);
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('build:dev', ['browserify', 'prepare:staticNewStuff'], (done) => {
|
||||
gulp.task('build:dev', ['browserify', 'prepare:staticNewStuff', 'semantic-ui'], (done) => {
|
||||
gulp.start('grunt-build:dev', done);
|
||||
});
|
||||
|
||||
@@ -42,12 +33,7 @@ gulp.task('build:dev:watch', ['build:dev'], () => {
|
||||
gulp.watch(['website/client-old/**/*.styl', 'website/common/script/*']);
|
||||
});
|
||||
|
||||
gulp.task('build:prod', [
|
||||
'browserify',
|
||||
'build:server',
|
||||
'prepare:staticNewStuff',
|
||||
'build:client',
|
||||
], (done) => {
|
||||
gulp.task('build:prod', ['browserify', 'build:server', 'prepare:staticNewStuff', 'semantic-ui'], (done) => {
|
||||
runSequence(
|
||||
'grunt-build:prod',
|
||||
'apidoc',
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import gulp from 'gulp';
|
||||
import fs from 'fs';
|
||||
|
||||
// Make semantic-ui-less work with a theme in a different folder
|
||||
// Code taken from https://www.artembutusov.com/webpack-semantic-ui/
|
||||
|
||||
// Relative to node_modules/semantic-ui-less
|
||||
const SEMANTIC_THEME_PATH = '../../website/client/assets/less/semantic-ui/theme.config';
|
||||
|
||||
// fix well known bug with default distribution
|
||||
function fixFontPath (filename) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readFile(filename, 'utf8', (err, content) => {
|
||||
if (err) return reject(err);
|
||||
|
||||
let newContent = content.replace(
|
||||
'@fontPath : \'../../themes/',
|
||||
'@fontPath : \'../../../themes/'
|
||||
);
|
||||
|
||||
fs.writeFile(filename, newContent, 'utf8', (err1) => {
|
||||
if (err) return reject(err1);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
gulp.task('semantic-ui', (done) => {
|
||||
// relocate default config
|
||||
fs.writeFile(
|
||||
'node_modules/semantic-ui-less/theme.config',
|
||||
`@import '${SEMANTIC_THEME_PATH}';\n`,
|
||||
'utf8',
|
||||
(err) => {
|
||||
if (err) return done(err);
|
||||
|
||||
fixFontPath('node_modules/semantic-ui-less/themes/default/globals/site.variables')
|
||||
.then(() => done())
|
||||
.catch(done);
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -49,7 +49,7 @@ gulp.task('sprites:checkCompiledDimensions', ['sprites:main', 'sprites:largeSpri
|
||||
});
|
||||
|
||||
if (numberOfSheetsThatAreTooBig > 0) {
|
||||
console.error(`${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.`); // https://github.com/HabitRPG/habitica/pull/6683#issuecomment-185462180
|
||||
console.error(`${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.`); // https://github.com/HabitRPG/habitrpg/pull/6683#issuecomment-185462180
|
||||
} else {
|
||||
console.log('All images are within the correct dimensions');
|
||||
}
|
||||
|
||||
+2
-2
@@ -280,7 +280,7 @@ gulp.task('test:e2e:safe', ['test:prepare', 'test:prepare:server'], (cb) => {
|
||||
|
||||
gulp.task('test:api-v3:unit', (done) => {
|
||||
let runner = exec(
|
||||
testBin('node_modules/.bin/istanbul cover --dir coverage/api-v3-unit --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/unit --recursive --require ./test/helpers/start-server'),
|
||||
testBin('mocha test/api/v3/unit --recursive --require ./test/helpers/start-server'),
|
||||
(err, stdout, stderr) => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
@@ -298,7 +298,7 @@ gulp.task('test:api-v3:unit:watch', () => {
|
||||
|
||||
gulp.task('test:api-v3:integration', (done) => {
|
||||
let runner = exec(
|
||||
testBin('node_modules/.bin/istanbul cover --dir coverage/api-v3-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/integration --recursive --require ./test/helpers/start-server'),
|
||||
testBin('mocha test/api/v3/integration --recursive --require ./test/helpers/start-server'),
|
||||
{maxBuffer: 500 * 1024},
|
||||
(err, stdout, stderr) => {
|
||||
if (err) {
|
||||
|
||||
@@ -67,7 +67,7 @@ gulp.task('transifex:malformedStrings', () => {
|
||||
let stringsWithIncorrectNumberOfInterpolations = [];
|
||||
|
||||
let count = 0;
|
||||
_.each(ALL_LANGUAGES, function (lang) {
|
||||
_(ALL_LANGUAGES).each(function (lang) {
|
||||
|
||||
_.each(stringsToLookFor, function (strings, file) {
|
||||
let translationFile = fs.readFileSync(LOCALES + lang + '/' + file);
|
||||
@@ -89,7 +89,7 @@ gulp.task('transifex:malformedStrings', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}).value();
|
||||
|
||||
if (!_.isEmpty(stringsWithMalformedInterpolations)) {
|
||||
let message = 'The following strings have malformed or missing interpolations';
|
||||
@@ -114,7 +114,7 @@ function getArrayOfLanguages () {
|
||||
function eachTranslationFile (languages, cb) {
|
||||
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
|
||||
|
||||
_.each(languages, (lang) => {
|
||||
_(languages).each((lang) => {
|
||||
_.each(jsonFiles, (filename) => {
|
||||
try {
|
||||
var translationFile = fs.readFileSync(LOCALES + lang + '/' + filename);
|
||||
@@ -128,7 +128,7 @@ function eachTranslationFile (languages, cb) {
|
||||
|
||||
cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile);
|
||||
});
|
||||
});
|
||||
}).value();
|
||||
}
|
||||
|
||||
function eachTranslationString (languages, cb) {
|
||||
@@ -153,7 +153,7 @@ function formatMessageForPosting (msg, items) {
|
||||
function getStringsWith (json, interpolationRegex) {
|
||||
var strings = {};
|
||||
|
||||
_.each(json, function (file_name) {
|
||||
_(json).each(function (file_name) {
|
||||
var raw_file = fs.readFileSync(ENGLISH_LOCALE + file_name);
|
||||
var parsed_json = JSON.parse(raw_file);
|
||||
|
||||
@@ -162,7 +162,7 @@ function getStringsWith (json, interpolationRegex) {
|
||||
var match = value.match(interpolationRegex);
|
||||
if (match) strings[file_name][key] = match;
|
||||
});
|
||||
});
|
||||
}).value();
|
||||
|
||||
return strings;
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,11 +9,11 @@
|
||||
require('babel-register');
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
require('./gulp/gulp-semanticui');
|
||||
require('./gulp/gulp-apidoc');
|
||||
require('./gulp/gulp-newstuff');
|
||||
require('./gulp/gulp-build');
|
||||
require('./gulp/gulp-babelify');
|
||||
require('./gulp/gulp-bootstrap');
|
||||
} else {
|
||||
require('glob').sync('./gulp/gulp-*').forEach(require);
|
||||
require('gulp').task('default', ['test']);
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
# Habitica in Kubernetes
|
||||
This is a set of sample Kubernetes configuration files to launch Habitica under AWS, both as a single-node web frontend as well as a multi-node web frontend.
|
||||
|
||||
## Prerequisites
|
||||
* An AWS account.
|
||||
* A working Kubernetes installation.
|
||||
* A basic understanding of how to use Kubernetes. https://kubernetes.io/
|
||||
* A persistent volume for MongoDB data.
|
||||
* Docker images of Habitica.
|
||||
+ You can use your own, or use the one included in the YAML files.
|
||||
+ If you use your own, you'll need a fork of the Habitica GitHub repo and your own Docker Hub repo, both of which are free.
|
||||
|
||||
## Before you begin
|
||||
1. Set up Kubernetes.
|
||||
2. Create an EBS volume for MongoDB data.
|
||||
+ Make a note of the name, you'll need it later.
|
||||
|
||||
## Starting MongoDB
|
||||
1. Edit mongo.yaml
|
||||
+ Find the volumeID line.
|
||||
+ Change the volume to the one created in the section above.
|
||||
2. Run the following commands:
|
||||
+ `kubectl.sh create -f mongo.yaml`
|
||||
+ `kubectl.sh create -f mongo-service.yaml`
|
||||
3. Wait for the MongoDB pod to start up.
|
||||
|
||||
## Starting a Single Web Frontend
|
||||
|
||||
1. Run the following commands:
|
||||
+ `kubectl.sh create -f habitica.yaml`
|
||||
+ `kubectl.sh create -f habitica-service.yaml`
|
||||
2. Wait for the frontend to start up.
|
||||
|
||||
## Starting Multi-node Web Frontend
|
||||
1. Run the following commands :
|
||||
+ `kubectl.sh create -f habitica-rc.yaml`
|
||||
+ `kubectl.sh create -f habitica-service.yaml`
|
||||
2. Wait for the frontend to start up.
|
||||
|
||||
## Accessing Your Habitica web interface
|
||||
Using `kubectl describe svc habiticaweb` get the hostname generated for the Habitica service. Open a browser and go to http://hostname:3000 to access the web front-end for the installations above.
|
||||
|
||||
## Shutting down
|
||||
Shutting down is basically done by reversing the steps above:
|
||||
+ `kubectl.sh delete -f habitica-service.yaml`
|
||||
+ `kubectl.sh delete -f habitica.yaml (or habitica-rc.yaml)`
|
||||
+ `kubectl.sh delete -f mongo-service.yaml`
|
||||
+ `kubectl.sh delete -f mongo.yaml`
|
||||
|
||||
You can also just shut down all of Kubernetes as well.
|
||||
|
||||
## Notes
|
||||
+ MongoDB data will be persistent! If you need to start with a fresh database, you'll need to remove the volume and re-create it.
|
||||
+ On AWS, you probably want to use at least t2.medium minion nodes for Kubernetes. The default t2.small is too small for more than two Habitica nodes.
|
||||
|
||||
## Future Plans
|
||||
+ Multi-node MongoDB.
|
||||
+ Monitoring
|
||||
+ Instructions for a better hostname. The default generated ones stink.
|
||||
+ More to come....
|
||||
@@ -1,25 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
name: habitica
|
||||
labels:
|
||||
name: habitica
|
||||
spec:
|
||||
replicas: 4
|
||||
selector:
|
||||
name: habitica
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
name: habitica
|
||||
spec:
|
||||
containers:
|
||||
- name: habitica
|
||||
image: ksonney/habitrpg:latest
|
||||
env:
|
||||
- name: NODE_DB_URI
|
||||
value: mongodb://mongosvc/habitrpg
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
hostPort: 3000
|
||||
name: habitica
|
||||
@@ -1,14 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
name: habiticaweb
|
||||
name: habiticaweb
|
||||
spec:
|
||||
ports:
|
||||
# the port that this service should serve on
|
||||
- port: 3000
|
||||
# label keys and values that must match in order to receive traffic for this service
|
||||
selector:
|
||||
name: habitica
|
||||
type: LoadBalancer
|
||||
@@ -1,22 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: habitica
|
||||
labels:
|
||||
name: habitica
|
||||
spec:
|
||||
containers:
|
||||
# - image: mongo:latest
|
||||
# name: mongo
|
||||
# ports:
|
||||
# - containerPort: 27017
|
||||
# name: mongo
|
||||
- image: ksonney/habitrpg:latest
|
||||
name: habitica
|
||||
env:
|
||||
- name: NODE_DB_URI
|
||||
value: mongodb://mongosvc/habitrpg
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
hostPort: 3000
|
||||
name: habitica
|
||||
@@ -1,13 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
name: mongosvc
|
||||
name: mongosvc
|
||||
spec:
|
||||
ports:
|
||||
# the port that this service should serve on
|
||||
- port: 27017
|
||||
# label keys and values that must match in order to receive traffic for this service
|
||||
selector:
|
||||
name: mongodb
|
||||
@@ -1,28 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: mongodb
|
||||
labels:
|
||||
name: mongodb
|
||||
spec:
|
||||
containers:
|
||||
- resources:
|
||||
limits :
|
||||
cpu: 0.5
|
||||
image: mongo
|
||||
name: mongodb
|
||||
ports:
|
||||
- containerPort: 27017
|
||||
hostPort: 27017
|
||||
name: mongo
|
||||
volumeMounts:
|
||||
# # name must match the volume name below
|
||||
- name: mongo-persistent-storage
|
||||
# # mount path within the container
|
||||
mountPath: /data/db
|
||||
volumes:
|
||||
- name: mongo-persistent-storage
|
||||
awsElasticBlockStore:
|
||||
volumeID: aws://YOUR-REGION/YOUR-VOLNAME
|
||||
fsType: ext3
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
// %mongo server:27017/dbname underscore.js my_commands.js
|
||||
// %mongo server:27017/dbname underscore.js --shell
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var habits = 0,
|
||||
dailies = 0,
|
||||
todos = 0,
|
||||
|
||||
@@ -3,12 +3,6 @@
|
||||
*/
|
||||
// mongo habitrpg ./node_modules/underscore/underscore.js ./migrations/20130326_migrate_pets.js
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var mapping = {
|
||||
bearcub: {name:'BearCub', modifier: 'Base'},
|
||||
cactus: {name:'Cactus', modifier:'Base'},
|
||||
|
||||
@@ -4,12 +4,6 @@
|
||||
|
||||
// mongo habitrpg ./node_modules/underscore/underscore.js migrations/20130327_apply_tokens.js
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var mapping = [
|
||||
{
|
||||
tier: 1,
|
||||
|
||||
@@ -6,11 +6,6 @@
|
||||
* mongo habitrpg ./node_modules/underscore/underscore.js ./migrations/20130508_fix_duff_party_subscriptions.js
|
||||
*/
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
// since our primary subscription will first hit parties now, we *definitely* need an index there
|
||||
db.parties.ensureIndex( { 'members': 1}, {background: true} );
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
//mongo habitrpg ./node_modules/lodash/lodash.js migrations/20130602_survey_rewards.js
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var members = []
|
||||
members = _.uniq(members);
|
||||
|
||||
|
||||
@@ -3,12 +3,6 @@
|
||||
// Racer was notorious for adding duplicates, randomly deleting documents, etc. Once we pull the plug on old.habit,
|
||||
// run this migration to cleanup all the corruption
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
db.users.find().forEach(function(user){
|
||||
|
||||
// remove corrupt tasks, which will either be null-value or no id
|
||||
|
||||
@@ -5,12 +5,6 @@
|
||||
// @see http://stackoverflow.com/questions/14867697/mongoose-full-collection-scan
|
||||
//Also, what do we think of a Mongoose Migration module? something like https://github.com/madhums/mongoose-migrate
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
db.users.find().forEach(function(user){
|
||||
|
||||
// Add invites to groups
|
||||
|
||||
@@ -8,12 +8,6 @@
|
||||
var mongo = require('mongoskin');
|
||||
var _ = require('lodash');
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var backupUsers = mongo.db('localhost:27017/habitrpg_old?auto_reconnect').collection('users');
|
||||
var liveUsers = mongo.db('localhost:27017/habitrpg_new?auto_reconnect').collection('users');
|
||||
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
// node .migrations/20131127_restore_dayStart.js
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
var _ = require('lodash');
|
||||
|
||||
|
||||
@@ -8,12 +8,6 @@ mongo = require('mongoskin')
|
||||
_ = require('lodash')
|
||||
async = require('async')
|
||||
|
||||
# IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
# We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
# adapted to work with it. Before this migration is used again any lodash method should
|
||||
# be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
# https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
db = mongo.db('localhost:27017/habitrpg?auto_reconnect')
|
||||
|
||||
###
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
// node .migrations/20131221_restore_NaN_history.js
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
/**
|
||||
* After the classes migration, users lost some history entries
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
// node .migrations/20131225_restore_streaks.js
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
/**
|
||||
* After the classes migration, users lost some history entries
|
||||
*/
|
||||
|
||||
@@ -5,12 +5,6 @@ var migrationName = '20140823_remove_undefined_and_false_notifications';
|
||||
var authorName = 'Alys'; // in case script author needs to know when their ...
|
||||
var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
/**
|
||||
* https://github.com/HabitRPG/habitrpg/pull/3907
|
||||
*/
|
||||
|
||||
@@ -4,12 +4,6 @@ var migrationName = '20140829_change_headAccessory_to_eyewear';
|
||||
var authorName = 'Alys'; // in case script author needs to know when their ...
|
||||
var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
/**
|
||||
* https://github.com/HabitRPG/habitrpg/issues/3645
|
||||
*/
|
||||
|
||||
@@ -4,11 +4,6 @@
|
||||
//
|
||||
// node 20140831_increase_gems_for_previous_contributions.js > 20140831_increase_gems_for_previous_contributions_output.txt
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var migrationName = '20140831_increase_gems_for_previous_contributions';
|
||||
|
||||
|
||||
@@ -8,12 +8,6 @@ var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done
|
||||
* Convert Tier 7 contributors with admin flag to Tier 8 (moderators).
|
||||
*/
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
var _ = require('lodash');
|
||||
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
// require moment, lodash
|
||||
db.users.find(
|
||||
{'purchased.plan.customerId':{$ne:null}},
|
||||
|
||||
@@ -9,12 +9,6 @@ var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done
|
||||
|
||||
var dbserver = 'localhost:27017' // CHANGE THIS FOR PRODUCTION DATABASE
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
var _ = require('lodash');
|
||||
|
||||
|
||||
@@ -8,12 +8,6 @@ var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done
|
||||
|
||||
var dbserver = 'localhost:27017' // CHANGE THIS FOR PRODUCTION DATABASE
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
var _ = require('lodash');
|
||||
|
||||
|
||||
@@ -19,12 +19,6 @@ var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done
|
||||
|
||||
var dbserver = 'localhost:27017' // CHANGE THIS FOR PRODUCTION DATABASE
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
var _ = require('lodash');
|
||||
var moment = require('moment');
|
||||
|
||||
@@ -6,12 +6,6 @@ var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done
|
||||
* force all active players to rest in the inn due to massive server fail
|
||||
*/
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var dbserver = 'localhost:27017' // CHANGE THIS FOR PRODUCTION DATABASE
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
|
||||
@@ -19,12 +19,6 @@ var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done
|
||||
* means minimal new testing.
|
||||
*/
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var dbserver = 'localhost:27017' // FOR TEST DATABASE
|
||||
// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379' // FOR PRODUCTION DATABASE
|
||||
var dbname = 'habitrpg';
|
||||
|
||||
@@ -7,12 +7,6 @@ var migrationName = '20160111_challenges_condense_same_day_history_entries.js';
|
||||
var dbserver = '';
|
||||
var dbname = '';
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
var _ = require('lodash');
|
||||
var moment = require('moment');
|
||||
|
||||
@@ -11,12 +11,6 @@ var dbserver = 'localhost:27017'; // FOR TEST DATABASE
|
||||
// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE
|
||||
var dbname = 'habitrpg';
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
var _ = require('lodash');
|
||||
|
||||
|
||||
@@ -14,12 +14,6 @@ var dbname = 'habitrpg';
|
||||
var mongo = require('mongoskin');
|
||||
var _ = require('lodash');
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var dbUsers = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
|
||||
@@ -2,12 +2,6 @@ var uuid = require('uuid').v4;
|
||||
var mongo = require('mongodb').MongoClient;
|
||||
var _ = require('lodash');
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
var taskIds = require('checklists-no-id.json').map(function (obj) {
|
||||
return obj._id;
|
||||
});
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/****************************************
|
||||
* Author: @Alys
|
||||
*
|
||||
* Reason: Collection quests are being changed
|
||||
* to require fewer items collected:
|
||||
* https://github.com/HabitRPG/habitrpg/pull/7987
|
||||
* This will cause existing quests to end sooner
|
||||
* than the party is expecting.
|
||||
* This script inserts an explanatory `system`
|
||||
* message into the chat for affected parties.
|
||||
***************************************/
|
||||
|
||||
global.Promise = require('bluebird');
|
||||
const uuid = require('uuid');
|
||||
const TaskQueue = require('cwait').TaskQueue;
|
||||
const logger = require('./utils/logger');
|
||||
const Timer = require('./utils/timer');
|
||||
const connectToDb = require('./utils/connect').connectToDb;
|
||||
const closeDb = require('./utils/connect').closeDb;
|
||||
|
||||
const message = '`This party\'s collection quest has been made easier! For details, refer to http://habitica.wikia.com/wiki/User_blog:LadyAlys/Collection_Quests_are_Now_Easier`';
|
||||
|
||||
const timer = new Timer();
|
||||
|
||||
// PROD: Enable prod db
|
||||
// const DB_URI = 'mongodb://username:password@dsXXXXXX-a0.mlab.com:XXXXX,dsXXXXXX-a1.mlab.com:XXXXX/habitica?replicaSet=rs-dsXXXXXX';
|
||||
const DB_URI = 'mongodb://localhost/habitrpg';
|
||||
|
||||
const COLLECTION_QUESTS = [
|
||||
'vice2',
|
||||
'egg',
|
||||
'moonstone1',
|
||||
'goldenknight1',
|
||||
'dilatoryDistress1',
|
||||
];
|
||||
|
||||
let Groups;
|
||||
|
||||
connectToDb(DB_URI).then((db) => {
|
||||
Groups = db.collection('groups');
|
||||
|
||||
return Promise.resolve();
|
||||
})
|
||||
.then(findPartiesWithCollectionQuest)
|
||||
// .then(displayGroups) // for testing only
|
||||
.then(addMessageToGroups)
|
||||
.then(() => {
|
||||
timer.stop();
|
||||
closeDb();
|
||||
}).catch(reportError);
|
||||
|
||||
function reportError (err) {
|
||||
logger.error('Uh oh, an error occurred');
|
||||
closeDb();
|
||||
timer.stop();
|
||||
throw err;
|
||||
}
|
||||
|
||||
function findPartiesWithCollectionQuest () {
|
||||
logger.info('Looking up groups on collection quests...');
|
||||
|
||||
return Groups.find({'quest.key': {$in: COLLECTION_QUESTS}}, ['name','quest']).toArray().then((groups) => {
|
||||
logger.success('Found', groups.length, 'parties on collection quests');
|
||||
|
||||
return Promise.resolve(groups);
|
||||
})
|
||||
}
|
||||
|
||||
function displayGroups (groups) { // for testing only
|
||||
logger.info('Displaying parties...');
|
||||
console.log(groups);
|
||||
return Promise.resolve(groups);
|
||||
}
|
||||
|
||||
function updateGroupById (group) {
|
||||
var newMessage = {
|
||||
'id' : uuid.v4(),
|
||||
'text' : message,
|
||||
'timestamp': Date.now(),
|
||||
'likes': {},
|
||||
'flags': {},
|
||||
'flagCount': 0,
|
||||
'uuid': 'system'
|
||||
};
|
||||
return Groups.findOneAndUpdate({_id: group._id}, {$push:{"chat" :{$each: [newMessage], $position:0}}}, {returnOriginal: false});
|
||||
// Does not set the newMessage flag for all party members because I don't think it's essential and
|
||||
// I don't want to run the extra code (extra database load, extra opportunity for bugs).
|
||||
}
|
||||
|
||||
function addMessageToGroups (groups) {
|
||||
let queue = new TaskQueue(Promise, 300);
|
||||
|
||||
logger.info('About to update', groups.length, 'parties...');
|
||||
|
||||
return Promise.map(groups, queue.wrap(updateGroupById)).then((result) => {
|
||||
let updates = result.filter(res => res.lastErrorObject && res.lastErrorObject.updatedExisting)
|
||||
let failures = result.filter(res => !(res.lastErrorObject && res.lastErrorObject.updatedExisting));
|
||||
|
||||
logger.success(updates.length, 'parties have been notified');
|
||||
|
||||
if (failures.length > 0) {
|
||||
logger.error(failures.length, 'parties could not be notified');
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
var migrationName = '20170418_subscriber_jackalopes.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Award Royal Purple Jackalope pet to all current subscribers
|
||||
*/
|
||||
|
||||
var monk = require('monk');
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
var now = new Date();
|
||||
|
||||
function processUsers(lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'purchased.plan.customerId': {$type: 2},
|
||||
$or: [
|
||||
{'purchased.plan.dateTerminated': null},
|
||||
{'purchased.plan.dateTerminated': {$exists: false}},
|
||||
{'purchased.plan.dateTerminated': {$gt: now}},
|
||||
]
|
||||
};
|
||||
|
||||
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(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var userPromises = users.map(updateUser);
|
||||
var lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(function () {
|
||||
processUsers(lastUser._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
|
||||
var set = {'items.pets.Jackalope-RoyalPurple': 5};
|
||||
|
||||
dbUsers.update({_id: user._id}, {$set:set});
|
||||
|
||||
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;
|
||||
@@ -1,207 +0,0 @@
|
||||
var migrationName = '20170425_missing_incentives';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Award missing Royal Purple Hatching Potion to users with 55+ check-ins
|
||||
* Reduce users with impossible check-in counts to a reasonable number
|
||||
*/
|
||||
|
||||
import monk from 'monk';
|
||||
import common from '../website/common';
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers(lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'loginIncentives': {$gt:99},
|
||||
'migration': {$ne: migrationName},
|
||||
};
|
||||
|
||||
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(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var userPromises = users.map(updateUser);
|
||||
var lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(function () {
|
||||
processUsers(lastUser._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
var language = user.preferences.language || 'en';
|
||||
var set = {'migration': migrationName};
|
||||
var inc = {
|
||||
'items.eggs.BearCub': 0,
|
||||
'items.eggs.Cactus': 0,
|
||||
'items.eggs.Dragon': 0,
|
||||
'items.eggs.FlyingPig': 0,
|
||||
'items.eggs.Fox': 0,
|
||||
'items.eggs.LionCub': 0,
|
||||
'items.eggs.PandaCub': 0,
|
||||
'items.eggs.TigerCub': 0,
|
||||
'items.eggs.Wolf': 0,
|
||||
'items.food.Chocolate': 0,
|
||||
'items.food.CottonCandyBlue': 0,
|
||||
'items.food.CottonCandyPink': 0,
|
||||
'items.food.Fish': 0,
|
||||
'items.food.Honey': 0,
|
||||
'items.food.Meat': 0,
|
||||
'items.food.Milk': 0,
|
||||
'items.food.Potatoe': 0,
|
||||
'items.food.RottenMeat': 0,
|
||||
'items.food.Strawberry': 0,
|
||||
'items.hatchingPotions.Base': 0,
|
||||
'items.hatchingPotions.CottonCandyBlue': 0,
|
||||
'items.hatchingPotions.CottonCandyPink': 0,
|
||||
'items.hatchingPotions.Desert': 0,
|
||||
'items.hatchingPotions.Golden': 0,
|
||||
'items.hatchingPotions.Red': 0,
|
||||
'items.hatchingPotions.RoyalPurple': 0,
|
||||
'items.hatchingPotions.Shade': 0,
|
||||
'items.hatchingPotions.Skeleton': 0,
|
||||
'items.hatchingPotions.White': 0,
|
||||
'items.hatchingPotions.Zombie': 0,
|
||||
};
|
||||
var nextReward;
|
||||
|
||||
if (user.loginIncentives >= 105) {
|
||||
inc['items.hatchingPotions.RoyalPurple'] += 1;
|
||||
nextReward = 110;
|
||||
}
|
||||
if (user.loginIncentives >= 110) {
|
||||
inc['items.eggs.BearCub'] += 1;
|
||||
inc['items.eggs.Cactus'] += 1;
|
||||
inc['items.eggs.Dragon'] += 1;
|
||||
inc['items.eggs.FlyingPig'] += 1;
|
||||
inc['items.eggs.Fox'] += 1;
|
||||
inc['items.eggs.LionCub'] += 1;
|
||||
inc['items.eggs.PandaCub'] += 1;
|
||||
inc['items.eggs.TigerCub'] += 1;
|
||||
inc['items.eggs.Wolf'] += 1;
|
||||
nextReward = 115;
|
||||
}
|
||||
if (user.loginIncentives >= 115) {
|
||||
inc['items.hatchingPotions.RoyalPurple'] += 1;
|
||||
nextReward = 120;
|
||||
}
|
||||
if (user.loginIncentives >= 120) {
|
||||
inc['items.hatchingPotions.Base'] += 1;
|
||||
inc['items.hatchingPotions.CottonCandyBlue'] += 1;
|
||||
inc['items.hatchingPotions.CottonCandyPink'] += 1;
|
||||
inc['items.hatchingPotions.Desert'] += 1;
|
||||
inc['items.hatchingPotions.Golden'] += 1;
|
||||
inc['items.hatchingPotions.Red'] += 1;
|
||||
inc['items.hatchingPotions.Shade'] += 1;
|
||||
inc['items.hatchingPotions.Skeleton'] += 1;
|
||||
inc['items.hatchingPotions.White'] += 1;
|
||||
inc['items.hatchingPotions.Zombie'] += 1;
|
||||
nextReward = 125;
|
||||
}
|
||||
if (user.loginIncentives >= 125) {
|
||||
inc['items.hatchingPotions.RoyalPurple'] += 1;
|
||||
nextReward = 130;
|
||||
}
|
||||
if (user.loginIncentives >= 130) {
|
||||
inc['items.food.Chocolate'] += 3;
|
||||
inc['items.food.CottonCandyBlue'] += 3;
|
||||
inc['items.food.CottonCandyPink'] += 3;
|
||||
inc['items.food.Fish'] += 3;
|
||||
inc['items.food.Honey'] += 3;
|
||||
inc['items.food.Meat'] += 3;
|
||||
inc['items.food.Milk'] += 3;
|
||||
inc['items.food.Potatoe'] += 3;
|
||||
inc['items.food.RottenMeat'] += 3;
|
||||
inc['items.food.Strawberry'] += 3;
|
||||
}
|
||||
if (user.loginIncentives >= 135) {
|
||||
inc['items.hatchingPotions.RoyalPurple'] += 1;
|
||||
nextReward = 140;
|
||||
}
|
||||
if (user.loginIncentives >= 140) {
|
||||
set['items.gear.owned.weapon_special_skeletonKey'] = true;
|
||||
set['items.gear.owned.shield_special_lootBag'] = true;
|
||||
nextReward = 145;
|
||||
}
|
||||
if (user.loginIncentives >= 145) {
|
||||
inc['items.hatchingPotions.RoyalPurple'] += 1;
|
||||
nextReward = 150;
|
||||
}
|
||||
if (user.loginIncentives >= 150) {
|
||||
set['items.gear.owned.head_special_clandestineCowl'] = true;
|
||||
set['items.gear.owned.armor_special_sneakthiefRobes'] = true;
|
||||
nextReward = 155;
|
||||
}
|
||||
if (user.loginIncentives > 155) {
|
||||
set.loginIncentives = 155;
|
||||
nextReward = 160;
|
||||
}
|
||||
|
||||
var push = {
|
||||
'notifications': {
|
||||
'type': 'LOGIN_INCENTIVE',
|
||||
'data': {
|
||||
'nextRewardAt': nextReward,
|
||||
'rewardKey': [
|
||||
'shop_armoire',
|
||||
],
|
||||
'rewardText': common.i18n.t('checkInRewards', language),
|
||||
'reward': [],
|
||||
'message': common.i18n.t('backloggedCheckInRewards', language),
|
||||
},
|
||||
'id': common.uuid(),
|
||||
}
|
||||
};
|
||||
|
||||
dbUsers.update({_id: user._id}, {$set:set, $push:push, $inc:inc});
|
||||
|
||||
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;
|
||||
@@ -1,114 +0,0 @@
|
||||
var migrationName = '20170616_achievements';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Updates to achievements for June 16, 2017 biweekly merge
|
||||
* 1. Multiply various collection quest achievements based on difficulty reduction
|
||||
* 2. Award Joined Challenge achievement to those who should have it already
|
||||
*/
|
||||
|
||||
import monk from 'monk';
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers(lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
$or: [
|
||||
{'achievements.quests.dilatoryDistress1': {$gt:0}},
|
||||
{'achievements.quests.egg': {$gt:0}},
|
||||
{'achievements.quests.goldenknight1': {$gt:0}},
|
||||
{'achievements.quests.moonstone1': {$gt:0}},
|
||||
{'achievements.quests.vice2': {$gt:0}},
|
||||
{'achievements.challenges': {$exists: true, $ne: []}},
|
||||
{'challenges': {$exists: true, $ne: []}},
|
||||
],
|
||||
};
|
||||
|
||||
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):
|
||||
'achievements',
|
||||
'challenges',
|
||||
],
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var userPromises = users.map(updateUser);
|
||||
var lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(function () {
|
||||
processUsers(lastUser._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
var set = {'migration': migrationName};
|
||||
|
||||
if (user.challenges.length > 0 || user.achievements.challenges.length > 0) {
|
||||
set['achievements.joinedChallenge'] = true;
|
||||
}
|
||||
if (user.achievements.quests.dilatoryDistress1) {
|
||||
set['achievements.quests.dilatoryDistress1'] = Math.ceil(user.achievements.quests.dilatoryDistress1 * 1.25);
|
||||
}
|
||||
if (user.achievements.quests.egg) {
|
||||
set['achievements.quests.egg'] = Math.ceil(user.achievements.quests.egg * 2.5);
|
||||
}
|
||||
if (user.achievements.quests.goldenknight1) {
|
||||
set['achievements.quests.goldenknight1'] = user.achievements.quests.goldenknight1 * 5;
|
||||
}
|
||||
if (user.achievements.quests.moonstone1) {
|
||||
set['achievements.quests.moonstone1'] = user.achievements.quests.moonstone1 * 5;
|
||||
}
|
||||
if (user.achievements.quests.vice2) {
|
||||
set['achievements.quests.vice2'] = Math.ceil(user.achievements.quests.vice2 * 1.5);
|
||||
}
|
||||
|
||||
dbUsers.update({_id: user._id}, {$set:set});
|
||||
|
||||
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;
|
||||
@@ -1,90 +0,0 @@
|
||||
var migrationName = '20170711_orcas.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Award Orca pets to owners of Orca mount, and Orca mount to everyone else
|
||||
*/
|
||||
|
||||
var monk = require('monk');
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers(lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
fields: [
|
||||
'items.mounts',
|
||||
] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var userPromises = users.map(updateUser);
|
||||
var lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(function () {
|
||||
processUsers(lastUser._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
|
||||
var set = {};
|
||||
|
||||
if (user.items.mounts['Orca-Base']) {
|
||||
set = {'migration':migrationName, 'items.pets.Orca-Base': 5};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.mounts.Orca-Base': true};
|
||||
}
|
||||
|
||||
dbUsers.update({_id: user._id}, {$set:set});
|
||||
|
||||
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;
|
||||
@@ -1,109 +0,0 @@
|
||||
var migrationName = '20170731_naming_day.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Award Royal Purple Gryphon Helm to Royal Purple Gryphon pet owners,
|
||||
* award Royal Purple Gryphon pet to Royal Purple Gryphon mount owners,
|
||||
* award Royal Purple Gryphon mount to everyone else
|
||||
*/
|
||||
|
||||
var monk = require('monk');
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers(lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
'auth.timestamps.loggedin': {$gt: new Date('2017-01-01')},
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
fields: [
|
||||
'items.mounts',
|
||||
'items.pets',
|
||||
] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var userPromises = users.map(updateUser);
|
||||
var lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(function () {
|
||||
processUsers(lastUser._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
|
||||
var set = {};
|
||||
var inc = {
|
||||
'achievements.habiticaDays': 1,
|
||||
'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
|
||||
};
|
||||
|
||||
if (user.items.pets['Gryphon-RoyalPurple']) {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_namingDay2017': false};
|
||||
} else if (user.items.mounts['Gryphon-RoyalPurple']) {
|
||||
set = {'migration':migrationName, 'items.pets.Gryphon-RoyalPurple': 5};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.mounts.Gryphon-RoyalPurple': true};
|
||||
}
|
||||
|
||||
dbUsers.update({_id: user._id}, {$set: set, $inc: inc});
|
||||
|
||||
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;
|
||||
@@ -1,97 +0,0 @@
|
||||
var migrationName = '20170928_redesign_guilds.js';
|
||||
|
||||
/*
|
||||
* Copy Guild Leader messages to end of Guild descriptions
|
||||
* Copy Guild logos to beginning of Guild descriptions
|
||||
*/
|
||||
|
||||
var monk = require('monk');
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbGroups = monk(connectionString).get('groups', { castIds: false });
|
||||
|
||||
function processGroups(lastId) {
|
||||
// specify a query to limit the affected groups (empty for all groups):
|
||||
var query = {
|
||||
};
|
||||
|
||||
var fields = {
|
||||
'description': 1,
|
||||
'logo': 1,
|
||||
'leaderMessage': 1,
|
||||
}
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
return dbGroups.find(query, {
|
||||
fields: fields,
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
})
|
||||
.then(updateGroups)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
|
||||
function updateGroups (groups) {
|
||||
if (!groups || groups.length === 0) {
|
||||
console.warn('All appropriate groups found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var groupPromises = groups.map(updateGroup);
|
||||
var lastGroup = groups[groups.length - 1];
|
||||
|
||||
return Promise.all(groupPromises)
|
||||
.then(function () {
|
||||
processGroups(lastGroup._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateGroup (group) {
|
||||
count++;
|
||||
|
||||
var description = group.description;
|
||||
|
||||
if (group.logo) {
|
||||
description = '\n\n \n\n' + description;
|
||||
}
|
||||
|
||||
if (group.leaderMessage) {
|
||||
description = description + '\n\n \n\n' + group.leaderMessage;
|
||||
}
|
||||
|
||||
var set = {
|
||||
description: description,
|
||||
};
|
||||
|
||||
if (count % progressCount == 0) console.warn(count + ' ' + group._id);
|
||||
|
||||
return dbGroups.update({_id: group._id}, {$set:set});
|
||||
}
|
||||
|
||||
function displayData() {
|
||||
console.warn('\n' + count + ' groups 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 = processGroups;
|
||||
@@ -1,127 +0,0 @@
|
||||
var updateStore = require('../website/common/script/libs/updateStore');
|
||||
var getItemInfo = require('../website/common/script/libs/getItemInfo');
|
||||
|
||||
var migrationName = '20170928_redesign_launch.js';
|
||||
var authorName = 'paglias'; // in case script author needs to know when their ...
|
||||
var authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; //... own data is done
|
||||
|
||||
/*
|
||||
* Migrate existing in app rewards lists to pinned items
|
||||
* Award Veteran Pets
|
||||
*/
|
||||
|
||||
var monk = require('monk');
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers(lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration': {$ne:migrationName},
|
||||
'auth.timestamps.loggedin': {$gt: new Date('2017-09-21')},
|
||||
};
|
||||
|
||||
var fields = {
|
||||
'items.pets': 1,
|
||||
'items.gear': 1,
|
||||
'stats.class': 1,
|
||||
}
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
return dbUsers.find(query, {
|
||||
fields: fields,
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var userPromises = users.map(updateUser);
|
||||
var lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(function () {
|
||||
processUsers(lastUser._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
|
||||
var set = {'migration': migrationName};
|
||||
|
||||
var oldRewardsList = updateStore(user);
|
||||
var newPinnedItems = [
|
||||
{
|
||||
type: 'armoire',
|
||||
path: 'armoire',
|
||||
},
|
||||
{
|
||||
type: 'potion',
|
||||
path: 'potion',
|
||||
},
|
||||
];
|
||||
|
||||
oldRewardsList.forEach(item => {
|
||||
var type = 'marketGear';
|
||||
|
||||
var itemInfo = getItemInfo(user, 'marketGear', item);
|
||||
newPinnedItems.push({
|
||||
type,
|
||||
path: itemInfo.path,
|
||||
})
|
||||
});
|
||||
|
||||
set.pinnedItems = newPinnedItems;
|
||||
|
||||
if (user.items.pets['Lion-Veteran']) {
|
||||
set['items.pets.Bear-Veteran'] = 5;
|
||||
} else if (user.items.pets['Tiger-Veteran']) {
|
||||
set['items.pets.Lion-Veteran'] = 5;
|
||||
} else if (user.items.pets['Wolf-Veteran']) {
|
||||
set['items.pets.Tiger-Veteran'] = 5;
|
||||
} else {
|
||||
set['items.pets.Wolf-Veteran'] = 5;
|
||||
}
|
||||
|
||||
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||
|
||||
return dbUsers.update({_id: user._id}, {$set:set});
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -6,12 +6,6 @@
|
||||
|
||||
// Due to some big user profiles it needs more RAM than is allowed by default by v8 (arounf 1.7GB).
|
||||
// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
console.log('Starting migrations/api_v3/challenges.js.');
|
||||
|
||||
require('babel-register');
|
||||
|
||||
@@ -9,12 +9,6 @@
|
||||
// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM
|
||||
console.log('Starting migrations/api_v3/challengesMembers.js.');
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
require('babel-register');
|
||||
require('babel-polyfill');
|
||||
|
||||
|
||||
@@ -8,12 +8,6 @@
|
||||
// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM
|
||||
console.log('Starting migrations/api_v3/coupons.js.');
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
require('babel-register');
|
||||
require('babel-polyfill');
|
||||
|
||||
|
||||
@@ -8,12 +8,6 @@
|
||||
// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM
|
||||
console.log('Starting migrations/api_v3/unsubscriptions.js.');
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
require('babel-register');
|
||||
require('babel-polyfill');
|
||||
|
||||
|
||||
@@ -16,12 +16,6 @@
|
||||
// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM
|
||||
console.log('Starting migrations/api_v3/groups.js.');
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
require('babel-register');
|
||||
require('babel-polyfill');
|
||||
|
||||
|
||||
@@ -9,12 +9,6 @@
|
||||
// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM
|
||||
console.log('Starting migrations/api_v3/users.js.');
|
||||
|
||||
// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash.
|
||||
// We've now upgraded to lodash v4 but the code used in this migration has not been
|
||||
// adapted to work with it. Before this migration is used again any lodash method should
|
||||
// be checked for compatibility against the v4 changelog and changed if necessary.
|
||||
// https://github.com/lodash/lodash/wiki/Changelog#v400
|
||||
|
||||
require('babel-register');
|
||||
require('babel-polyfill');
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import Bluebird from 'Bluebird';
|
||||
|
||||
import { model as Challenges } from '../../website/server/models/challenge';
|
||||
import { model as User } from '../../website/server/models/user';
|
||||
|
||||
async function syncChallengeToMembers (challenges) {
|
||||
let challengSyncPromises = challenges.map(async function (challenge) {
|
||||
let users = await User.find({
|
||||
// _id: '',
|
||||
challenges: challenge._id,
|
||||
}).exec();
|
||||
|
||||
let promises = [];
|
||||
users.forEach(function (user) {
|
||||
promises.push(challenge.syncToUser(user));
|
||||
promises.push(challenge.save());
|
||||
promises.push(user.save());
|
||||
});
|
||||
|
||||
return Bluebird.all(promises);
|
||||
});
|
||||
|
||||
return await Bluebird.all(challengSyncPromises);
|
||||
}
|
||||
|
||||
async function syncChallenges (lastChallengeDate) {
|
||||
let query = {
|
||||
// _id: '',
|
||||
};
|
||||
|
||||
if (lastChallengeDate) {
|
||||
query.createdOn = { $lte: lastChallengeDate };
|
||||
}
|
||||
|
||||
let challengesFound = await Challenges.find(query)
|
||||
.limit(10)
|
||||
.sort('-createdAt')
|
||||
.exec();
|
||||
|
||||
let syncedChallenges = await syncChallengeToMembers(challengesFound)
|
||||
.catch(reason => console.error(reason));
|
||||
let lastChallenge = challengesFound[challengesFound.length - 1];
|
||||
if (lastChallenge) syncChallenges(lastChallenge.createdAt);
|
||||
return syncedChallenges;
|
||||
};
|
||||
|
||||
module.exports = syncChallenges;
|
||||
@@ -7,6 +7,6 @@
|
||||
|
||||
db.users.find().forEach(function(user){
|
||||
user.tasks = user.habits.concat(user.dailys).concat(user.todos).concat(user.rewards);
|
||||
var found = _.some(user.tasks, {text: ""})
|
||||
var found = _.any(user.tasks, {text: ""})
|
||||
if (found) printjson({id:user._id, auth:user.auth});
|
||||
})
|
||||
@@ -1,40 +0,0 @@
|
||||
var migrationName = 'AddUnlimitedSubscription';
|
||||
var authorName = 'TheHollidayInn'; // in case script author needs to know when their ...
|
||||
var authorUuid = ''; //... own data is done
|
||||
|
||||
/*
|
||||
* This migrations will add a free subscription to a specified group
|
||||
*/
|
||||
import moment from 'moment';
|
||||
|
||||
import { model as Group } from '../../website/server/models/group';
|
||||
|
||||
// @TODO: this should probably be a GroupManager library method
|
||||
async function addUnlimitedSubscription (groupId, dateTerminated) {
|
||||
let group = await Group.findById(groupId);
|
||||
|
||||
group.purchased.plan.customerId = "group-unlimited";
|
||||
group.purchased.plan.dateCreated = new Date();
|
||||
group.purchased.plan.dateUpdated = new Date();
|
||||
group.purchased.plan.paymentMethod = "Group Unlimited";
|
||||
group.purchased.plan.planId = "group_monthly";
|
||||
group.purchased.plan.dateTerminated = null;
|
||||
if (dateTerminated) {
|
||||
let dateToEnd = moment(dateTerminated).toDate();
|
||||
group.purchased.plan.dateTerminated = dateToEnd;
|
||||
}
|
||||
// group.purchased.plan.owner = ObjectId();
|
||||
group.purchased.plan.subscriptionId = "";
|
||||
|
||||
return group.save();
|
||||
};
|
||||
|
||||
module.exports = async function addUnlimitedSubscriptionCreator () {
|
||||
let groupId = process.argv[2];
|
||||
|
||||
if (!groupId) throw Error('Group ID is required');
|
||||
|
||||
let dateTerminated = process.argv[3];
|
||||
|
||||
let result = await addUnlimitedSubscription(groupId, dateTerminated);
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
import Bluebird from 'bluebird';
|
||||
|
||||
import { model as Group } from '../../website/server/models/group';
|
||||
import { model as User } from '../../website/server/models/user';
|
||||
|
||||
// @TODO: this should probably be a GroupManager library method
|
||||
async function createGroup (name, privacy, type, leaderId) {
|
||||
let user = await User.findById(leaderId);
|
||||
|
||||
let group = new Group({
|
||||
name,
|
||||
privacy,
|
||||
type,
|
||||
});
|
||||
|
||||
group.leader = user._id;
|
||||
user.guilds.push(group._id);
|
||||
|
||||
return Bluebird.all([group.save(), user.save()]);
|
||||
};
|
||||
|
||||
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];
|
||||
|
||||
let result = await createGroup(name, privacy, type, leaderId)
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
var migrationName = 'Jackalopes for Unlimited Subscribers';
|
||||
|
||||
/*
|
||||
* This migration will find users with unlimited subscriptions who are also eligible for Jackalope mounts, and award them
|
||||
*/
|
||||
import Bluebird from 'bluebird';
|
||||
|
||||
import { model as Group } from '../../website/server/models/group';
|
||||
import { model as User } from '../../website/server/models/user';
|
||||
import * as payments from '../../website/server/libs/payments';
|
||||
|
||||
async function handOutJackalopes () {
|
||||
let promises = [];
|
||||
let cursor = User.find({
|
||||
'purchased.plan.customerId':'habitrpg',
|
||||
}).cursor();
|
||||
|
||||
cursor.on('data', async function(user) {
|
||||
console.log('User: ' + user._id);
|
||||
|
||||
let groupList = [];
|
||||
if (user.party._id) groupList.push(user.party._id);
|
||||
groupList = groupList.concat(user.guilds);
|
||||
|
||||
let subscribedGroup =
|
||||
await Group.findOne({
|
||||
'_id': {$in: groupList},
|
||||
'purchased.plan.planId': 'group_monthly',
|
||||
'purchased.plan.dateTerminated': null,
|
||||
},
|
||||
{'_id':1}
|
||||
);
|
||||
|
||||
if (subscribedGroup) {
|
||||
User.update({'_id':user._id},{$set:{'items.mounts.Jackalope-RoyalPurple':true}}).exec();
|
||||
promises.push(user.save());
|
||||
}
|
||||
});
|
||||
|
||||
cursor.on('close', async function() {
|
||||
console.log('done');
|
||||
return await Bluebird.all(promises);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = handOutJackalopes;
|
||||
@@ -1,33 +0,0 @@
|
||||
var migrationName = 'ResyncGroupPlanMembers';
|
||||
var authorName = 'TheHollidayInn'; // in case script author needs to know when their ...
|
||||
var 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
|
||||
*/
|
||||
|
||||
import Bluebird from 'bluebird';
|
||||
|
||||
import { model as Group } from '../../website/server/models/group';
|
||||
import * as payments from '../../website/server/libs/payments';
|
||||
|
||||
async function updateGroupsWithGroupPlans () {
|
||||
let cursor = Group.find({
|
||||
'purchased.plan.planId': 'group_monthly',
|
||||
'purchased.plan.dateTerminated': null,
|
||||
}).cursor();
|
||||
|
||||
let promises = [];
|
||||
|
||||
cursor.on('data', function(group) {
|
||||
promises.push(payments.addSubscriptionToGroupUsers(group));
|
||||
promises.push(group.save())
|
||||
});
|
||||
|
||||
cursor.on('close', async function() {
|
||||
return await Bluebird.all(promises);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = updateGroupsWithGroupPlans;
|
||||
@@ -1,9 +1,6 @@
|
||||
// EMAIL="x@y.com" node ./migrations/manual_password_reset.js
|
||||
// Be sure to have PRODUCTION_DB in your config.json
|
||||
|
||||
// IMPORTANT: this script isn't updated to use the new password encryption that uses bcrypt
|
||||
// using it will break accounts and should not be used until upgraded
|
||||
|
||||
var nconf = require('nconf'),
|
||||
path = require('path');
|
||||
nconf.argv().env().file('user', path.join(path.resolve(__dirname, '../config.json')));
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
require("babel-register");
|
||||
require("babel-polyfill");
|
||||
|
||||
// This file must use ES5, everything required can be in ES6
|
||||
|
||||
function setUpServer () {
|
||||
var nconf = require('nconf');
|
||||
var mongoose = require('mongoose');
|
||||
var Bluebird = require('bluebird');
|
||||
var setupNconf = require('../website/server/libs/setupNconf');
|
||||
setupNconf();
|
||||
// We require src/server and npt src/index because
|
||||
// 1. nconf is already setup
|
||||
// 2. we don't need clustering
|
||||
require('../website/server/server'); // eslint-disable-line global-require
|
||||
}
|
||||
setUpServer();
|
||||
|
||||
// Replace this with your migration
|
||||
var processUsers = require('./groups/update-groups-with-group-plans');
|
||||
processUsers()
|
||||
.catch(function (err) {
|
||||
console.log(err)
|
||||
})
|
||||
require("babel-register");
|
||||
require("babel-polyfill");
|
||||
|
||||
// This file must use ES5, everything required can be in ES6
|
||||
|
||||
function setUpServer () {
|
||||
var nconf = require('nconf');
|
||||
var mongoose = require('mongoose');
|
||||
var Bluebird = require('bluebird');
|
||||
var setupNconf = require('../website/server/libs/setupNconf');
|
||||
setupNconf();
|
||||
// We require src/server and npt src/index because
|
||||
// 1. nconf is already setup
|
||||
// 2. we don't need clustering
|
||||
require('../website/server/server'); // eslint-disable-line global-require
|
||||
}
|
||||
setUpServer();
|
||||
|
||||
// Replace this with your migration
|
||||
var processUsers = require('./new_stuff');
|
||||
processUsers();
|
||||
|
||||
@@ -2,7 +2,7 @@ var _id = '';
|
||||
var update = {
|
||||
$addToSet: {
|
||||
'purchased.plan.mysteryItems':{
|
||||
$each:['shield_mystery_201709','back_mystery_201709']
|
||||
$each:['head_mystery_201702','back_mystery_201702']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
let Bluebird = require('bluebird');
|
||||
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({
|
||||
accessKeyId: config.S3.accessKeyId,
|
||||
secretAccessKey: config.S3.secretAccessKey,
|
||||
// region: config.get('S3_REGION'),
|
||||
});
|
||||
|
||||
let BUCKET_NAME = config.S3.bucket;
|
||||
let s3 = new AWS.S3();
|
||||
|
||||
// Adapted from http://stackoverflow.com/a/22210077/2601552
|
||||
function uploadFile (buffer, fileName) {
|
||||
return new Promise((resolve, reject) => {
|
||||
s3.putObject({
|
||||
Body: buffer,
|
||||
Key: fileName,
|
||||
Bucket: BUCKET_NAME,
|
||||
}, (error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
// console.info(`${fileName} uploaded to ${BUCKET_NAME} succesfully.`);
|
||||
resolve(fileName);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getFileName (file) {
|
||||
let piecesOfPath = file.split('/');
|
||||
let name = last(piecesOfPath);
|
||||
let fullName = S3_DIRECTORY + name;
|
||||
|
||||
return fullName;
|
||||
}
|
||||
|
||||
function getFileFromUrl (url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
request.get(url).end((err, res) => {
|
||||
if (err) return reject(err);
|
||||
let file = res.body;
|
||||
resolve(file);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let commit = '78f94e365c72cc58f66857d5941105638db7d35c';
|
||||
commit = 'df0dbaba636c9ce424cc7040f7bd7fc1aa311015';
|
||||
let gihuburl = `https://api.github.com/repos/HabitRPG/habitica/commits/${commit}`
|
||||
|
||||
|
||||
let currentIndex = 0;
|
||||
|
||||
function uploadToS3(start, end, filesUrls) {
|
||||
let urls = filesUrls.slice(start, end);
|
||||
|
||||
if (urls.length === 0) {
|
||||
console.log("done");
|
||||
return;
|
||||
}
|
||||
|
||||
let promises = urls.map(fullUrl => {
|
||||
return getFileFromUrl(fullUrl)
|
||||
.then((buffer) => {
|
||||
return uploadFile(buffer, getFileName(fullUrl));
|
||||
});
|
||||
});
|
||||
console.log(promises.length)
|
||||
|
||||
return Bluebird.all(promises)
|
||||
.then(() => {
|
||||
currentIndex += 50;
|
||||
uploadToS3(currentIndex, currentIndex + 50, filesUrls);
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
|
||||
request.get(gihuburl)
|
||||
.end((err, res) => {
|
||||
console.log(err);
|
||||
let files = res.body.files;
|
||||
|
||||
let filesUrls = [''];
|
||||
filesUrls = files.map(file => {
|
||||
return file.raw_url;
|
||||
})
|
||||
|
||||
uploadToS3(currentIndex, currentIndex + 50, filesUrls);
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
var migrationName = '20170502_takeThis.js'; // Update per month
|
||||
var migrationName = '20170201_takeThis.js'; // Update per month
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
@@ -14,7 +14,7 @@ function processUsers(lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
'challenges':{$in:['69999331-d4ea-45a0-8c3f-f725d22b56c8']} // Update per month
|
||||
'challenges':{$in:['b1d436b5-c784-42e3-9b07-7072479a6f8e']} // Update per month
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
var migrationName = 'tasks-set-yesterdaily';
|
||||
var authorName = 'TheHollidayInn'; // in case script author needs to know when their ...
|
||||
var authorUuid = ''; //... own data is done
|
||||
|
||||
/*
|
||||
* Iterates over all tasks and sets the yseterDaily field to True
|
||||
*/
|
||||
|
||||
import monk from 'monk';
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbTasks = monk(connectionString).get('tasks', { castIds: false });
|
||||
|
||||
function processTasks(lastId) {
|
||||
// specify a query to limit the affected tasks (empty for all tasks):
|
||||
var query = {
|
||||
yesterDaily: false,
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
dbTasks.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(updateTasks)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
|
||||
function updateTasks (tasks) {
|
||||
if (!tasks || tasks.length === 0) {
|
||||
console.warn('All appropriate tasks found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var taskPromises = tasks.map(updatetask);
|
||||
var lasttask = tasks[tasks.length - 1];
|
||||
|
||||
return Promise.all(taskPromises)
|
||||
.then(function () {
|
||||
processtasks(lasttask._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updatetask (task) {
|
||||
count++;
|
||||
var set = {'yesterDaily': true};
|
||||
|
||||
dbTasks.update({_id: task._id}, {$set:set});
|
||||
|
||||
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');
|
||||
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 = processtasks;
|
||||
@@ -1,109 +0,0 @@
|
||||
var migrationName = 'UserFromProdToTest';
|
||||
var authorName = 'TheHollidayInn'; // in case script author needs to know when their ...
|
||||
var authorUuid = ''; //... own data is done
|
||||
|
||||
/*
|
||||
* This migraition will copy user data from prod to test
|
||||
*/
|
||||
|
||||
var monk = require('monk');
|
||||
var testConnectionSting = ''; // FOR TEST DATABASE
|
||||
var usersTest = monk(testConnectionSting).get('users', { castIds: false });
|
||||
var groupsTest = monk(testConnectionSting).get('groups', { castIds: false });
|
||||
var challengesTest = monk(testConnectionSting).get('challenges', { castIds: false });
|
||||
var tasksTest = monk(testConnectionSting).get('tasks', { castIds: false });
|
||||
|
||||
var monk2 = require('monk');
|
||||
var liveConnectString = ''; // FOR TEST DATABASE
|
||||
var userLive = monk2(liveConnectString).get('users', { castIds: false });
|
||||
var groupsLive = monk2(liveConnectString).get('groups', { castIds: false });
|
||||
var challengesLive = monk2(liveConnectString).get('challenges', { castIds: false });
|
||||
var tasksLive = monk2(liveConnectString).get('tasks', { castIds: false });
|
||||
|
||||
import uniq from 'lodash/uniq';
|
||||
import Bluebird from 'bluebird';
|
||||
|
||||
// Variabls for updating
|
||||
let userIds = [
|
||||
'206039c6-24e4-4b9f-8a31-61cbb9aa3f66',
|
||||
];
|
||||
|
||||
let groupIds = [];
|
||||
let challengeIds = [];
|
||||
let tasksIds = [];
|
||||
|
||||
async function processUsers () {
|
||||
let userPromises = [];
|
||||
//{_id: {$in: userIds}}
|
||||
|
||||
return userLive.find({guilds: 'b0764d64-8276-45a1-afa5-5ca9a5c64ca0'})
|
||||
.each((user, {close, pause, resume}) => {
|
||||
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);
|
||||
if (user.tasksOrder.rewards.length > 0) tasksIds = tasksIds.concat(user.tasksOrder.rewards);
|
||||
if (user.tasksOrder.todos.length > 0) tasksIds = tasksIds.concat(user.tasksOrder.todos);
|
||||
if (user.tasksOrder.dailys.length > 0) tasksIds = tasksIds.concat(user.tasksOrder.dailys);
|
||||
if (user.tasksOrder.habits.length > 0) tasksIds = tasksIds.concat(user.tasksOrder.habits);
|
||||
|
||||
let userPromise = usersTest.update({'_id': user._id}, user, {upsert:true});
|
||||
userPromises.push(userPromise);
|
||||
}).then(() => {
|
||||
return Bluebird.all(userPromises);
|
||||
})
|
||||
.then(() => {
|
||||
console.log("Done User");
|
||||
});
|
||||
}
|
||||
|
||||
function processGroups () {
|
||||
let promises = [];
|
||||
let groupsToQuery = uniq(groupIds);
|
||||
return groupsLive.find({_id: {$in: groupsToQuery}})
|
||||
.each((group, {close, pause, resume}) => {
|
||||
let promise = groupsTest.update({_id: group._id}, group, {upsert:true});
|
||||
promises.push(promise);
|
||||
}).then(() => {
|
||||
return Bluebird.all(promises);
|
||||
})
|
||||
.then(() => {
|
||||
console.log("Done Group");
|
||||
});
|
||||
}
|
||||
|
||||
function processChallenges () {
|
||||
let promises = [];
|
||||
let challengesToQuery = uniq(challengeIds);
|
||||
return challengesLive.find({_id: {$in: challengesToQuery}})
|
||||
.each((challenge, {close, pause, resume}) => {
|
||||
let promise = challengesTest.update({_id: challenge._id}, challenge, {upsert:true});
|
||||
promises.push(promise);
|
||||
}).then(() => {
|
||||
return Bluebird.all(promises);
|
||||
})
|
||||
.then(() => {
|
||||
console.log("Done Challenge");
|
||||
});
|
||||
}
|
||||
|
||||
function processTasks () {
|
||||
let promises = [];
|
||||
let tasksToQuery = uniq(tasksIds);
|
||||
return tasksLive.find({_id: {$in: tasksToQuery}})
|
||||
.each((task, {close, pause, resume}) => {
|
||||
let promise = tasksTest.update({_id: task._id}, task, {upsert:true});
|
||||
promises.push(promise);
|
||||
}).then(() => {
|
||||
return Bluebird.all(promises);
|
||||
})
|
||||
.then(() => {
|
||||
console.log("Done Tasks");
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = async function prodToTest () {
|
||||
await processUsers();
|
||||
await processGroups();
|
||||
await processChallenges();
|
||||
await processTasks();
|
||||
};
|
||||
Generated
+13195
File diff suppressed because it is too large
Load Diff
Generated
-21115
File diff suppressed because it is too large
Load Diff
+30
-46
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "4.0.2",
|
||||
"version": "3.78.0",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@slack/client": "^3.8.1",
|
||||
@@ -13,12 +13,10 @@
|
||||
"async": "^1.5.0",
|
||||
"autoprefixer": "^6.4.0",
|
||||
"aws-sdk": "^2.0.25",
|
||||
"axios": "^0.16.0",
|
||||
"axios": "^0.15.3",
|
||||
"babel-core": "^6.0.0",
|
||||
"babel-eslint": "^7.2.3",
|
||||
"babel-loader": "^6.0.0",
|
||||
"babel-plugin-syntax-async-functions": "^6.13.0",
|
||||
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
||||
"babel-plugin-transform-async-to-module-method": "^6.8.0",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.16.0",
|
||||
"babel-plugin-transform-regenerator": "^6.16.1",
|
||||
@@ -30,22 +28,18 @@
|
||||
"bcrypt": "^1.0.2",
|
||||
"bluebird": "^3.3.5",
|
||||
"body-parser": "^1.15.0",
|
||||
"bootstrap": "4.0.0-alpha.6",
|
||||
"bootstrap-vue": "^1.0.0-beta.6",
|
||||
"bower": "~1.3.12",
|
||||
"browserify": "~12.0.1",
|
||||
"compression": "^1.6.1",
|
||||
"connect-ratelimit": "0.0.7",
|
||||
"cookie-session": "^1.2.0",
|
||||
"coupon-code": "^0.4.5",
|
||||
"cross-env": "^4.0.0",
|
||||
"css-loader": "^0.28.0",
|
||||
"css-loader": "^0.26.1",
|
||||
"csv-stringify": "^1.0.2",
|
||||
"cwait": "~1.0.1",
|
||||
"cwait": "^1.0.0",
|
||||
"domain-middleware": "~0.1.0",
|
||||
"estraverse": "^4.1.1",
|
||||
"express": "~4.14.0",
|
||||
"express-basic-auth": "^1.0.1",
|
||||
"express-csv": "~0.6.0",
|
||||
"express-validator": "^2.18.0",
|
||||
"extract-text-webpack-plugin": "^2.0.0-rc.3",
|
||||
@@ -60,7 +54,7 @@
|
||||
"grunt-contrib-stylus": "~0.20.0",
|
||||
"grunt-contrib-uglify": "~0.6.0",
|
||||
"grunt-contrib-watch": "~0.6.1",
|
||||
"grunt-hashres": "git://github.com/habitrpg/grunt-hashres#dc85db6d3002e29e1b7c5ee186b80d708d2f0e0b",
|
||||
"grunt-hashres": "~0.4.1",
|
||||
"gulp": "^3.9.0",
|
||||
"gulp-babel": "^6.1.2",
|
||||
"gulp-grunt": "^0.5.2",
|
||||
@@ -70,26 +64,26 @@
|
||||
"gulp-uglify": "^1.4.2",
|
||||
"gulp.spritesmith": "^4.1.0",
|
||||
"habitica-markdown": "^1.3.0",
|
||||
"hellojs": "^1.15.1",
|
||||
"html-webpack-plugin": "^2.8.1",
|
||||
"image-size": "~0.3.2",
|
||||
"in-app-purchase": "^1.1.6",
|
||||
"intro.js": "^2.6.0",
|
||||
"jade": "~1.11.0",
|
||||
"jquery": ">=3.0.0",
|
||||
"jquery": "^3.1.1",
|
||||
"js2xmlparser": "~1.0.0",
|
||||
"lodash": "^4.17.4",
|
||||
"less": "^2.7.1",
|
||||
"less-loader": "^2.2.3",
|
||||
"lodash": "^3.10.1",
|
||||
"lodash.pickby": "^4.2.0",
|
||||
"lodash.setwith": "^4.2.0",
|
||||
"merge-stream": "^1.0.0",
|
||||
"method-override": "^2.3.5",
|
||||
"moment": "^2.13.0",
|
||||
"moment-recur": "git://github.com/habitrpg/moment-recur#f147ef27bbc26ca67638385f3db4a44084c76626",
|
||||
"mongoose": "~4.8.6",
|
||||
"mongoose": "^4.7.1",
|
||||
"mongoose-id-autoinc": "~2013.7.14-4",
|
||||
"morgan": "^1.7.0",
|
||||
"nconf": "~0.8.2",
|
||||
"nib": "^1.1.0",
|
||||
"node-gcm": "^0.14.4",
|
||||
"node-sass": "^4.5.0",
|
||||
"nodemailer": "^2.3.2",
|
||||
"object-path": "^0.9.2",
|
||||
"ora": "^1.1.0",
|
||||
@@ -99,26 +93,21 @@
|
||||
"passport-google-oauth20": "1.0.0",
|
||||
"paypal-ipn": "3.0.0",
|
||||
"paypal-rest-sdk": "^1.2.1",
|
||||
"popper.js": "^1.11.0",
|
||||
"postcss-easy-import": "^2.0.0",
|
||||
"pretty-data": "^0.40.0",
|
||||
"ps-tree": "^1.0.0",
|
||||
"pug": "^2.0.0-beta.12",
|
||||
"push-notify": "git://github.com/habitrpg/push-notify#6bc2b5fdb1bdc9649b9ec1964d79ca50187fc8a9",
|
||||
"pug": "^2.0.0-beta11",
|
||||
"push-notify": "habitrpg/push-notify#v1.2.0",
|
||||
"pusher": "^1.3.0",
|
||||
"request": "~2.74.0",
|
||||
"rimraf": "^2.4.3",
|
||||
"run-sequence": "^1.1.4",
|
||||
"s3-upload-stream": "^1.0.6",
|
||||
"sass-loader": "^6.0.2",
|
||||
"semantic-ui-less": "~2.2.4",
|
||||
"serve-favicon": "^2.3.0",
|
||||
"shelljs": "^0.7.6",
|
||||
"sortablejs": "^1.6.1",
|
||||
"stripe": "^4.2.0",
|
||||
"superagent": "^3.4.3",
|
||||
"svg-inline-loader": "^0.7.1",
|
||||
"svg-url-loader": "^2.0.2",
|
||||
"svgo-loader": "^1.2.1",
|
||||
"universal-analytics": "~0.3.2",
|
||||
"url-loader": "^0.5.7",
|
||||
"useragent": "^2.1.9",
|
||||
@@ -128,33 +117,30 @@
|
||||
"vinyl-source-stream": "^1.1.0",
|
||||
"vue": "^2.1.0",
|
||||
"vue-loader": "^11.0.0",
|
||||
"vue-mugen-scroll": "^0.2.1",
|
||||
"vue-router": "^2.0.0-rc.5",
|
||||
"vue-style-loader": "^3.0.0",
|
||||
"vue-style-loader": "^2.0.0",
|
||||
"vue-template-compiler": "^2.1.10",
|
||||
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker#45e607a7bccf4e3e089761b3b7b33e3f2c5dc21f",
|
||||
"webpack": "^2.2.1",
|
||||
"webpack-merge": "^4.0.0",
|
||||
"webpack-merge": "^2.6.1",
|
||||
"winston": "^2.1.0",
|
||||
"winston-loggly-bulk": "^1.4.2",
|
||||
"xml2js": "^0.4.4"
|
||||
},
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "^6.9.1",
|
||||
"npm": "^5.0.0"
|
||||
"npm": "^4.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .js,.vue .",
|
||||
"test": "npm run lint && gulp test && npm run client:unit && gulp apidoc",
|
||||
"test": "npm run lint && gulp test && npm run client:unit",
|
||||
"test:build": "gulp test:prepare:build",
|
||||
"test:api-v3": "gulp test:api-v3",
|
||||
"test:api-v3:unit": "gulp test:api-v3:unit",
|
||||
"test:api-v3:integration": "gulp test:api-v3:integration",
|
||||
"test:api-v3:integration:separate-server": "NODE_ENV=test gulp test:api-v3:integration:separate-server",
|
||||
"test:sanity": "istanbul cover --dir coverage/sanity --report lcovonly node_modules/mocha/bin/_mocha -- test/sanity --recursive",
|
||||
"test:common": "istanbul cover --dir coverage/common --report lcovonly node_modules/mocha/bin/_mocha -- test/common --recursive",
|
||||
"test:content": "istanbul cover --dir coverage/content --report lcovonly node_modules/mocha/bin/_mocha -- test/content --recursive",
|
||||
"test:sanity": "mocha test/sanity --recursive",
|
||||
"test:common": "mocha test/common --recursive",
|
||||
"test:content": "mocha test/content --recursive",
|
||||
"test:karma": "karma start test/client-old/spec/karma.conf.js --single-run",
|
||||
"test:karma:watch": "karma start test/client-old/spec/karma.conf.js",
|
||||
"test:prepare:webdriver": "webdriver-manager update",
|
||||
@@ -163,15 +149,14 @@
|
||||
"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": "gulp bootstrap && node webpack/dev-server.js",
|
||||
"client:build": "gulp build:client",
|
||||
"client:dev": "node webpack/dev-server.js",
|
||||
"client:build": "node webpack/build.js",
|
||||
"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 run:dev",
|
||||
"postinstall": "gulp build",
|
||||
"apidoc": "gulp apidoc"
|
||||
"postinstall": "bower --config.interactive=false install -f; gulp build; npm run client:build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-istanbul": "^4.0.0",
|
||||
@@ -181,11 +166,12 @@
|
||||
"chromedriver": "^2.27.2",
|
||||
"connect-history-api-fallback": "^1.1.0",
|
||||
"coveralls": "^2.11.2",
|
||||
"cross-env": "^3.1.4",
|
||||
"cross-spawn": "^5.0.1",
|
||||
"csv": "~0.3.6",
|
||||
"deep-diff": "~0.1.4",
|
||||
"eslint": "^3.0.0",
|
||||
"eslint-config-habitrpg": "^3.0.0",
|
||||
"eslint-config-habitrpg": "^2.0.0",
|
||||
"eslint-friendly-formatter": "^2.0.5",
|
||||
"eslint-loader": "^1.3.0",
|
||||
"eslint-plugin-html": "^2.0.0",
|
||||
@@ -196,7 +182,7 @@
|
||||
"grunt-karma": "~0.12.1",
|
||||
"http-proxy-middleware": "^0.17.0",
|
||||
"inject-loader": "^3.0.0-beta4",
|
||||
"istanbul": "^1.1.0-alpha.1",
|
||||
"istanbul": "^0.3.14",
|
||||
"karma": "^1.3.0",
|
||||
"karma-babel-preprocessor": "^6.0.1",
|
||||
"karma-chai-plugins": "~0.6.0",
|
||||
@@ -204,21 +190,19 @@
|
||||
"karma-mocha": "^0.2.0",
|
||||
"karma-mocha-reporter": "^1.1.1",
|
||||
"karma-phantomjs-launcher": "^1.0.0",
|
||||
"karma-sinon-chai": "~1.2.0",
|
||||
"karma-sinon-stub-promise": "^1.0.0",
|
||||
"karma-sinon-chai": "^1.2.0",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-spec-reporter": "0.0.24",
|
||||
"karma-webpack": "^2.0.2",
|
||||
"lcov-result-merger": "^1.0.2",
|
||||
"lolex": "^1.4.0",
|
||||
"mocha": "^3.2.0",
|
||||
"mocha": "^2.3.3",
|
||||
"mongodb": "^2.0.46",
|
||||
"mongoskin": "~2.1.0",
|
||||
"monk": "^4.0.0",
|
||||
"nightwatch": "^0.9.12",
|
||||
"phantomjs-prebuilt": "^2.1.12",
|
||||
"protractor": "^3.1.1",
|
||||
"raw-loader": "^0.5.1",
|
||||
"require-again": "^2.0.0",
|
||||
"rewire": "^2.3.3",
|
||||
"selenium-server": "^3.0.1",
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require("babel-register");
|
||||
require("babel-polyfill");
|
||||
// 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),
|
||||
@@ -9,10 +7,10 @@ var path = require('path');
|
||||
var nconf = require('nconf');
|
||||
var _ = require('lodash');
|
||||
var paypal = require('paypal-rest-sdk');
|
||||
var blocks = require('../website/common').content.subscriptionBlocks;
|
||||
var blocks = require('../../../../common').content.subscriptionBlocks;
|
||||
var live = nconf.get('PAYPAL:mode')=='live';
|
||||
|
||||
nconf.argv().env().file('user', path.join(path.resolve(__dirname, '../config.json')));
|
||||
nconf.argv().env().file('user', path.join(path.resolve(__dirname, '../../../config.json')));
|
||||
|
||||
var OP = 'create'; // list create update remove
|
||||
|
||||
@@ -51,8 +49,6 @@ _.each(blocks, function(block){
|
||||
});
|
||||
})
|
||||
|
||||
// @TODO: Add cli library for this
|
||||
|
||||
switch(OP) {
|
||||
case "list":
|
||||
paypal.billingPlan.list({status: 'ACTIVE'}, function(err, plans){
|
||||
@@ -95,17 +91,4 @@ switch(OP) {
|
||||
});
|
||||
break;
|
||||
case "remove": break;
|
||||
|
||||
case 'create-webprofile':
|
||||
let webexpinfo = {
|
||||
"name": "HabiticaProfile",
|
||||
"input_fields": {
|
||||
"no_shipping": 1,
|
||||
},
|
||||
};
|
||||
|
||||
paypal.webProfile.create(webexpinfo, (error, result) => {
|
||||
console.log(error, result)
|
||||
})
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
For information about writing and running tests, see [Using Your Local Install to Modify Habitica's Website and API](http://habitica.wikia.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API).
|
||||
@@ -0,0 +1,123 @@
|
||||
# So you want to write API integration tests?
|
||||
|
||||
@TODO rewrite
|
||||
|
||||
That's great! This README will serve as a quick primer for style conventions and practices for these tests.
|
||||
|
||||
## What is this?
|
||||
|
||||
These are integration tests for the Habitica API. They are performed by making HTTP requests to the API's endpoints and asserting on the data that is returned.
|
||||
|
||||
If the javascript looks weird to you, that's because it's written in [ES2015](http://www.ecma-international.org/ecma-262/6.0/) and transpiled by [Babel](https://babeljs.io/docs/learn-es2015/).
|
||||
|
||||
## How to run the tests
|
||||
|
||||
First, install gulp.
|
||||
|
||||
```bash
|
||||
$ npm install -g gulp
|
||||
```
|
||||
|
||||
To run the api tests, make sure the mongo db is up and running and then type this on the command line:
|
||||
|
||||
```bash
|
||||
$ gulp test:api-v2
|
||||
```
|
||||
|
||||
It may take a little while to get going, since it requires the web server to start up before the tests can run.
|
||||
|
||||
You can also run a watch command for the api tests. This will allow the tests to re-run automatically when you make changes in the `/test/api/v2/`.
|
||||
|
||||
```bash
|
||||
$ gulp test:api-v2:watch
|
||||
```
|
||||
|
||||
One caveat. If you have a severe syntax error in your files, the tests may fail to run, but they won't alert you that they are not running. If you ever find your test hanging for a while, run this to get the stackstrace. Once you've fixed the problem, you can resume running the watch comand.
|
||||
|
||||
```bash
|
||||
$ gulp test:api:safe
|
||||
```
|
||||
|
||||
If you'd like to run the tests individually and inspect the output from the server, in one pane you can run:
|
||||
|
||||
```bash
|
||||
$ gulp test:nodemon
|
||||
```
|
||||
|
||||
And run your tests in another pane:
|
||||
|
||||
```bash
|
||||
$ mocha path/to/file.js
|
||||
|
||||
# Mark a test with the `.only` attribute
|
||||
$ mocha
|
||||
```
|
||||
|
||||
## Structure
|
||||
|
||||
Each top level route has it's own directory. So, all the routes that begin with `/groups/` live in `/test/api/groups/`.
|
||||
|
||||
Each test should:
|
||||
|
||||
* encompase a single route
|
||||
* begin with the REST parameter for the route
|
||||
* display the full name of the route, swapping out `/` for `_`
|
||||
* end with test.js
|
||||
|
||||
So, for the `POST` route `/groups/:id/leave`, it would be
|
||||
|
||||
```bash
|
||||
POST-groups_id_leave.test.js
|
||||
```
|
||||
|
||||
## Promises
|
||||
|
||||
To mitigate [callback hell](http://callbackhell.com/) :imp:, we've written a helper method to generate a user object that can make http requests that [return promises](https://babeljs.io/docs/learn-es2015/#promises). This makes it very easy to chain together commands. All you need to do to make a subsequent request is return another promise and then call `.then((result) => {})` on the surrounding block, like so:
|
||||
|
||||
```js
|
||||
it('does something', () => {
|
||||
let user;
|
||||
|
||||
return generateUser().then((_user) => { // We return the initial promise so this test can be run asyncronously
|
||||
user = _user;
|
||||
|
||||
return user.post('/groups', {
|
||||
type: 'party',
|
||||
});
|
||||
}).then((party) => { // the result of the promise above is the argument of the function
|
||||
return user.put(`/groups/${party._id}`, {
|
||||
name: 'My party',
|
||||
});
|
||||
}).then((result) => {
|
||||
return user.get('/groups/party');
|
||||
}).then((party) => {
|
||||
expect(party.name).to.eql('My party');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
If the test is simple, you can use the [chai-as-promised](http://chaijs.com/plugins/chai-as-promised) `return expect(somePromise).to.eventually` syntax to make your assertion.
|
||||
|
||||
```js
|
||||
it('makes the party creator the leader automatically', () => {
|
||||
return expect(user.post('/groups', {
|
||||
type: 'party',
|
||||
})).to.eventually.have.deep.property('leader._id', user._id);
|
||||
});
|
||||
```
|
||||
|
||||
If the test is checking that the request returns an error, use the `.eventually.be.rejected.and.eql` syntax.
|
||||
|
||||
```js
|
||||
it('returns an error', () => {
|
||||
return expect(user.get('/groups/id-of-a-party-that-user-does-not-belong-to'))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
text: t('messageGroupNotFound'),
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Questions?
|
||||
|
||||
Ask in the [Aspiring Coder's Guild](https://habitica.com/#/options/groups/guilds/68d4a01e-db97-4786-8ee3-05d612c5af6f)!
|
||||
@@ -0,0 +1,4 @@
|
||||
# How to run tests:
|
||||
|
||||
1. `npm test` is equivalent to `gulp test:api-v3` which will run, in order, `gulp lint`, `gulp test:api-v3:unit` and `gulp test:api-v3:integration`. If one of these fails, the whole `npm test` command blocks and fails. Each of these commands can also be run as a standalone command.
|
||||
2. To run the server and the integrations tests in two different terminals (to better inspect the output in the server) run `npm start` in one and `npm test:api-v3:integration:separate-server` in the other
|
||||
@@ -48,10 +48,8 @@ describe('GET /challenges/:challengeId', () => {
|
||||
});
|
||||
expect(chal.group).to.eql({
|
||||
_id: group._id,
|
||||
categories: [],
|
||||
id: group.id,
|
||||
name: group.name,
|
||||
summary: group.name,
|
||||
type: group.type,
|
||||
privacy: group.privacy,
|
||||
leader: groupLeader.id,
|
||||
@@ -102,10 +100,8 @@ describe('GET /challenges/:challengeId', () => {
|
||||
});
|
||||
expect(chal.group).to.eql({
|
||||
_id: group._id,
|
||||
categories: [],
|
||||
id: group.id,
|
||||
name: group.name,
|
||||
summary: group.name,
|
||||
type: group.type,
|
||||
privacy: group.privacy,
|
||||
leader: groupLeader.id,
|
||||
@@ -157,9 +153,7 @@ describe('GET /challenges/:challengeId', () => {
|
||||
expect(chal.group).to.eql({
|
||||
_id: group._id,
|
||||
id: group.id,
|
||||
categories: [],
|
||||
name: group.name,
|
||||
summary: group.name,
|
||||
type: group.type,
|
||||
privacy: group.privacy,
|
||||
leader: groupLeader.id,
|
||||
|
||||
@@ -41,12 +41,10 @@ describe('GET challenges/user', () => {
|
||||
});
|
||||
expect(foundChallenge.group).to.eql({
|
||||
_id: publicGuild._id,
|
||||
categories: [],
|
||||
id: publicGuild._id,
|
||||
type: publicGuild.type,
|
||||
privacy: publicGuild.privacy,
|
||||
name: publicGuild.name,
|
||||
summary: publicGuild.name,
|
||||
leader: publicGuild.leader._id,
|
||||
});
|
||||
});
|
||||
@@ -63,12 +61,10 @@ describe('GET challenges/user', () => {
|
||||
});
|
||||
expect(foundChallenge1.group).to.eql({
|
||||
_id: publicGuild._id,
|
||||
categories: [],
|
||||
id: publicGuild._id,
|
||||
type: publicGuild.type,
|
||||
privacy: publicGuild.privacy,
|
||||
name: publicGuild.name,
|
||||
summary: publicGuild.name,
|
||||
leader: publicGuild.leader._id,
|
||||
});
|
||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
@@ -80,12 +76,10 @@ describe('GET challenges/user', () => {
|
||||
});
|
||||
expect(foundChallenge2.group).to.eql({
|
||||
_id: publicGuild._id,
|
||||
categories: [],
|
||||
id: publicGuild._id,
|
||||
type: publicGuild.type,
|
||||
privacy: publicGuild.privacy,
|
||||
name: publicGuild.name,
|
||||
summary: publicGuild.name,
|
||||
leader: publicGuild.leader._id,
|
||||
});
|
||||
});
|
||||
@@ -102,12 +96,10 @@ describe('GET challenges/user', () => {
|
||||
});
|
||||
expect(foundChallenge1.group).to.eql({
|
||||
_id: publicGuild._id,
|
||||
categories: [],
|
||||
id: publicGuild._id,
|
||||
type: publicGuild.type,
|
||||
privacy: publicGuild.privacy,
|
||||
name: publicGuild.name,
|
||||
summary: publicGuild.name,
|
||||
leader: publicGuild.leader._id,
|
||||
});
|
||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
@@ -119,26 +111,14 @@ describe('GET challenges/user', () => {
|
||||
});
|
||||
expect(foundChallenge2.group).to.eql({
|
||||
_id: publicGuild._id,
|
||||
categories: [],
|
||||
id: publicGuild._id,
|
||||
type: publicGuild.type,
|
||||
privacy: publicGuild.privacy,
|
||||
name: publicGuild.name,
|
||||
summary: publicGuild.name,
|
||||
leader: publicGuild.leader._id,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return not return challenges in user groups if we send member true param', async () => {
|
||||
let challenges = await member.get(`/challenges/user?member=${true}`);
|
||||
|
||||
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
|
||||
expect(foundChallenge1).to.not.exist;
|
||||
|
||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||
expect(foundChallenge2).to.not.exist;
|
||||
});
|
||||
|
||||
it('should return newest challenges first', async () => {
|
||||
let challenges = await user.get('/challenges/user');
|
||||
|
||||
@@ -157,7 +137,6 @@ describe('GET challenges/user', () => {
|
||||
let { group, groupLeader } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'TestPrivateGuild',
|
||||
summary: 'summary for TestPrivateGuild',
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
},
|
||||
@@ -179,7 +158,6 @@ describe('GET challenges/user', () => {
|
||||
let { group, groupLeader } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'TestGuild',
|
||||
summary: 'summary for TestGuild',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
},
|
||||
|
||||
@@ -304,15 +304,5 @@ describe('POST /challenges', () => {
|
||||
|
||||
await expect(groupLeader.sync()).to.eventually.have.property('challenges').to.include(challenge._id);
|
||||
});
|
||||
|
||||
it('awards achievement if this is creator\'s first challenge', async () => {
|
||||
await groupLeader.post('/challenges', {
|
||||
group: group._id,
|
||||
name: 'Test Challenge',
|
||||
shortName: 'TC Label',
|
||||
});
|
||||
groupLeader = await groupLeader.sync();
|
||||
expect(groupLeader.achievements.joinedChallenge).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -123,12 +123,5 @@ describe('POST /challenges/:challengeId/join', () => {
|
||||
|
||||
await expect(authorizedUser.get('/tags')).to.eventually.have.length(userTagsLength + 1);
|
||||
});
|
||||
|
||||
it('awards achievement if this is user\'s first challenge', async () => {
|
||||
await authorizedUser.post(`/challenges/${challenge._id}/join`);
|
||||
|
||||
await authorizedUser.sync();
|
||||
expect(authorizedUser.achievements.joinedChallenge).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -32,20 +32,18 @@ describe('POST /challenges/:challengeId/leave', () => {
|
||||
let group;
|
||||
let challenge;
|
||||
let notInChallengeUser;
|
||||
let notInGroupLeavingUser;
|
||||
let leavingUser;
|
||||
let taskText;
|
||||
|
||||
beforeEach(async () => {
|
||||
let populatedGroup = await createAndPopulateGroup({
|
||||
members: 3,
|
||||
members: 2,
|
||||
});
|
||||
|
||||
groupLeader = populatedGroup.groupLeader;
|
||||
group = populatedGroup.group;
|
||||
leavingUser = populatedGroup.members[0];
|
||||
notInChallengeUser = populatedGroup.members[1];
|
||||
notInGroupLeavingUser = populatedGroup.members[2];
|
||||
|
||||
challenge = await generateChallenge(groupLeader, group);
|
||||
|
||||
@@ -57,16 +55,17 @@ describe('POST /challenges/:challengeId/leave', () => {
|
||||
|
||||
await leavingUser.post(`/challenges/${challenge._id}/join`);
|
||||
|
||||
await notInGroupLeavingUser.post(`/challenges/${challenge._id}/join`);
|
||||
await notInGroupLeavingUser.post(`/groups/${group._id}/leave`, {
|
||||
keepChallenges: 'remain-in-challenges',
|
||||
});
|
||||
|
||||
await challenge.sync();
|
||||
});
|
||||
|
||||
it('lets user leave when not a member of the challenge group', async () => {
|
||||
await expect(notInGroupLeavingUser.post(`/challenges/${challenge._id}/leave`)).to.eventually.be.ok;
|
||||
it('returns an error when user doesn\'t have permissions to view the challenge', async () => {
|
||||
let unauthorizedUser = await generateUser();
|
||||
|
||||
await expect(unauthorizedUser.post(`/challenges/${challenge._id}/leave`)).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('challengeNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error when user isn\'t a member of the challenge', async () => {
|
||||
@@ -118,6 +117,7 @@ describe('POST /challenges/:challengeId/leave', () => {
|
||||
});
|
||||
|
||||
expect(testTask).to.not.be.undefined;
|
||||
expect(testTask.challenge).to.eql({});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+2
-22
@@ -100,8 +100,8 @@ describe('POST /challenges/:challengeId/winner/:winnerId', () => {
|
||||
await sleep(0.5);
|
||||
|
||||
await expect(winningUser.sync()).to.eventually.have.deep.property('achievements.challenges').to.include(challenge.name);
|
||||
expect(winningUser.notifications.length).to.equal(2); // 2 because winningUser just joined the challenge, which now awards an achievement
|
||||
expect(winningUser.notifications[1].type).to.equal('WON_CHALLENGE');
|
||||
expect(winningUser.notifications.length).to.equal(1);
|
||||
expect(winningUser.notifications[0].type).to.equal('WON_CHALLENGE');
|
||||
});
|
||||
|
||||
it('gives winner gems as reward', async () => {
|
||||
@@ -114,26 +114,6 @@ describe('POST /challenges/:challengeId/winner/:winnerId', () => {
|
||||
await expect(winningUser.sync()).to.eventually.have.property('balance', oldBalance + challenge.prize / 4);
|
||||
});
|
||||
|
||||
it('doesn\'t gives winner gems if group policy prevents it', async () => {
|
||||
let oldBalance = winningUser.balance;
|
||||
let oldLeaderBalance = (await groupLeader.sync()).balance;
|
||||
|
||||
await winningUser.update({
|
||||
'purchased.plan.customerId': 'group-plan',
|
||||
});
|
||||
await group.update({
|
||||
'leaderOnly.getGems': true,
|
||||
'purchased.plan.customerId': 123,
|
||||
});
|
||||
|
||||
await groupLeader.post(`/challenges/${challenge._id}/selectWinner/${winningUser._id}`);
|
||||
|
||||
await sleep(0.5);
|
||||
|
||||
await expect(winningUser.sync()).to.eventually.have.property('balance', oldBalance);
|
||||
await expect(groupLeader.sync()).to.eventually.have.property('balance', oldLeaderBalance + challenge.prize / 4);
|
||||
});
|
||||
|
||||
it('doesn\'t refund gems to group leader', async () => {
|
||||
let oldBalance = (await groupLeader.sync()).balance;
|
||||
|
||||
|
||||
@@ -84,10 +84,6 @@ describe('POST /chat/:chatId/flag', () => {
|
||||
type: 'party',
|
||||
privacy: 'private',
|
||||
});
|
||||
await user.post(`/groups/${privateGroup._id}/invite`, {
|
||||
uuids: [anotherUser._id],
|
||||
});
|
||||
await anotherUser.post(`/groups/${privateGroup._id}/join`);
|
||||
let { message } = await user.post(`/groups/${privateGroup._id}/chat`, {message: TEST_MESSAGE});
|
||||
|
||||
let flagResult = await admin.post(`/groups/${privateGroup._id}/chat/${message.id}/flag`);
|
||||
@@ -95,7 +91,7 @@ describe('POST /chat/:chatId/flag', () => {
|
||||
expect(flagResult.flags[admin._id]).to.equal(true);
|
||||
expect(flagResult.flagCount).to.equal(5);
|
||||
|
||||
let groupWithFlags = await anotherUser.get(`/groups/${privateGroup._id}`);
|
||||
let groupWithFlags = await user.get(`/groups/${privateGroup._id}`);
|
||||
let messageToCheck = find(groupWithFlags.chat, {id: message.id});
|
||||
|
||||
expect(messageToCheck).to.not.exist;
|
||||
@@ -129,20 +125,4 @@ describe('POST /chat/:chatId/flag', () => {
|
||||
message: t('messageGroupChatFlagAlreadyReported'),
|
||||
});
|
||||
});
|
||||
|
||||
it('shows a hidden message to the original poster', async () => {
|
||||
let { message } = await user.post(`/groups/${group._id}/chat`, {message: TEST_MESSAGE});
|
||||
|
||||
await admin.post(`/groups/${group._id}/chat/${message.id}/flag`);
|
||||
|
||||
let groupWithFlags = await user.get(`/groups/${group._id}`);
|
||||
let messageToCheck = find(groupWithFlags.chat, {id: message.id});
|
||||
|
||||
expect(messageToCheck).to.exist;
|
||||
|
||||
let auGroupWithFlags = await anotherUser.get(`/groups/${group._id}`);
|
||||
let auMessageToCheck = find(auGroupWithFlags.chat, {id: message.id});
|
||||
|
||||
expect(auMessageToCheck).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,28 +4,11 @@ import {
|
||||
sleep,
|
||||
server,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import {
|
||||
SPAM_MESSAGE_LIMIT,
|
||||
SPAM_MIN_EXEMPT_CONTRIB_LEVEL,
|
||||
TAVERN_ID,
|
||||
} from '../../../../../website/server/models/group';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
import { getMatchesByWordArray, removePunctuationFromString } from '../../../../../website/server/libs/stringUtils';
|
||||
import bannedWords from '../../../../../website/server/libs/bannedWords';
|
||||
import * as email from '../../../../../website/server/libs/email';
|
||||
import { IncomingWebhook } from '@slack/client';
|
||||
import nconf from 'nconf';
|
||||
|
||||
const BASE_URL = nconf.get('BASE_URL');
|
||||
|
||||
describe('POST /chat', () => {
|
||||
let user, groupWithChat, member, additionalMember;
|
||||
let user, groupWithChat, userWithChatRevoked, member;
|
||||
let testMessage = 'Test Message';
|
||||
let testBannedWordMessage = 'TEST_PLACEHOLDER_SWEAR_WORD_HERE';
|
||||
let testSlurMessage = 'message with TEST_PLACEHOLDER_SLUR_WORD_HERE';
|
||||
let bannedWordErrorMessage = t('bannedWordUsed').split('.');
|
||||
bannedWordErrorMessage[0] += ` (${removePunctuationFromString(testBannedWordMessage.toLowerCase())})`;
|
||||
bannedWordErrorMessage = bannedWordErrorMessage.join('.');
|
||||
|
||||
before(async () => {
|
||||
let { group, groupLeader, members } = await createAndPopulateGroup({
|
||||
@@ -36,10 +19,11 @@ describe('POST /chat', () => {
|
||||
},
|
||||
members: 2,
|
||||
});
|
||||
|
||||
user = groupLeader;
|
||||
groupWithChat = group;
|
||||
userWithChatRevoked = await members[0].update({'flags.chatRevoked': true});
|
||||
member = members[0];
|
||||
additionalMember = members[1];
|
||||
});
|
||||
|
||||
it('Returns an error when no message is provided', async () => {
|
||||
@@ -78,225 +62,10 @@ describe('POST /chat', () => {
|
||||
});
|
||||
|
||||
it('returns an error when chat privileges are revoked when sending a message to a public guild', async () => {
|
||||
let userWithChatRevoked = await member.update({'flags.chatRevoked': true});
|
||||
await expect(userWithChatRevoked.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage})).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('chatPrivilegesRevoked'),
|
||||
});
|
||||
});
|
||||
|
||||
context('banned word', () => {
|
||||
it('returns an error when chat message contains a banned word in tavern', async () => {
|
||||
await expect(user.post('/groups/habitrpg/chat', { message: testBannedWordMessage}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: bannedWordErrorMessage,
|
||||
});
|
||||
});
|
||||
|
||||
it('errors when word is part of a phrase', async () => {
|
||||
let wordInPhrase = `phrase ${testBannedWordMessage} end`;
|
||||
await expect(user.post('/groups/habitrpg/chat', { message: wordInPhrase}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: bannedWordErrorMessage,
|
||||
});
|
||||
});
|
||||
|
||||
it('errors when word is surrounded by non alphabet characters', async () => {
|
||||
let wordInPhrase = `_!${testBannedWordMessage}@_`;
|
||||
await expect(user.post('/groups/habitrpg/chat', { message: wordInPhrase}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: bannedWordErrorMessage,
|
||||
});
|
||||
});
|
||||
|
||||
it('checks error message has the banned words used', async () => {
|
||||
let randIndex = Math.floor(Math.random() * (bannedWords.length + 1));
|
||||
let testBannedWords = bannedWords.slice(randIndex, randIndex + 2).map((w) => w.replace(/\\/g, ''));
|
||||
let chatMessage = `Mixing ${testBannedWords[0]} and ${testBannedWords[1]} is bad for you.`;
|
||||
await expect(user.post('/groups/habitrpg/chat', { message: chatMessage}))
|
||||
.to.eventually.be.rejected
|
||||
.and.have.property('message')
|
||||
.that.includes(testBannedWords.join(', '));
|
||||
});
|
||||
|
||||
it('check all banned words are matched', async () => {
|
||||
let message = bannedWords.join(',').replace(/\\/g, '');
|
||||
let matches = getMatchesByWordArray(message, bannedWords);
|
||||
expect(matches.length).to.equal(bannedWords.length);
|
||||
});
|
||||
|
||||
it('does not error when bad word is suffix of a word', async () => {
|
||||
let wordAsSuffix = `prefix${testBannedWordMessage}`;
|
||||
let message = await user.post('/groups/habitrpg/chat', { message: wordAsSuffix});
|
||||
|
||||
expect(message.message.id).to.exist;
|
||||
});
|
||||
|
||||
it('does not error when bad word is prefix of a word', async () => {
|
||||
let wordAsPrefix = `${testBannedWordMessage}suffix`;
|
||||
let message = await user.post('/groups/habitrpg/chat', { message: wordAsPrefix});
|
||||
|
||||
expect(message.message.id).to.exist;
|
||||
});
|
||||
|
||||
it('does not error when sending a chat message containing a banned word to a party', async () => {
|
||||
let { group, members } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'Party',
|
||||
type: 'party',
|
||||
privacy: 'private',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
let message = await members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage});
|
||||
|
||||
expect(message.message.id).to.exist;
|
||||
});
|
||||
|
||||
it('does not error when sending a chat message containing a banned word to a public guild', async () => {
|
||||
let { group, members } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'public guild',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
let message = await members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage});
|
||||
|
||||
expect(message.message.id).to.exist;
|
||||
});
|
||||
|
||||
it('does not error when sending a chat message containing a banned word to a private guild', async () => {
|
||||
let { group, members } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'private guild',
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
let message = await members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage});
|
||||
|
||||
expect(message.message.id).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
context('banned slur', () => {
|
||||
beforeEach(() => {
|
||||
sandbox.spy(email, 'sendTxn');
|
||||
sandbox.stub(IncomingWebhook.prototype, 'send');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('errors and revokes privileges when chat message contains a banned slur', async () => {
|
||||
await expect(user.post(`/groups/${groupWithChat._id}/chat`, { message: testSlurMessage})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('bannedSlurUsed'),
|
||||
});
|
||||
|
||||
// Email sent to mods
|
||||
await sleep(0.5);
|
||||
expect(email.sendTxn).to.be.calledOnce;
|
||||
expect(email.sendTxn.args[0][1]).to.be.eql('slur-report-to-mods');
|
||||
|
||||
// Slack message to mods
|
||||
expect(IncomingWebhook.prototype.send).to.be.calledOnce;
|
||||
/* eslint-disable camelcase */
|
||||
expect(IncomingWebhook.prototype.send).to.be.calledWith({
|
||||
text: `${user.profile.name} (${user.id}) tried to post a slur`,
|
||||
attachments: [{
|
||||
fallback: 'Slur Message',
|
||||
color: 'danger',
|
||||
author_name: `${user.profile.name} - ${user.auth.local.email} - ${user._id}`,
|
||||
title: 'Slur in Test Guild',
|
||||
title_link: `${BASE_URL}/groups/guild/${groupWithChat.id}`,
|
||||
text: testSlurMessage,
|
||||
// footer: sandbox.match(/<.*?groupId=group-id&chatId=chat-id\|Flag this message>/),
|
||||
mrkdwn_in: [
|
||||
'text',
|
||||
],
|
||||
}],
|
||||
});
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
// Chat privileges are revoked
|
||||
await expect(user.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage})).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('chatPrivilegesRevoked'),
|
||||
});
|
||||
|
||||
// Restore chat privileges to continue testing
|
||||
user.flags.chatRevoked = false;
|
||||
await user.update({'flags.chatRevoked': false});
|
||||
});
|
||||
|
||||
it('does not allow slurs in private groups', async () => {
|
||||
let { group, members } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'Party',
|
||||
type: 'party',
|
||||
privacy: 'private',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
await expect(members[0].post(`/groups/${group._id}/chat`, { message: testSlurMessage})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('bannedSlurUsed'),
|
||||
});
|
||||
|
||||
// Email sent to mods
|
||||
await sleep(0.5);
|
||||
expect(email.sendTxn).to.be.calledThrice;
|
||||
expect(email.sendTxn.args[2][1]).to.be.eql('slur-report-to-mods');
|
||||
|
||||
// Slack message to mods
|
||||
expect(IncomingWebhook.prototype.send).to.be.calledOnce;
|
||||
/* eslint-disable camelcase */
|
||||
expect(IncomingWebhook.prototype.send).to.be.calledWith({
|
||||
text: `${members[0].profile.name} (${members[0].id}) tried to post a slur`,
|
||||
attachments: [{
|
||||
fallback: 'Slur Message',
|
||||
color: 'danger',
|
||||
author_name: `${members[0].profile.name} - ${members[0].auth.local.email} - ${members[0]._id}`,
|
||||
title: 'Slur in Party - (private party)',
|
||||
title_link: undefined,
|
||||
text: testSlurMessage,
|
||||
// footer: sandbox.match(/<.*?groupId=group-id&chatId=chat-id\|Flag this message>/),
|
||||
mrkdwn_in: [
|
||||
'text',
|
||||
],
|
||||
}],
|
||||
});
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
// Chat privileges are revoked
|
||||
await expect(members[0].post(`/groups/${groupWithChat._id}/chat`, { message: testMessage})).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('chatPrivilegesRevoked'),
|
||||
});
|
||||
|
||||
// Restore chat privileges to continue testing
|
||||
members[0].flags.chatRevoked = false;
|
||||
await members[0].update({'flags.chatRevoked': false});
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: 'Your chat privileges have been revoked.',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -404,30 +173,4 @@ describe('POST /chat', () => {
|
||||
expect(message.message.id).to.exist;
|
||||
expect(memberWithNotification.newMessages[`${group._id}`]).to.exist;
|
||||
});
|
||||
|
||||
context('Spam prevention', () => {
|
||||
it('Returns an error when the user has been posting too many messages', async () => {
|
||||
// Post as many messages are needed to reach the spam limit
|
||||
for (let i = 0; i < SPAM_MESSAGE_LIMIT; i++) {
|
||||
let result = await additionalMember.post(`/groups/${TAVERN_ID}/chat`, { message: testMessage }); // eslint-disable-line no-await-in-loop
|
||||
expect(result.message.id).to.exist;
|
||||
}
|
||||
|
||||
await expect(additionalMember.post(`/groups/${TAVERN_ID}/chat`, { message: testMessage })).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('messageGroupChatSpam'),
|
||||
});
|
||||
});
|
||||
|
||||
it('contributor should not receive spam alert', async () => {
|
||||
let userSocialite = await member.update({'contributor.level': SPAM_MIN_EXEMPT_CONTRIB_LEVEL, 'flags.chatRevoked': false});
|
||||
|
||||
// Post 1 more message than the spam limit to ensure they do not reach the limit
|
||||
for (let i = 0; i < SPAM_MESSAGE_LIMIT + 1; i++) {
|
||||
let result = await userSocialite.post(`/groups/${TAVERN_ID}/chat`, { message: testMessage }); // eslint-disable-line no-await-in-loop
|
||||
expect(result.message.id).to.exist;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
|
||||
describe('GET /group-plans', () => {
|
||||
let user;
|
||||
let groupPlan;
|
||||
|
||||
before(async () => {
|
||||
user = await generateUser({balance: 4});
|
||||
groupPlan = await generateGroup(user,
|
||||
{
|
||||
name: 'public guild - is member',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
},
|
||||
{
|
||||
purchased: {
|
||||
plan: {
|
||||
customerId: 'existings',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('returns group plans for the user', async () => {
|
||||
let groupPlans = await user.get('/group-plans');
|
||||
|
||||
expect(groupPlans[0]._id).to.eql(groupPlan._id);
|
||||
});
|
||||
});
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
import {
|
||||
TAVERN_ID,
|
||||
} from '../../../../../website/server/models/group';
|
||||
import apiMessages from '../../../../../website/server/libs/apiMessages';
|
||||
|
||||
describe('GET /groups', () => {
|
||||
let user;
|
||||
@@ -15,13 +14,6 @@ describe('GET /groups', () => {
|
||||
const NUMBER_OF_PUBLIC_GUILDS_USER_IS_MEMBER = 1;
|
||||
const NUMBER_OF_USERS_PRIVATE_GUILDS = 1;
|
||||
const NUMBER_OF_GROUPS_USER_CAN_VIEW = 5;
|
||||
const GUILD_PER_PAGE = 30;
|
||||
let categories = [{
|
||||
slug: 'newCat',
|
||||
name: 'New Category',
|
||||
}];
|
||||
let publicGuildNotMember;
|
||||
let privateGuildUserIsMemberOf;
|
||||
|
||||
before(async () => {
|
||||
await resetHabiticaDB();
|
||||
@@ -37,18 +29,16 @@ describe('GET /groups', () => {
|
||||
await leader.post(`/groups/${publicGuildUserIsMemberOf._id}/invite`, { uuids: [user._id]});
|
||||
await user.post(`/groups/${publicGuildUserIsMemberOf._id}/join`);
|
||||
|
||||
publicGuildNotMember = await generateGroup(leader, {
|
||||
await generateGroup(leader, {
|
||||
name: 'public guild - is not member',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
categories,
|
||||
});
|
||||
|
||||
privateGuildUserIsMemberOf = await generateGroup(leader, {
|
||||
let privateGuildUserIsMemberOf = await generateGroup(leader, {
|
||||
name: 'private guild - is member',
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
categories,
|
||||
});
|
||||
await leader.post(`/groups/${privateGuildUserIsMemberOf._id}/invite`, { uuids: [user._id]});
|
||||
await user.post(`/groups/${privateGuildUserIsMemberOf._id}/join`);
|
||||
@@ -108,104 +98,6 @@ describe('GET /groups', () => {
|
||||
.to.eventually.have.a.lengthOf(NUMBER_OF_PUBLIC_GUILDS);
|
||||
});
|
||||
|
||||
describe('filters', () => {
|
||||
it('returns public guilds filtered by category', async () => {
|
||||
let guilds = await user.get(`/groups?type=publicGuilds&categories=${categories[0].slug}`);
|
||||
|
||||
expect(guilds[0]._id).to.equal(publicGuildNotMember._id);
|
||||
});
|
||||
|
||||
it('returns private guilds filtered by category', async () => {
|
||||
let guilds = await user.get(`/groups?type=privateGuilds&categories=${categories[0].slug}`);
|
||||
|
||||
expect(guilds[0]._id).to.equal(privateGuildUserIsMemberOf._id);
|
||||
});
|
||||
|
||||
it('filters public guilds by size', async () => {
|
||||
await generateGroup(user, {
|
||||
name: 'guild1',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
memberCount: 1,
|
||||
});
|
||||
|
||||
// @TODO: anyway to set higher memberCount in tests right now?
|
||||
|
||||
let guilds = await user.get('/groups?type=publicGuilds&minMemberCount=3');
|
||||
|
||||
expect(guilds.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('filters private guilds by size', async () => {
|
||||
await generateGroup(user, {
|
||||
name: 'guild1',
|
||||
type: 'guild',
|
||||
privacy: 'private',
|
||||
memberCount: 1,
|
||||
});
|
||||
|
||||
// @TODO: anyway to set higher memberCount in tests right now?
|
||||
|
||||
let guilds = await user.get('/groups?type=privateGuilds&minMemberCount=3');
|
||||
|
||||
expect(guilds.length).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('public guilds pagination', () => {
|
||||
it('req.query.paginate must be a boolean string', async () => {
|
||||
await expect(user.get('/groups?paginate=aString&type=publicGuilds'))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'Invalid request parameters.',
|
||||
});
|
||||
});
|
||||
|
||||
it('req.query.paginate can only be true when req.query.type includes publicGuilds', async () => {
|
||||
await expect(user.get('/groups?paginate=true&type=notPublicGuilds'))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: apiMessages('guildsOnlyPaginate'),
|
||||
});
|
||||
});
|
||||
|
||||
it('req.query.page can\'t be negative', async () => {
|
||||
await expect(user.get('/groups?paginate=true&page=-1&type=publicGuilds'))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'Invalid request parameters.',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 30 guilds per page ordered by number of members', async () => {
|
||||
await user.update({balance: 9000});
|
||||
let groups = await Promise.all(_.times(60, (i) => {
|
||||
return generateGroup(user, {
|
||||
name: `public guild ${i} - is member`,
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
});
|
||||
}));
|
||||
|
||||
// update group number 32 and not the first to make sure sorting works
|
||||
await groups[32].update({name: 'guild with most members', memberCount: 199});
|
||||
await groups[33].update({name: 'guild with less members', memberCount: -100});
|
||||
|
||||
let page0 = await expect(user.get('/groups?type=publicGuilds&paginate=true'))
|
||||
.to.eventually.have.a.lengthOf(GUILD_PER_PAGE);
|
||||
expect(page0[0].name).to.equal('guild with most members');
|
||||
|
||||
await expect(user.get('/groups?type=publicGuilds&paginate=true&page=1'))
|
||||
.to.eventually.have.a.lengthOf(GUILD_PER_PAGE);
|
||||
let page2 = await expect(user.get('/groups?type=publicGuilds&paginate=true&page=2'))
|
||||
.to.eventually.have.a.lengthOf(1 + 3); // 1 created now, 3 by other tests
|
||||
expect(page2[3].name).to.equal('guild with less members');
|
||||
});
|
||||
});
|
||||
|
||||
it('returns all the user\'s guilds when guilds passed in as query', async () => {
|
||||
await expect(user.get('/groups?type=guilds'))
|
||||
.to.eventually.have.a.lengthOf(NUMBER_OF_PUBLIC_GUILDS_USER_IS_MEMBER + NUMBER_OF_USERS_PRIVATE_GUILDS);
|
||||
|
||||
@@ -63,17 +63,15 @@ describe('GET /groups/:groupId/invites', () => {
|
||||
});
|
||||
|
||||
it('returns only first 30 invites', async () => {
|
||||
let leader = await generateUser({balance: 4});
|
||||
let group = await generateGroup(leader, {type: 'guild', privacy: 'public', name: generateUUID()});
|
||||
|
||||
let group = await generateGroup(user, {type: 'party', name: generateUUID()});
|
||||
let invitesToGenerate = [];
|
||||
for (let i = 0; i < 31; i++) {
|
||||
invitesToGenerate.push(generateUser());
|
||||
}
|
||||
let generatedInvites = await Promise.all(invitesToGenerate);
|
||||
await leader.post(`/groups/${group._id}/invite`, {uuids: generatedInvites.map(invite => invite._id)});
|
||||
await user.post(`/groups/${group._id}/invite`, {uuids: generatedInvites.map(invite => invite._id)});
|
||||
|
||||
let res = await leader.get(`/groups/${group._id}/invites`);
|
||||
let res = await user.get('/groups/party/invites');
|
||||
expect(res.length).to.equal(30);
|
||||
res.forEach(member => {
|
||||
expect(member).to.have.all.keys(['_id', 'id', 'profile']);
|
||||
|
||||
@@ -66,25 +66,11 @@ describe('GET /groups/:groupId/members', () => {
|
||||
expect(res[0].profile).to.have.all.keys(['name']);
|
||||
});
|
||||
|
||||
it('req.query.includeAllPublicFields === true works with guilds', async () => {
|
||||
it('req.query.includeAllPublicFields === true only works with parties', async () => {
|
||||
let group = await generateGroup(user, {type: 'guild', name: generateUUID()});
|
||||
let [memberRes] = await user.get(`/groups/${group._id}/members?includeAllPublicFields=true`);
|
||||
|
||||
expect(memberRes).to.have.all.keys([ // works as: object has all and only these keys
|
||||
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
||||
'backer', 'contributor', 'auth', 'items', 'inbox',
|
||||
]);
|
||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||
'size', 'hair', 'skin', 'shirt',
|
||||
'chair', 'costume', 'sleep', 'background', 'tasks',
|
||||
].sort());
|
||||
|
||||
expect(memberRes.stats.maxMP).to.exist;
|
||||
expect(memberRes.stats.maxHealth).to.equal(common.maxHealth);
|
||||
expect(memberRes.stats.toNextLevel).to.equal(common.tnl(memberRes.stats.lvl));
|
||||
expect(memberRes.inbox.optOut).to.exist;
|
||||
expect(memberRes.inbox.messages).to.not.exist;
|
||||
let res = await user.get(`/groups/${group._id}/members?includeAllPublicFields=true`);
|
||||
expect(res[0]).to.have.all.keys(['_id', 'id', 'profile']);
|
||||
expect(res[0].profile).to.have.all.keys(['name']);
|
||||
});
|
||||
|
||||
it('populates all public fields if req.query.includeAllPublicFields === true and it is a party', async () => {
|
||||
@@ -98,7 +84,7 @@ describe('GET /groups/:groupId/members', () => {
|
||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||
'size', 'hair', 'skin', 'shirt',
|
||||
'chair', 'costume', 'sleep', 'background', 'tasks',
|
||||
'chair', 'costume', 'sleep', 'background',
|
||||
].sort());
|
||||
|
||||
expect(memberRes.stats.maxMP).to.exist;
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { find } from 'lodash';
|
||||
|
||||
describe('POST /group/:groupId/remove-manager', () => {
|
||||
let leader, nonLeader, groupToUpdate;
|
||||
let groupName = 'Test Public Guild';
|
||||
let groupType = 'guild';
|
||||
let nonManager;
|
||||
|
||||
function findAssignedTask (memberTask) {
|
||||
return memberTask.group.id === groupToUpdate._id;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
let { group, groupLeader, members } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: groupName,
|
||||
type: groupType,
|
||||
privacy: 'public',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
groupToUpdate = group;
|
||||
leader = groupLeader;
|
||||
nonLeader = members[0];
|
||||
nonManager = members[0];
|
||||
});
|
||||
|
||||
it('returns an error when a non group leader tries to add member', async () => {
|
||||
await expect(nonLeader.post(`/groups/${groupToUpdate._id}/remove-manager`, {
|
||||
managerId: nonLeader._id,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('messageGroupOnlyLeaderCanUpdate'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error when manager does not exist', async () => {
|
||||
await expect(leader.post(`/groups/${groupToUpdate._id}/remove-manager`, {
|
||||
managerId: nonManager._id,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('userIsNotManager'),
|
||||
});
|
||||
});
|
||||
|
||||
it('allows a leader to remove managers', async () => {
|
||||
await leader.post(`/groups/${groupToUpdate._id}/add-manager`, {
|
||||
managerId: nonLeader._id,
|
||||
});
|
||||
|
||||
let updatedGroup = await leader.post(`/groups/${groupToUpdate._id}/remove-manager`, {
|
||||
managerId: nonLeader._id,
|
||||
});
|
||||
|
||||
expect(updatedGroup.managers[nonLeader._id]).to.not.exist;
|
||||
});
|
||||
|
||||
it('removes group approval notifications from a manager that is removed', async () => {
|
||||
await leader.post(`/groups/${groupToUpdate._id}/add-manager`, {
|
||||
managerId: nonLeader._id,
|
||||
});
|
||||
let task = await leader.post(`/tasks/group/${groupToUpdate._id}`, {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
requiresApproval: true,
|
||||
});
|
||||
await nonLeader.post(`/tasks/${task._id}/assign/${leader._id}`);
|
||||
let memberTasks = await leader.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
await expect(leader.post(`/tasks/${syncedTask._id}/score/up`))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('taskApprovalHasBeenRequested'),
|
||||
});
|
||||
|
||||
let updatedGroup = await leader.post(`/groups/${groupToUpdate._id}/remove-manager`, {
|
||||
managerId: nonLeader._id,
|
||||
});
|
||||
|
||||
await nonLeader.sync();
|
||||
|
||||
expect(nonLeader.notifications.length).to.equal(0);
|
||||
expect(updatedGroup.managers[nonLeader._id]).to.not.exist;
|
||||
});
|
||||
});
|
||||
@@ -74,18 +74,6 @@ describe('POST /group', () => {
|
||||
expect(updatedUser.guilds).to.include(guild._id);
|
||||
});
|
||||
|
||||
it('awards the Joined Guild achievement', async () => {
|
||||
await user.post('/groups', {
|
||||
name: 'some guild',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
});
|
||||
|
||||
let updatedUser = await user.get('/user');
|
||||
|
||||
expect(updatedUser.achievements.joinedGuild).to.eql(true);
|
||||
});
|
||||
|
||||
context('public guild', () => {
|
||||
it('creates a group', async () => {
|
||||
let groupName = 'Test Public Guild';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user