Compare commits

..

6 Commits

Author SHA1 Message Date
Sabe Jones 93437a00dd 3.78.0 2017-03-02 21:05:49 +00:00
Sabe Jones 31afa0345e chore(news): Bailey 2017-03-02 2017-03-02 20:59:03 +00:00
Sabe Jones b2c278b00a fix(sprites): correct shop canvas 2017-03-02 20:58:42 +00:00
Sabe Jones b54037f230 chore(event): disable Cupid Potions 2017-03-02 20:49:02 +00:00
Sabe Jones 488dcb916a chore(sprites): compile 2017-03-02 20:44:25 +00:00
Sabe Jones 10792b65be feat(content): add Armoire and BGs 2017-03 2017-03-02 20:44:00 +00:00
9972 changed files with 148524 additions and 163696 deletions
-1
View File
@@ -1,7 +1,6 @@
{
"presets": ["es2015"],
"plugins": [
"transform-object-rest-spread",
["transform-async-to-module-method", {
"module": "bluebird",
"method": "coroutine"
+3
View File
@@ -0,0 +1,3 @@
{
"directory": "website/client-old/bower_components"
}
+7 -3
View File
@@ -14,13 +14,17 @@ 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"
02_ownBabel:
command: "chmod a+rw /tmp/.babel.json"
03_installGulp:
03_installBower:
command: "$NODE_HOME/bin/npm install -g bower"
04_installGulp:
command: "$NODE_HOME/bin/npm install -g gulp"
04_runGulp:
05_runBower:
command: "$NODE_HOME/lib/node_modules/bower/bin/bower --config.interactive=false --allow-root install -f"
06_runGulp:
command: "$NODE_HOME/lib/node_modules/gulp/bin/gulp.js build"
+7 -2
View File
@@ -8,11 +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
+2 -2
View File
@@ -1,10 +1,10 @@
# 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
[Please see these instructions for adding a pull request](http://habitica.wikia.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API)
[Please see these instructions for adding a pull request](http://habitica.wikia.com/wiki/Using_Habitica_Git#Pull_Request)
# Requesting a feature
+9 -7
View File
@@ -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 from the Habitia website at User Icon > Settings > API)
### General Info
[//]: # (Fill out relevant information - UUID is found in Settings -> API)
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.)
+2 -2
View File
@@ -1,4 +1,4 @@
[//]: # (Note: See http://habitica.wikia.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API for more info)
[//]: # (Note: See http://habitica.wikia.com/wiki/Using_Habitica_Git#Pull_Request for more info)
[//]: # (Put Issue # or URL here, if applicable. This will automatically close the issue if your PR is merged in)
Fixes put_issue_url_here
@@ -8,7 +8,7 @@ Fixes put_issue_url_here
[//]: # (Put User ID in here - found on the Habitica website at User Icon > Settings > API)
[//]: # (Put User ID in here - found in Settings -> API)
----
UUID:
-3
View File
@@ -2,14 +2,11 @@
website/client-old/gen
website/client-old/common
website/client-old/apidoc
website/build
website/client-old/js/habitrpg-shared.js*
website/client-old/css/habitrpg-shared.css
website/transpiled-babel/
website/common/transpiled-babel/
node_modules
content_cache
apidoc_build
*.swp
.idea*
config.json
+10 -14
View File
@@ -2,9 +2,6 @@ language: node_js
node_js:
- '6'
sudo: required
dist: precise
services:
- mongodb
addons:
apt:
sources:
@@ -13,25 +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="apidoc"
- TEST="test:content"
- TEST="test:common"
- TEST="test:karma"
- TEST="client:unit"
+45 -21
View File
@@ -1,21 +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 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
# 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"]
-32
View File
@@ -1,32 +0,0 @@
FROM node:boron
ENV ADMIN_EMAIL admin@habitica.com
ENV AMAZON_PAYMENTS_CLIENT_ID amzn1.application-oa2-client.68ed9e6904ef438fbc1bf86bf494056e
ENV AMAZON_PAYMENTS_SELLER_ID AMQ3SB4SG5E91
ENV AMPLITUDE_KEY e8d4c24b3d6ef3ee73eeba715023dd43
ENV BASE_URL https://habitica.com
ENV FACEBOOK_KEY 128307497299777
ENV GA_ID UA-33510635-1
ENV GOOGLE_CLIENT_ID 1035232791481-32vtplgnjnd1aufv3mcu1lthf31795fq.apps.googleusercontent.com
ENV NODE_ENV production
ENV STRIPE_PUB_KEY pk_85fQ0yMECHNfHTSsZoxZXlPSwSNfA
# 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 mocha
# Clone Habitica repo and install dependencies
RUN mkdir -p /usr/src/habitrpg
WORKDIR /usr/src/habitrpg
RUN git clone --branch v4.6.3 https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
RUN npm install
RUN gulp build:prod --force
# Create Build dir
RUN mkdir -p ./website/build
# Start Habitica
EXPOSE 3000
CMD ["node", "./website/transpiled-babel/index.js"]
+142
View File
@@ -0,0 +1,142 @@
/*global module:false*/
require('babel-register');
var _ = require('lodash');
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
karma: {
unit: {
configFile: 'test/client-old/spec/karma.conf.js'
},
continuous: {
configFile: 'test/client-old/spec/karma.conf.js',
singleRun: true,
autoWatch: false
}
},
clean: {
build: ['website/build']
},
cssmin: {
dist: {
options: {
report: 'gzip'
},
files:{
"website/client-old/css/habitrpg-shared.css": [
"website/assets/sprites/dist/spritesmith*.css",
"website/assets/sprites/css/backer.css",
"website/assets/sprites/css/Mounts.css",
"website/assets/sprites/css/index.css"
]
}
}
},
stylus: {
build: {
options: {
compress: false, // AFTER
'include css': true,
paths: ['website/client-old']
},
files: {
'website/build/app.css': ['website/client-old/css/index.styl'],
'website/build/static.css': ['website/client-old/css/static.styl']
}
}
},
copy: {
build: {
files: [
{expand: true, cwd: 'website/client-old/', src: 'favicon.ico', dest: 'website/build/'},
{expand: true, cwd: 'website/client-old/', src: 'favicon_192x192.png', dest: 'website/build/'},
{expand: true, cwd: 'website/assets/sprites/dist/', src: 'spritesmith*.png', dest: 'website/build/static/sprites'},
{expand: true, cwd: 'website/assets/sprites/', src: 'backer-only/*.gif', dest: 'website/build/'},
{expand: true, cwd: 'website/assets/sprites/', src: 'npc_ian.gif', dest: 'website/build/'},
{expand: true, cwd: 'website/assets/sprites/', src: 'quest_*.gif', dest: 'website/build/'},
{expand: true, cwd: 'website/client-old/', src: 'bower_components/bootstrap/dist/fonts/*', dest: 'website/build/'}
]
}
},
// UPDATE IT WHEN YOU ADD SOME FILES NOT ALREADY MATCHED!
hashres: {
build: {
options: {
fileNameFormat: '${name}-${hash}.${ext}'
},
src: [
'website/build/*.js',
'website/build/*.css',
'website/build/favicon.ico',
'website/build/favicon_192x192.png',
'website/build/*.png',
'website/build/static/sprites/*.png',
'website/build/*.gif',
'website/build/bower_components/bootstrap/dist/fonts/*'
],
dest: 'website/build/*.css'
}
}
});
//Load build files from client-old/manifest.json
grunt.registerTask('loadManifestFiles', 'Load all build files from client-old/manifest.json', function(){
var files = grunt.file.readJSON('./website/client-old/manifest.json');
var uglify = {};
var cssmin = {};
_.each(files, function(val, key){
var js = uglify['website/build/' + key + '.js'] = [];
_.each(files[key].js, function(val){
var path = "./";
if( val.indexOf('common/') == -1)
path = './website/client-old/';
js.push(path + val);
});
var css = cssmin['website/build/' + key + '.css'] = [];
_.each(files[key].css, function(val){
var path = "./";
if( val.indexOf('common/') == -1) {
path = (val == 'app.css' || val == 'static.css') ? './website/build/' : './website/client-old/';
}
css.push(path + val)
});
});
grunt.config.set('uglify.build.files', uglify);
grunt.config.set('uglify.build.options', {compress: false});
grunt.config.set('cssmin.build.files', cssmin);
// Rewrite urls to relative path
grunt.config.set('cssmin.build.options', {'target': 'website/client-old/css/whatever-css.css'});
});
// Register tasks.
grunt.registerTask('build:prod', ['loadManifestFiles', 'clean:build', 'uglify', 'stylus', 'cssmin', 'copy:build', 'hashres']);
grunt.registerTask('build:dev', ['cssmin', 'stylus']);
grunt.registerTask('build:test', ['build:dev']);
// Load tasks
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-stylus');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-hashres');
if (process.env.NODE_ENV !== 'production') grunt.loadNpmTasks('grunt-karma');
};
+3 -2
View File
@@ -5,7 +5,8 @@ Habitica [![Build Status](https://travis-ci.org/HabitRPG/habitica.svg?branch=dev
We need more programmers! Your assistance will be greatly appreciated.
For an introduction to the technologies used and how the software is organized, refer to [Guidance for Blacksmiths](http://habitica.wikia.com/wiki/Guidance_for_Blacksmiths).
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.
+56
View File
@@ -0,0 +1,56 @@
{
"name": "HabitRPG",
"version": "0.1.1",
"homepage": "https://github.com/lefnire/habitrpg",
"authors": [
"Tyler Renelle <tylerrenelle@gmail.com>"
],
"private": true,
"ignore": [
"**/.*",
"node_modules",
"website/client-old/bower_components",
"test",
"tests"
],
"dependencies": {
"Angular-At-Directive": "snicker/Angular-At-Directive#c27bae207aa06d1e",
"angular": "1.3.9",
"angular-bootstrap": "0.13.0",
"angular-filter": "0.5.1",
"angular-loading-bar": "0.6.0",
"angular-resource": "1.3.9",
"angular-sanitize": "1.3.9",
"angular-ui": "0.4.0",
"angular-ui-router": "0.2.13",
"angular-ui-select2": "angular-ui/ui-select2#afa6589a54cb72815f",
"angular-ui-utils": "0.1.0",
"bootstrap": "3.1.0",
"bootstrap-growl": "ifightcrime/bootstrap-growl#162daa41cd1155f",
"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.13.4",
"jquery": "2.1.0",
"jquery-colorbox": "1.4.36",
"jquery-ui": "1.10.3",
"jquery.cookie": "1.4.0",
"js-emoji": "snicker/js-emoji#f25d8a303f",
"ngInfiniteScroll": "1.1.0",
"pnotify": "1.3.1",
"sticky": "1.0.3",
"swagger-ui": "wordnik/swagger-ui#v2.0.24",
"smart-app-banner": "78ef9c0679723b25be1a0ae04f7b4aef7cbced4f",
"habitica-markdown": "1.2.2",
"pusher-js-auth": "^2.0.0",
"pusher-websocket-iso": "pusher#^3.2.0",
"taggle": "^1.11.1"
},
"devDependencies": {
"angular-mocks": "1.3.9"
},
"resolutions": {
"angular": "1.3.9",
"jquery": ">=1.9.0"
}
}
+3 -18
View File
@@ -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 -1
View File
@@ -1,3 +1,3 @@
web:
volumes:
- '.:/usr/src/habitrpg'
- '.:/habitrpg'
+10
View File
@@ -0,0 +1,10 @@
{
"root": true,
"env": {
"node": true,
},
"extends": [
"habitrpg/server",
"habitrpg/babel"
],
}
+2 -2
View File
@@ -2,7 +2,7 @@ import gulp from 'gulp';
import clean from 'rimraf';
import apidoc from 'apidoc';
const APIDOC_DEST_PATH = './apidoc_build';
const APIDOC_DEST_PATH = './website/build/apidoc';
const APIDOC_SRC_PATH = './website/server';
gulp.task('apidoc:clean', (done) => {
clean(APIDOC_DEST_PATH, done);
@@ -22,5 +22,5 @@ gulp.task('apidoc', ['apidoc:clean'], (done) => {
});
gulp.task('apidoc:watch', ['apidoc'], () => {
return gulp.watch(`${APIDOC_SRC_PATH}/**/*.js`, ['apidoc']);
return gulp.watch(APIDOC_SRC_PATH + '/**/*.js', ['apidoc']);
});
+31
View File
@@ -0,0 +1,31 @@
import gulp from 'gulp';
import browserify from 'browserify';
import source from 'vinyl-source-stream';
import buffer from 'vinyl-buffer';
import uglify from 'gulp-uglify';
import sourcemaps from 'gulp-sourcemaps';
import babel from 'babelify';
gulp.task('browserify', function () {
let bundler = browserify({
entries: './website/common/browserify.js',
debug: true,
transform: [[babel, { compact: false }]],
});
return bundler.bundle()
.pipe(source('habitrpg-shared.js'))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(uglify())
.on('error', function (err) {
console.error(err);
this.emit('end');
})
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('./website/client-old/js/'));
});
gulp.task('browserify:watch', () => {
gulp.watch('./website/common/script/**/*.js', ['browserify']);
});
-36
View File
@@ -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,
);
});
+18 -13
View File
@@ -1,10 +1,13 @@
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', () => {
if (process.env.NODE_ENV === 'production') { // eslint-disable-line no-process-env
if (process.env.NODE_ENV === 'production') {
gulp.start('build:prod');
} else {
gulp.start('build:dev');
}
});
@@ -22,16 +25,18 @@ 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); // eslint-disable-line no-console
});
gulp.task('build:dev', ['browserify', 'prepare:staticNewStuff', 'semantic-ui'], (done) => {
gulp.start('grunt-build:dev', done);
});
gulp.task('build:prod', [
'build:server',
'build:client',
'apidoc',
]);
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', 'semantic-ui'], (done) => {
runSequence(
'grunt-build:prod',
'apidoc',
done
);
});
+10 -10
View File
@@ -7,11 +7,10 @@ import gulp from 'gulp';
// Add additional properties to the repl's context
let improveRepl = (context) => {
// Let "exit" and "quit" terminate the console
['exit', 'quit'].forEach((term) => {
Object.defineProperty(context, term, { get () {
process.exit();
}});
Object.defineProperty(context, term, { get () { process.exit(); }});
});
// "clear" clears the screen
@@ -19,12 +18,12 @@ let improveRepl = (context) => {
process.stdout.write('\u001B[2J\u001B[0;0f');
}});
context.Challenge = require('../website/server/models/challenge').model; // eslint-disable-line global-require
context.Group = require('../website/server/models/group').model; // eslint-disable-line global-require
context.User = require('../website/server/models/user').model; // eslint-disable-line global-require
context.Challenge = require('../website/server/models/challenge').model;
context.Group = require('../website/server/models/group').model;
context.User = require('../website/server/models/user').model;
const isProd = nconf.get('NODE_ENV') === 'production';
const mongooseOptions = !isProd ? {} : {
var isProd = nconf.get('NODE_ENV') === 'production';
var mongooseOptions = !isProd ? {} : {
replset: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } },
server: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } },
};
@@ -32,15 +31,16 @@ let improveRepl = (context) => {
mongoose.connect(
nconf.get('NODE_DB_URI'),
mongooseOptions,
(err) => {
function (err) {
if (err) throw err;
logger.info('Connected with Mongoose');
}
)
);
};
gulp.task('console', () => {
gulp.task('console', (cb) => {
improveRepl(repl.start({
prompt: 'Habitica > ',
}).context);
+10
View File
@@ -0,0 +1,10 @@
import gulp from 'gulp';
import jade from 'jade';
import {writeFileSync} from 'fs';
gulp.task('prepare:staticNewStuff', () => {
writeFileSync(
'./website/client-old/new-stuff.html',
jade.compileFile('./website/views/shared/new-stuff.jade')()
);
});
+43
View File
@@ -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);
}
);
});
+104 -105
View File
@@ -10,39 +10,82 @@ import {each} from 'lodash';
// https://github.com/Ensighten/grunt-spritesmith/issues/67#issuecomment-34786248
const MAX_SPRITESHEET_SIZE = 1024 * 1024 * 3;
const DIST_PATH = 'website/assets/sprites/dist/';
const IMG_DIST_PATH = 'website/static/sprites/';
const CSS_DIST_PATH = 'website/client/assets/css/sprites/';
const IMG_DIST_PATH_NEW_CLIENT = 'website/static/sprites/';
const CSS_DIST_PATH_NEW_CLIENT = 'website/client/assets/css/sprites/';
function checkForSpecialTreatment (name) {
let regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame/;
return name.match(regex) || name === 'head_0';
}
gulp.task('sprites:compile', ['sprites:clean', 'sprites:main', 'sprites:largeSprites', 'sprites:checkCompiledDimensions']);
function calculateImgDimensions (img, addPadding) {
let dims = sizeOf(img);
gulp.task('sprites:main', () => {
let mainSrc = sync('website/assets/sprites/spritesmith/**/*.png');
return createSpritesStream('main', mainSrc);
});
let requiresSpecialTreatment = checkForSpecialTreatment(img);
if (requiresSpecialTreatment) {
let newWidth = dims.width < 90 ? 90 : dims.width;
let newHeight = dims.height < 90 ? 90 : dims.height;
dims = {
width: newWidth,
height: newHeight,
};
gulp.task('sprites:largeSprites', () => {
let largeSrc = sync('website/assets/sprites/spritesmith_large/**/*.png');
return createSpritesStream('largeSprites', largeSrc);
});
gulp.task('sprites:clean', (done) => {
clean(`{${DIST_PATH}spritesmith*,${IMG_DIST_PATH_NEW_CLIENT}spritesmith*,${CSS_DIST_PATH_NEW_CLIENT}spritesmith*}`, done);
});
gulp.task('sprites:checkCompiledDimensions', ['sprites:main', 'sprites:largeSprites'], () => {
console.log('Verifiying that images do not exceed max dimensions');
let numberOfSheetsThatAreTooBig = 0;
let distSpritesheets = sync(`${DIST_PATH}*.png`);
each(distSpritesheets, (img, index) => {
let spriteSize = calculateImgDimensions(img);
if (spriteSize > MAX_SPRITESHEET_SIZE) {
numberOfSheetsThatAreTooBig++;
let name = basename(img, '.png');
console.error(`WARNING: ${name} might be too big - ${spriteSize} > ${MAX_SPRITESHEET_SIZE}`);
}
});
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/habitrpg/pull/6683#issuecomment-185462180
} else {
console.log('All images are within the correct dimensions');
}
});
let padding = 0;
function createSpritesStream (name, src) {
let spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src);
let stream = mergeStream();
if (addPadding) {
padding = dims.width * 8 + dims.height * 8;
}
each(spritesheetSliceIndicies, (start, index) => {
let slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]);
if (!dims.width || !dims.height) console.error('MISSING DIMENSIONS:', dims); // eslint-disable-line no-console
let spriteData = gulp.src(slicedSrc)
.pipe(spritesmith({
imgName: `spritesmith-${name}-${index}.png`,
cssName: `spritesmith-${name}-${index}.css`,
algorithm: 'binary-tree',
padding: 1,
cssTemplate: 'website/assets/sprites/css/css.template.handlebars',
cssVarMap: cssVarMap,
}));
let totalPixelSize = dims.width * dims.height + padding;
let imgStream = spriteData.img
.pipe(imagemin())
.pipe(gulp.dest(IMG_DIST_PATH_NEW_CLIENT))
.pipe(gulp.dest(DIST_PATH));
return totalPixelSize;
let cssStream = spriteData.css
.pipe(gulp.dest(CSS_DIST_PATH_NEW_CLIENT))
.pipe(gulp.dest(DIST_PATH));
stream.add(imgStream);
stream.add(cssStream);
});
return stream;
}
function calculateSpritesheetsSrcIndicies (src) {
@@ -62,6 +105,37 @@ function calculateSpritesheetsSrcIndicies (src) {
return slices;
}
function calculateImgDimensions (img, addPadding) {
let dims = sizeOf(img);
let requiresSpecialTreatment = checkForSpecialTreatment(img);
if (requiresSpecialTreatment) {
let newWidth = dims.width < 90 ? 90 : dims.width;
let newHeight = dims.height < 90 ? 90 : dims.height;
dims = {
width: newWidth,
height: newHeight,
};
}
let padding = 0;
if (addPadding) {
padding = (dims.width * 8) + (dims.height * 8);
}
if (!dims.width || !dims.height) console.error('MISSING DIMENSIONS:', dims);
let totalPixelSize = (dims.width * dims.height) + padding;
return totalPixelSize;
}
function checkForSpecialTreatment (name) {
let regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame/;
return name.match(regex) || name === 'head_0';
}
function cssVarMap (sprite) {
// For hair, skins, beards, etc. we want to output a '.customize-options.WHATEVER' class, which works as a
// 60x60 image pointing at the proper part of the 90x90 sprite.
@@ -70,93 +144,18 @@ function cssVarMap (sprite) {
if (requiresSpecialTreatment) {
sprite.custom = {
px: {
offsetX: `-${ sprite.x + 25 }px`,
offsetY: `-${ sprite.y + 15 }px`,
offset_x: `-${ sprite.x + 25 }px`,
offset_y: `-${ sprite.y + 15 }px`,
width: '60px',
height: '60px',
},
};
}
if (sprite.name.indexOf('shirt') !== -1)
sprite.custom.px.offsetY = `-${ sprite.y + 35 }px`; // even more for shirts
if (sprite.name.indexOf('hair_base') !== -1) {
let styleArray = sprite.name.split('_').slice(2, 3);
if (~sprite.name.indexOf('shirt'))
sprite.custom.px.offset_y = `-${ sprite.y + 30 }px`; // even more for shirts
if (~sprite.name.indexOf('hair_base')) {
let styleArray = sprite.name.split('_').slice(2,3);
if (Number(styleArray[0]) > 14)
sprite.custom.px.offsetY = `-${ sprite.y }px`; // don't crop updos
sprite.custom.px.offset_y = `-${ sprite.y }px`; // don't crop updos
}
}
function createSpritesStream (name, src) {
let spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src);
let stream = mergeStream();
each(spritesheetSliceIndicies, (start, index) => {
let slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]);
let spriteData = gulp.src(slicedSrc)
.pipe(spritesmith({
imgName: `spritesmith-${name}-${index}.png`,
cssName: `spritesmith-${name}-${index}.css`,
algorithm: 'binary-tree',
padding: 1,
cssTemplate: 'website/raw_sprites/css/css.template.handlebars',
cssVarMap,
}));
let imgStream = spriteData.img
.pipe(imagemin())
.pipe(gulp.dest(IMG_DIST_PATH));
let cssStream = spriteData.css
.pipe(gulp.dest(CSS_DIST_PATH));
stream.add(imgStream);
stream.add(cssStream);
});
return stream;
}
gulp.task('sprites:compile', ['sprites:clean', 'sprites:main', 'sprites:largeSprites', 'sprites:checkCompiledDimensions']);
gulp.task('sprites:main', () => {
let mainSrc = sync('website/raw_sprites/spritesmith/**/*.png');
return createSpritesStream('main', mainSrc);
});
gulp.task('sprites:largeSprites', () => {
let largeSrc = sync('website/raw_sprites/spritesmith_large/**/*.png');
return createSpritesStream('largeSprites', largeSrc);
});
gulp.task('sprites:clean', (done) => {
clean(`${IMG_DIST_PATH}spritesmith*,${CSS_DIST_PATH}spritesmith*}`, done);
});
gulp.task('sprites:checkCompiledDimensions', ['sprites:main', 'sprites:largeSprites'], () => {
console.log('Verifiying that images do not exceed max dimensions'); // eslint-disable-line no-console
let numberOfSheetsThatAreTooBig = 0;
let distSpritesheets = sync(`${IMG_DIST_PATH}*.png`);
each(distSpritesheets, (img) => {
let spriteSize = calculateImgDimensions(img);
if (spriteSize > MAX_SPRITESHEET_SIZE) {
numberOfSheetsThatAreTooBig++;
let name = basename(img, '.png');
console.error(`WARNING: ${name} might be too big - ${spriteSize} > ${MAX_SPRITESHEET_SIZE}`); // eslint-disable-line no-console
}
});
if (numberOfSheetsThatAreTooBig > 0) {
// https://github.com/HabitRPG/habitica/pull/6683#issuecomment-185462180
console.error( // eslint-disable-line no-console
`${numberOfSheetsThatAreTooBig} sheets might too big for mobile Safari to be able to handle
them, but there is a margin of error in these calculations so it is probably okay. Mention
this to an admin so they can test a staging site on mobile Safari after your PR is merged.`);
} else {
console.log('All images are within the correct dimensions'); // eslint-disable-line no-console
}
});
+2
View File
@@ -3,6 +3,8 @@ import nodemon from 'gulp-nodemon';
let pkg = require('../package.json');
gulp.task('run:dev', ['nodemon', 'build:dev:watch']);
gulp.task('nodemon', () => {
nodemon({
script: pkg.main,
+138 -23
View File
@@ -1,12 +1,21 @@
import {
pipe,
awaitPort,
kill,
runMochaTests,
} from './taskHelper';
import { server as karma } from 'karma';
import mongoose from 'mongoose';
import { exec } from 'child_process';
import psTree from 'ps-tree';
import gulp from 'gulp';
import Bluebird from 'bluebird';
import runSequence from 'run-sequence';
import os from 'os';
import nconf from 'nconf';
import fs from 'fs';
const i18n = require('../website/server/libs/i18n');
// TODO rewrite
@@ -15,23 +24,25 @@ let server;
const TEST_DB_URI = nconf.get('TEST_DB_URI');
const API_V3_TEST_COMMAND = 'npm run test:api-v3';
const SANITY_TEST_COMMAND = 'npm run test:sanity';
const COMMON_TEST_COMMAND = 'npm run test:common';
const CONTENT_TEST_COMMAND = 'npm run test:content';
const CONTENT_OPTIONS = {maxBuffer: 1024 * 500};
const KARMA_TEST_COMMAND = 'npm run test:karma';
/* Helper methods for reporting test summary */
let testResults = [];
let testCount = (stdout, regexp) => {
let match = stdout.match(regexp);
return parseInt(match && match[1] || 0, 10);
return parseInt(match && match[1] || 0);
};
let testBin = (string, additionalEnvVariables = '') => {
if (os.platform() === 'win32') {
if (additionalEnvVariables !== '') {
if (additionalEnvVariables != '') {
additionalEnvVariables = additionalEnvVariables.split(' ').join('&&set ');
additionalEnvVariables = `set ${additionalEnvVariables}&&`;
additionalEnvVariables = 'set ' + additionalEnvVariables + '&&';
}
return `set NODE_ENV=test&&${additionalEnvVariables}${string}`;
} else {
@@ -39,9 +50,9 @@ let testBin = (string, additionalEnvVariables = '') => {
}
};
gulp.task('test:nodemon', () => {
process.env.PORT = TEST_SERVER_PORT; // eslint-disable-line no-process-env
process.env.NODE_DB_URI = TEST_DB_URI; // eslint-disable-line no-process-env
gulp.task('test:nodemon', (done) => {
process.env.PORT = TEST_SERVER_PORT;
process.env.NODE_DB_URI = TEST_DB_URI;
runSequence('nodemon');
});
@@ -58,27 +69,37 @@ gulp.task('test:prepare:mongo', (cb) => {
gulp.task('test:prepare:server', ['test:prepare:mongo'], () => {
if (!server) {
server = exec(testBin('node ./website/server/index.js', `NODE_DB_URI=${TEST_DB_URI} PORT=${TEST_SERVER_PORT}`), (error, stdout, stderr) => {
if (error) {
throw new Error(`Problem with the server: ${error}`);
}
if (stderr) {
console.error(stderr); // eslint-disable-line no-console
}
if (error) { throw `Problem with the server: ${error}`; }
if (stderr) { console.error(stderr); }
});
}
});
gulp.task('test:prepare:build', ['build']);
gulp.task('test:prepare:translations', (cb) => {
fs.writeFile(
'test/client-old/spec/mocks/translations.js',
`if(!window.env) window.env = {};
window.env.translations = ${JSON.stringify(i18n.translations['en'])};`, cb);
});
gulp.task('test:prepare:build', ['build', 'test:prepare:translations']);
// exec(testBin('grunt build:test'), cb);
gulp.task('test:prepare:webdriver', (cb) => {
exec('npm run test:prepare:webdriver', cb);
});
gulp.task('test:prepare', [
'test:prepare:build',
'test:prepare:mongo',
'test:prepare:webdriver',
]);
gulp.task('test:sanity', (cb) => {
let runner = exec(
testBin(SANITY_TEST_COMMAND),
(err) => {
(err, stdout, stderr) => {
if (err) {
process.exit(1);
}
@@ -91,7 +112,7 @@ gulp.task('test:sanity', (cb) => {
gulp.task('test:common', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(COMMON_TEST_COMMAND),
(err) => {
(err, stdout, stderr) => {
if (err) {
process.exit(1);
}
@@ -112,7 +133,7 @@ gulp.task('test:common:watch', ['test:common:clean'], () => {
gulp.task('test:common:safe', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(COMMON_TEST_COMMAND),
(err, stdout) => { // eslint-disable-line handle-callback-err
(err, stdout, stderr) => {
testResults.push({
suite: 'Common Specs\t',
pass: testCount(stdout, /(\d+) passing/),
@@ -129,7 +150,7 @@ gulp.task('test:content', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(CONTENT_TEST_COMMAND),
CONTENT_OPTIONS,
(err) => {
(err, stdout, stderr) => {
if (err) {
process.exit(1);
}
@@ -151,7 +172,7 @@ gulp.task('test:content:safe', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(CONTENT_TEST_COMMAND),
CONTENT_OPTIONS,
(err, stdout) => { // eslint-disable-line handle-callback-err
(err, stdout, stderr) => {
testResults.push({
suite: 'Content Specs\t',
pass: testCount(stdout, /(\d+) passing/),
@@ -164,10 +185,103 @@ gulp.task('test:content:safe', ['test:prepare:build'], (cb) => {
pipe(runner);
});
gulp.task('test:karma', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(KARMA_TEST_COMMAND),
(err, stdout) => {
if (err) {
process.exit(1);
}
cb();
}
);
pipe(runner);
});
gulp.task('test:karma:watch', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(`${KARMA_TEST_COMMAND}:watch`),
(err, stdout) => {
cb(err);
}
);
pipe(runner);
});
gulp.task('test:karma:safe', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(KARMA_TEST_COMMAND),
(err, stdout) => {
testResults.push({
suite: 'Karma Specs\t',
pass: testCount(stdout, /(\d+) tests? completed/),
fail: testCount(stdout, /(\d+) tests? failed/),
pend: testCount(stdout, /(\d+) tests? skipped/),
});
cb();
}
);
pipe(runner);
});
gulp.task('test:e2e', ['test:prepare', 'test:prepare:server'], (cb) => {
let support = [
'Xvfb :99 -screen 0 1024x768x24 -extension RANDR',
testBin('npm run test:e2e:webdriver', 'DISPLAY=:99'),
].map(exec);
support.push(server);
Bluebird.all([
awaitPort(TEST_SERVER_PORT),
awaitPort(4444),
]).then(() => {
let runner = exec(
'npm run test:e2e',
(err, stdout, stderr) => {
support.forEach(kill);
if (err) {
process.exit(1);
}
cb();
}
);
pipe(runner);
});
});
gulp.task('test:e2e:safe', ['test:prepare', 'test:prepare:server'], (cb) => {
let support = [
'Xvfb :99 -screen 0 1024x768x24 -extension RANDR',
'npm run test:e2e:webdriver',
].map(exec);
Bluebird.all([
awaitPort(TEST_SERVER_PORT),
awaitPort(4444),
]).then(() => {
let runner = exec(
'npm run test:e2e',
(err, stdout, stderr) => {
let match = stdout.match(/(\d+) tests?.*(\d) failures?/);
testResults.push({
suite: 'End-to-End Specs\t',
pass: testCount(stdout, /(\d+) passing/),
fail: testCount(stdout, /(\d+) failing/),
pend: testCount(stdout, /(\d+) pending/),
});
support.forEach(kill);
cb();
}
);
pipe(runner);
});
});
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'),
(err) => {
testBin('mocha test/api/v3/unit --recursive --require ./test/helpers/start-server'),
(err, stdout, stderr) => {
if (err) {
process.exit(1);
}
@@ -184,9 +298,9 @@ 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) => {
(err, stdout, stderr) => {
if (err) {
process.exit(1);
}
@@ -206,7 +320,7 @@ gulp.task('test:api-v3:integration:separate-server', (done) => {
let runner = exec(
testBin('mocha test/api/v3/integration --recursive --require ./test/helpers/start-server', 'LOAD_SERVER=0'),
{maxBuffer: 500 * 1024},
(err) => done(err)
(err, stdout, stderr) => done(err)
);
pipe(runner);
@@ -217,6 +331,7 @@ gulp.task('test', (done) => {
'test:sanity',
'test:content',
'test:common',
'test:karma',
'test:api-v3:unit',
'test:api-v3:integration',
done
+85 -82
View File
@@ -1,5 +1,6 @@
import fs from 'fs';
import _ from 'lodash';
import nconf from 'nconf';
import gulp from 'gulp';
import { postToSlack, conf } from './taskHelper';
@@ -11,82 +12,8 @@ const SLACK_CONFIG = {
const LOCALES = './website/common/locales/';
const ENGLISH_LOCALE = `${LOCALES}en/`;
function getArrayOfLanguages () {
let languages = fs.readdirSync(LOCALES);
languages.shift(); // Remove README.md from array of languages
return languages;
}
const ALL_LANGUAGES = getArrayOfLanguages();
function stripOutNonJsonFiles (collection) {
let onlyJson = _.filter(collection, (file) => {
return file.match(/[a-zA-Z]*\.json/);
});
return onlyJson;
}
function eachTranslationFile (languages, cb) {
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
_.each(languages, (lang) => {
_.each(jsonFiles, (filename) => {
let parsedTranslationFile;
try {
const translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`);
parsedTranslationFile = JSON.parse(translationFile);
} catch (err) {
return cb(err);
}
let englishFile = fs.readFileSync(ENGLISH_LOCALE + filename);
let parsedEnglishFile = JSON.parse(englishFile);
cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile);
});
});
}
function eachTranslationString (languages, cb) {
eachTranslationFile(languages, (error, language, filename, englishJSON, translationJSON) => {
if (error) return;
_.each(englishJSON, (string, key) => {
const translationString = translationJSON[key];
cb(language, filename, key, string, translationString);
});
});
}
function formatMessageForPosting (msg, items) {
let body = `*Warning:* ${msg}`;
body += '\n\n```\n';
body += items.join('\n');
body += '\n```';
return body;
}
function getStringsWith (json, interpolationRegex) {
let strings = {};
_.each(json, (fileName) => {
const rawFile = fs.readFileSync(ENGLISH_LOCALE + fileName);
const parsedJson = JSON.parse(rawFile);
strings[fileName] = {};
_.each(parsedJson, (value, key) => {
const match = value.match(interpolationRegex);
if (match) strings[fileName][key] = match;
});
});
return strings;
}
const malformedStringExceptions = {
messageDropFood: true,
armoireFood: true,
@@ -96,6 +23,7 @@ const malformedStringExceptions = {
gulp.task('transifex', ['transifex:missingFiles', 'transifex:missingStrings', 'transifex:malformedStrings']);
gulp.task('transifex:missingFiles', () => {
let missingStrings = [];
eachTranslationFile(ALL_LANGUAGES, (error) => {
@@ -112,6 +40,7 @@ gulp.task('transifex:missingFiles', () => {
});
gulp.task('transifex:missingStrings', () => {
let missingStrings = [];
eachTranslationString(ALL_LANGUAGES, (language, filename, key, englishString, translationString) => {
@@ -129,6 +58,7 @@ gulp.task('transifex:missingStrings', () => {
});
gulp.task('transifex:malformedStrings', () => {
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
let interpolationRegex = /<%= [a-zA-Z]* %>/g;
let stringsToLookFor = getStringsWith(jsonFiles, interpolationRegex);
@@ -136,28 +66,30 @@ gulp.task('transifex:malformedStrings', () => {
let stringsWithMalformedInterpolations = [];
let stringsWithIncorrectNumberOfInterpolations = [];
_.each(ALL_LANGUAGES, (lang) => {
_.each(stringsToLookFor, (strings, filename) => {
let translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`);
let count = 0;
_(ALL_LANGUAGES).each(function (lang) {
_.each(stringsToLookFor, function (strings, file) {
let translationFile = fs.readFileSync(LOCALES + lang + '/' + file);
let parsedTranslationFile = JSON.parse(translationFile);
_.each(strings, (value, key) => { // eslint-disable-line max-nested-callbacks
_.each(strings, function (value, key) {
let translationString = parsedTranslationFile[key];
if (!translationString) return;
let englishOccurences = stringsToLookFor[filename][key];
let englishOccurences = stringsToLookFor[file][key];
let translationOccurences = translationString.match(interpolationRegex);
if (!translationOccurences) {
let malformedString = `${lang} - ${filename} - ${key} - ${translationString}`;
let malformedString = `${lang} - ${file} - ${key} - ${translationString}`;
stringsWithMalformedInterpolations.push(malformedString);
} else if (englishOccurences.length !== translationOccurences.length && !malformedStringExceptions[key]) {
let missingInterpolationString = `${lang} - ${filename} - ${key} - ${translationString}`;
let missingInterpolationString = `${lang} - ${file} - ${key} - ${translationString}`;
stringsWithIncorrectNumberOfInterpolations.push(missingInterpolationString);
}
});
});
});
}).value();
if (!_.isEmpty(stringsWithMalformedInterpolations)) {
let message = 'The following strings have malformed or missing interpolations';
@@ -171,3 +103,74 @@ gulp.task('transifex:malformedStrings', () => {
postToSlack(formattedMessage, SLACK_CONFIG);
}
});
function getArrayOfLanguages () {
let languages = fs.readdirSync(LOCALES);
languages.shift(); // Remove README.md from array of languages
return languages;
}
function eachTranslationFile (languages, cb) {
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
_(languages).each((lang) => {
_.each(jsonFiles, (filename) => {
try {
var translationFile = fs.readFileSync(LOCALES + lang + '/' + filename);
var parsedTranslationFile = JSON.parse(translationFile);
} catch (err) {
return cb(err);
}
let englishFile = fs.readFileSync(ENGLISH_LOCALE + filename);
let parsedEnglishFile = JSON.parse(englishFile);
cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile);
});
}).value();
}
function eachTranslationString (languages, cb) {
eachTranslationFile(languages, (error, language, filename, englishJSON, translationJSON) => {
if (error) return;
_.each(englishJSON, (string, key) => {
var translationString = translationJSON[key];
cb(language, filename, key, string, translationString);
});
});
}
function formatMessageForPosting (msg, items) {
let body = `*Warning:* ${msg}`;
body += '\n\n```\n';
body += items.join('\n');
body += '\n```';
return body;
}
function getStringsWith (json, interpolationRegex) {
var strings = {};
_(json).each(function (file_name) {
var raw_file = fs.readFileSync(ENGLISH_LOCALE + file_name);
var parsed_json = JSON.parse(raw_file);
strings[file_name] = {};
_.each(parsed_json, function (value, key) {
var match = value.match(interpolationRegex);
if (match) strings[file_name][key] = match;
});
}).value();
return strings;
}
function stripOutNonJsonFiles (collection) {
let onlyJson = _.filter(collection, (file) => {
return file.match(/[a-zA-Z]*\.json/);
});
return onlyJson;
}
+20 -29
View File
@@ -12,7 +12,7 @@ import { resolve } from 'path';
* Get access to configruable values
*/
nconf.argv().env().file({ file: 'config.json' });
export const conf = nconf;
export var conf = nconf;
/*
* Kill a child process and any sub-children that process may have spawned.
@@ -26,12 +26,11 @@ export function kill (proc) {
pids.forEach(kill); return;
}
try {
exec(/^win/.test(process.platform) ?
`taskkill /PID ${pid} /T /F` :
`kill -9 ${pid}`);
} catch (e) {
console.log(e); // eslint-disable-line no-console
exec(/^win/.test(process.platform)
? `taskkill /PID ${pid} /T /F`
: `kill -9 ${pid}`);
}
catch (e) { console.log(e); }
});
};
@@ -45,25 +44,21 @@ export function kill (proc) {
* before failing.
*/
export function awaitPort (port, max = 60) {
return new Bluebird((rej, res) => {
let socket;
let timeout;
let interval;
return new Bluebird((reject, resolve) => {
let socket, timeout, interval;
timeout = setTimeout(() => {
clearInterval(interval);
rej(`Timed out after ${max} seconds`);
reject(`Timed out after ${max} seconds`);
}, max * 1000);
interval = setInterval(() => {
socket = net.connect({port}, () => {
socket = net.connect({port: port}, () => {
clearInterval(interval);
clearTimeout(timeout);
socket.destroy();
res();
}).on('error', () => {
socket.destroy();
});
resolve();
}).on('error', () => { socket.destroy; });
}, 1000);
});
}
@@ -72,12 +67,8 @@ export function awaitPort (port, max = 60) {
* Pipe the child's stdin and stderr to the parent process.
*/
export function pipe (child) {
child.stdout.on('data', (data) => {
process.stdout.write(data);
});
child.stderr.on('data', (data) => {
process.stderr.write(data);
});
child.stdout.on('data', (data) => { process.stdout.write(data); });
child.stderr.on('data', (data) => { process.stderr.write(data); });
}
/*
@@ -87,8 +78,8 @@ export function postToSlack (msg, config = {}) {
let slackUrl = nconf.get('SLACK_URL');
if (!slackUrl) {
console.error('No slack post url specified. Your message was:'); // eslint-disable-line no-console
console.log(msg); // eslint-disable-line no-console
console.error('No slack post url specified. Your message was:');
console.log(msg);
return;
}
@@ -98,15 +89,15 @@ export function postToSlack (msg, config = {}) {
channel: `#${config.channel || '#general'}`,
username: config.username || 'gulp task',
text: msg,
icon_emoji: `:${config.emoji || 'gulp'}:`, // eslint-disable-line camelcase
icon_emoji: `:${config.emoji || 'gulp'}:`,
})
.end((err) => {
if (err) console.error('Unable to post to slack', err); // eslint-disable-line no-console
.end((err, res) => {
if (err) console.error('Unable to post to slack', err);
});
}
export function runMochaTests (files, server, cb) {
require('../test/helpers/globals.helper'); // eslint-disable-line global-require
require('../test/helpers/globals.helper');
let mocha = new Mocha({reporter: 'spec'});
let tests = glob(files);
@@ -117,7 +108,7 @@ export function runMochaTests (files, server, cb) {
});
mocha.run((numberOfFailures) => {
if (!process.env.RUN_INTEGRATION_TEST_FOREVER) { // eslint-disable-line no-process-env
if (!process.env.RUN_INTEGRATION_TEST_FOREVER) {
if (server) kill(server);
process.exit(numberOfFailures);
}
+8 -6
View File
@@ -8,11 +8,13 @@
require('babel-register');
if (process.env.NODE_ENV === 'production') { // eslint-disable-line no-process-env
require('./gulp/gulp-apidoc'); // eslint-disable-line global-require
require('./gulp/gulp-build'); // eslint-disable-line global-require
require('./gulp/gulp-bootstrap'); // eslint-disable-line global-require
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');
} else {
require('glob').sync('./gulp/gulp-*').forEach(require); // eslint-disable-line global-require
require('gulp').task('default', ['test']); // eslint-disable-line global-require
require('glob').sync('./gulp/gulp-*').forEach(require);
require('gulp').task('default', ['test']);
}
-60
View File
@@ -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....
-25
View File
@@ -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
-14
View File
@@ -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
-22
View File
@@ -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
-13
View File
@@ -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
-28
View File
@@ -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
-7
View File
@@ -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,
-6
View File
@@ -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'},
-6
View File
@@ -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} );
-6
View File
@@ -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');
-6
View File
@@ -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');
-6
View File
@@ -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
*/
-6
View File
@@ -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}},
-6
View File
@@ -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');
-6
View File
@@ -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');
-6
View File
@@ -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');
-6
View File
@@ -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;
-207
View File
@@ -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;
-114
View File
@@ -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;
-90
View File
@@ -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;
-109
View File
@@ -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;
-97
View File
@@ -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 = '![Guild Logo](' + group.logo + ')\n\n&nbsp;\n\n' + description;
}
if (group.leaderMessage) {
description = description + '\n\n&nbsp;\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;
-128
View File
@@ -1,128 +0,0 @@
import { selectGearToPin } from '../website/common/script/ops/pinnedGearUtils';
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 = selectGearToPin(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;
-111
View File
@@ -1,111 +0,0 @@
var migrationName = '20171030_jackolanterns.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 the Jack-O'-Lantern ladder:
* Ghost Jack-O-Lantern Mount to owners of Ghost Jack-O-Lantern Pet
* Ghost Jack-O-Lantern Pet to owners of Jack-O-Lantern Mount
* Jack-O-Lantern Mount to owners of Jack-O-Lantern Pet
* Jack-O-Lantern Pet 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.pets',
'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 = {};
var inc = {
'items.food.Candy_Skeleton': 1,
'items.food.Candy_Base': 1,
'items.food.Candy_CottonCandyBlue': 1,
'items.food.Candy_CottonCandyPink': 1,
'items.food.Candy_Shade': 1,
'items.food.Candy_White': 1,
'items.food.Candy_Golden': 1,
'items.food.Candy_Zombie': 1,
'items.food.Candy_Desert': 1,
'items.food.Candy_Red': 1,
};
if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Ghost']) {
set = {'migration':migrationName, 'items.mounts.JackOLantern-Ghost': true};
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Base']) {
set = {'migration':migrationName, 'items.pets.JackOLantern-Ghost': 5};
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Base']) {
set = {'migration':migrationName, 'items.mounts.JackOLantern-Base': true};
} else {
set = {'migration':migrationName, 'items.pets.JackOLantern-Base': 5};
}
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;
-6
View File
@@ -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');
-6
View File
@@ -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');
-6
View File
@@ -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');
-6
View File
@@ -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');
-6
View File
@@ -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;
+1 -1
View File
@@ -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);
};
-32
View File
@@ -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)
};
-46
View File
@@ -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;
-3
View File
@@ -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')));
+21 -24
View File
@@ -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();
+1 -1
View File
@@ -2,7 +2,7 @@ var _id = '';
var update = {
$addToSet: {
'purchased.plan.mysteryItems':{
$each:['armor_mystery_201710','head_mystery_201710']
$each:['head_mystery_201702','back_mystery_201702']
}
}
};
-98
View File
@@ -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);
});
+3 -13
View File
@@ -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) {
@@ -65,29 +65,19 @@ function updateUser (user) {
set = {'migration':migrationName};
} else if (typeof user.items.gear.owned.body_special_takeThis !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.back_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.back_special_takeThis', '_id': monk.id()}};
} else if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.body_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.body_special_takeThis', '_id': monk.id()}};
} else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_takeThis', '_id': monk.id()}};
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_takeThis', '_id': monk.id()}};
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.weapon_special_takeThis', '_id': monk.id()}};
} else {
set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.shield_special_takeThis', '_id': monk.id()}};
}
if (push) {
dbUsers.update({_id: user._id}, {$set: set, $push: push});
} else {
dbUsers.update({_id: user._id}, {$set: set});
}
dbUsers.update({_id: user._id}, {$set:set});
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
if (user._id == authorUuid) console.warn(authorName + ' processed');
@@ -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;
-109
View File
@@ -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();
};
+13195
View File
File diff suppressed because it is too large Load Diff
-19038
View File
File diff suppressed because it is too large Load Diff
+51 -51
View File
@@ -1,7 +1,7 @@
{
"name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
"version": "4.9.1",
"version": "3.78.0",
"main": "./website/server/index.js",
"dependencies": {
"@slack/client": "^3.8.1",
@@ -13,13 +13,10 @@
"async": "^1.5.0",
"autoprefixer": "^6.4.0",
"aws-sdk": "^2.0.25",
"axios": "^0.16.0",
"axios-progress-bar": "^0.1.7",
"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",
@@ -31,55 +28,62 @@
"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.7",
"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",
"file-loader": "^0.10.0",
"glob": "^4.3.5",
"got": "^6.1.1",
"grunt": "~0.4.1",
"grunt-cli": "~0.1.9",
"grunt-contrib-clean": "~0.6.0",
"grunt-contrib-copy": "~0.6.0",
"grunt-contrib-cssmin": "~0.10.0",
"grunt-contrib-stylus": "~0.20.0",
"grunt-contrib-uglify": "~0.6.0",
"grunt-contrib-watch": "~0.6.1",
"grunt-hashres": "~0.4.1",
"gulp": "^3.9.0",
"gulp-babel": "^6.1.2",
"gulp-grunt": "^0.5.2",
"gulp-imagemin": "^2.4.0",
"gulp-nodemon": "^2.0.4",
"gulp-sourcemaps": "^1.6.0",
"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",
@@ -89,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",
@@ -116,47 +115,48 @@
"validator": "^4.9.0",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
"vue": "git://github.com/habitrpg/vue#e8f45fcfc98ed1859669f3a6a8b23ae28bf4d46f",
"vue-loader": "^13.3.0",
"vue-mugen-scroll": "^0.2.1",
"vue-router": "^3.0.0",
"vue-style-loader": "^3.0.0",
"vue-template-compiler": "^2.5.2",
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker#45e607a7bccf4e3e089761b3b7b33e3f2c5dc21f",
"vue": "^2.1.0",
"vue-loader": "^11.0.0",
"vue-router": "^2.0.0-rc.5",
"vue-style-loader": "^2.0.0",
"vue-template-compiler": "^2.1.10",
"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 && 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",
"test:e2e:webdriver": "webdriver-manager start",
"test:e2e": "protractor test/client-old/e2e/protractor.conf.js",
"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 nodemon",
"postinstall": "gulp build",
"apidoc": "gulp apidoc"
"start": "gulp run:dev",
"postinstall": "bower --config.interactive=false install -f; gulp build; npm run client:build"
},
"devDependencies": {
"babel-plugin-istanbul": "^4.0.0",
@@ -166,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",
@@ -178,9 +179,10 @@
"event-stream": "^3.2.2",
"eventsource-polyfill": "^0.9.6",
"expect.js": "~0.2.0",
"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",
@@ -188,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",
+2 -19
View File
@@ -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
View File
@@ -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).
+123
View File
@@ -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)!
+4
View File
@@ -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,
@@ -142,22 +142,4 @@ describe('GET /challenges/:challengeId/members', () => {
let resIds = res.concat(res2).map(member => member._id);
expect(resIds).to.eql(expectedIds.sort());
});
it('supports using req.query.search to get search members', async () => {
let group = await generateGroup(user, {type: 'party', name: generateUUID()});
let challenge = await generateChallenge(user, group);
let usersToGenerate = [];
for (let i = 0; i < 3; i++) {
usersToGenerate.push(generateUser({challenges: [challenge._id]}));
}
let generatedUsers = await Promise.all(usersToGenerate);
let profileNames = generatedUsers.map(generatedUser => generatedUser.profile.name);
let firstProfileName = profileNames[0];
let nameToSearch = firstProfileName.substring(0, 4);
let response = await user.get(`/challenges/${challenge._id}/members?search=${nameToSearch}`);
expect(response[0].profile.name).to.eql(firstProfileName);
});
});

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