mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-10-27 11:12:28 +01:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fd5de1bf7 | ||
|
|
bd902e14cf | ||
|
|
3a0878c9a1 | ||
|
|
9b37f1e538 | ||
|
|
5774ac23e8 |
16
.babelrc
16
.babelrc
@@ -1,12 +1,10 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"node": true
|
||||
}
|
||||
}
|
||||
]
|
||||
"presets": ["es2015"],
|
||||
"plugins": [
|
||||
"transform-object-rest-spread",
|
||||
["transform-async-to-module-method", {
|
||||
"module": "bluebird",
|
||||
"method": "coroutine"
|
||||
}]
|
||||
]
|
||||
}
|
||||
|
||||
3
.bowerrc
Normal file
3
.bowerrc
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"directory": "website/client-old/bower_components"
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
https://github.com/heroku/heroku-buildpack-nodejs.git
|
||||
https://github.com/heroku/heroku-buildpack-nodejs.git
|
||||
https://github.com/stomita/heroku-buildpack-phantomjs.git
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
node_modules
|
||||
.git
|
||||
website
|
||||
|
||||
@@ -20,7 +20,11 @@ container_commands:
|
||||
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"
|
||||
|
||||
@@ -3,15 +3,18 @@ coverage/
|
||||
database_reports/
|
||||
website/build/
|
||||
website/transpiled-babel/
|
||||
# Has its own linter
|
||||
website/client/
|
||||
website/common/transpiled-babel/
|
||||
dist/
|
||||
dist-client/
|
||||
apidoc/html/
|
||||
content_cache/
|
||||
i18n_cache/
|
||||
node_modules/
|
||||
|
||||
# Old migrations, disabled
|
||||
migrations/archive/*
|
||||
# Not linted
|
||||
website/client-old/
|
||||
test/client-old/spec/**/*
|
||||
|
||||
# Temporarilly disabled. These should be removed when the linting errors are fixed TODO
|
||||
migrations/*
|
||||
scripts/*
|
||||
website/common/browserify.js
|
||||
Gruntfile.js
|
||||
gulpfile.js
|
||||
gulp
|
||||
10
.eslintrc
Normal file
10
.eslintrc
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true,
|
||||
},
|
||||
"extends": [
|
||||
"habitrpg",
|
||||
"habitrpg/esnext"
|
||||
],
|
||||
}
|
||||
20
.eslintrc.js
20
.eslintrc.js
@@ -1,20 +0,0 @@
|
||||
/* eslint-disable import/no-commonjs */
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'habitrpg/lib/node',
|
||||
],
|
||||
rules: {
|
||||
'prefer-regex-literals': 'warn',
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
'require-await': 'error',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['migrations/**', 'gulp/**'], // Or *.test.js
|
||||
rules: {
|
||||
'require-await': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
6
.github/CONTRIBUTING.md
vendored
6
.github/CONTRIBUTING.md
vendored
@@ -4,12 +4,12 @@
|
||||
|
||||
# Pull Request
|
||||
|
||||
[Please see these instructions for adding a pull request](https://habitica.fandom.com/wiki/Using_Your_Local_Install_to_Modify_Habitica's_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
|
||||
|
||||
Habitica uses [this Google form](https://docs.google.com/forms/d/e/1FAIpQLScPhrwq_7P1C6PTrI3lbvTsvqGyTNnGzp1ugi1Ml0PFee_p5g/viewform?usp=sf_link) to track feature requests. Please post there rather than creating an issue.
|
||||
Habitica uses [Trello](https://trello.com/b/EpoYEYod/habitica) to track feature requests. [Read more](https://trello.com/c/odmhIqyW/440-read-first-table-of-contents).
|
||||
|
||||
# Contributing Code
|
||||
|
||||
See [Contributing to Habitica](https://habitica.fandom.com/wiki/Contributing_to_Habitica#Coders_.28Web_.26_Mobile.29)
|
||||
See [Contributing to Habitica](http://habitica.wikia.com/wiki/Contributing_to_Habitica#Coders_.28Web_.26_Mobile.29)
|
||||
|
||||
15
.github/ISSUE_TEMPLATE.md
vendored
15
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,19 +1,20 @@
|
||||
[//]: # (Before logging this issue, please contact site administrators from "Report a Bug" in the Habitica website's Help menu. If a GitHub issue is needed, staff will let you know and will most likely log one on your behalf. It is recommended that you don't create a new issue unless advised to.)
|
||||
[//]: # (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.)
|
||||
|
||||
[//]: # (Bugs in the mobile apps can be reported via Menu > About > Support.)
|
||||
[//]: # (Bugs in the mobile apps can also be reported there.)
|
||||
|
||||
[//]: # (If you have a feature request, use "Help > Request a Feature", not GitHub.)
|
||||
[//]: # (If you have a feature request, use "Help > Request a Feature", not GitHub or the Report a Bug guild.)
|
||||
|
||||
[//]: # (For more guidelines see https://github.com/HabitRPG/habitica/issues/2760)
|
||||
|
||||
[//]: # (Fill out relevant information - UUID is found from the Habitica website at User Icon > Settings > API)
|
||||
[//]: # (Fill out relevant information - UUID is found in Settings -> API)
|
||||
### General Info
|
||||
* UUID:
|
||||
* Browser:
|
||||
* OS:
|
||||
* UUID:
|
||||
* Browser:
|
||||
* OS:
|
||||
|
||||
### Description
|
||||
[//]: # (Describe bug in detail here. Include screenshots if helpful.)
|
||||
|
||||
#### Console Errors
|
||||
[//]: # (Include any JavaScript console errors here.)
|
||||
|
||||
|
||||
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,14 +1,14 @@
|
||||
[//]: # (Note: See https://habitica.fandom.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 # here, if applicable. This will automatically close the issue if your PR is merged in)
|
||||
Fixes put_#_and_issue_number_here
|
||||
[//]: # (Put Issue # or URL here, if applicable. This will automatically close the issue if your PR is merged in)
|
||||
Fixes put_issue_url_here
|
||||
|
||||
### Changes
|
||||
[//]: # (Describe the changes that were made in detail here. Include pictures if necessary)
|
||||
|
||||
|
||||
|
||||
[//]: # (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:
|
||||
UUID:
|
||||
|
||||
273
.github/workflows/test.yml
vendored
273
.github/workflows/test.yml
vendored
@@ -1,273 +0,0 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'phillip/**'
|
||||
- 'sabrecat/**'
|
||||
- 'kalista/**'
|
||||
- 'natalie/**'
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [21.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: sudo apt update
|
||||
- run: sudo apt -y install libkrb5-dev
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm i
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: test
|
||||
- run: npm run lint-no-fix
|
||||
apidoc:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [21.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: sudo apt update
|
||||
- run: sudo apt -y install libkrb5-dev
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm i
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: test
|
||||
- run: npm run apidoc
|
||||
sanity:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [21.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: sudo apt update
|
||||
- run: sudo apt -y install libkrb5-dev
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm i
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: test
|
||||
- run: npm run test:sanity
|
||||
|
||||
common:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [21.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: sudo apt update
|
||||
- run: sudo apt -y install libkrb5-dev
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm i
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: test
|
||||
- run: npm run test:common
|
||||
content:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [21.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: sudo apt update
|
||||
- run: sudo apt -y install libkrb5-dev
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm i
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: test
|
||||
- run: npm run test:content
|
||||
|
||||
api-unit:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [21.x]
|
||||
mongodb-version: [4.2]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Start MongoDB ${{ matrix.mongodb-version }} Replica Set
|
||||
uses: supercharge/mongodb-github-action@1.3.0
|
||||
with:
|
||||
mongodb-version: ${{ matrix.mongodb-version }}
|
||||
mongodb-replica-set: rs
|
||||
- run: sudo apt update
|
||||
- run: sudo apt -y install libkrb5-dev
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm i
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: test
|
||||
- run: npm run test:api:unit
|
||||
env:
|
||||
REQUIRES_SERVER=true: true
|
||||
api-v3-integration:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [21.x]
|
||||
mongodb-version: [4.2]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Start MongoDB ${{ matrix.mongodb-version }} Replica Set
|
||||
uses: supercharge/mongodb-github-action@1.3.0
|
||||
with:
|
||||
mongodb-version: ${{ matrix.mongodb-version }}
|
||||
mongodb-replica-set: rs
|
||||
- run: sudo apt update
|
||||
- run: sudo apt -y install libkrb5-dev
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm i
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: test
|
||||
- run: npm run test:api-v3:integration
|
||||
env:
|
||||
REQUIRES_SERVER=true: true
|
||||
api-v4-integration:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [21.x]
|
||||
mongodb-version: [4.2]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Start MongoDB ${{ matrix.mongodb-version }} Replica Set
|
||||
uses: supercharge/mongodb-github-action@1.3.0
|
||||
with:
|
||||
mongodb-version: ${{ matrix.mongodb-version }}
|
||||
mongodb-replica-set: rs
|
||||
- run: sudo apt update
|
||||
- run: sudo apt -y install libkrb5-dev
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm i
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: test
|
||||
- run: npm run test:api-v4:integration
|
||||
env:
|
||||
REQUIRES_SERVER=true: true
|
||||
|
||||
client-unit:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [21.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: sudo apt update
|
||||
- run: sudo apt -y install libkrb5-dev
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm i
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: test
|
||||
- run: npm run test:unit
|
||||
working-directory: ./website/client
|
||||
|
||||
production-build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [21.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: sudo apt update
|
||||
- run: sudo apt -y install libkrb5-dev
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm install
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: production
|
||||
30
.gitignore
vendored
30
.gitignore
vendored
@@ -1,16 +1,19 @@
|
||||
.DS_Store
|
||||
website/build
|
||||
website/client-old/gen
|
||||
website/client-old/common
|
||||
website/client-old/apidoc
|
||||
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
|
||||
i18n_cache
|
||||
apidoc/html
|
||||
*.swp
|
||||
.idea*
|
||||
config*.json
|
||||
config.json
|
||||
npm-debug.log*
|
||||
lib
|
||||
website/client-old/bower_components
|
||||
website/client-old/new-stuff.html
|
||||
newrelic_agent.log
|
||||
.bower-tmp
|
||||
.bower-registry
|
||||
@@ -21,31 +24,20 @@ TODO
|
||||
*.log
|
||||
src/*/*.map
|
||||
src/*/*/*.map
|
||||
test/*.js
|
||||
test/*.map
|
||||
website/client-old/docs
|
||||
*.sublime-workspace
|
||||
coverage
|
||||
coverage.html
|
||||
common/dist/scripts/*
|
||||
dist
|
||||
dist-client
|
||||
website/client/dist
|
||||
test/client/unit/coverage
|
||||
test/client/e2e/reports
|
||||
test/client-old/spec/mocks/translations.js
|
||||
yarn.lock
|
||||
.gitattributes
|
||||
|
||||
# Elastic Beanstalk Files
|
||||
.elasticbeanstalk/*
|
||||
!.elasticbeanstalk/*.cfg.yml
|
||||
!.elasticbeanstalk/*.global.yml
|
||||
|
||||
/.vscode
|
||||
habitica.code-workspace
|
||||
|
||||
# webstorm fake webpack for path intellisense
|
||||
webpack.webstorm.config
|
||||
|
||||
# mongodb replica set for local dev
|
||||
mongodb-*.tgz
|
||||
/mongodb-data*
|
||||
/.nyc_output
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "habitica-images"]
|
||||
path = habitica-images
|
||||
url = https://github.com/HabitRPG/habitica-images
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
DEVELOPER="someone"
|
||||
if git rev-parse --git-dir > /dev/null 2>&1; then
|
||||
DEVELOPERS=$(git log -5 --pretty=format:'%an')
|
||||
IFS=$'\n'
|
||||
DEVELOPER=""
|
||||
for dev in $DEVELOPERS
|
||||
do
|
||||
if [ "$DEVELOPER" == "someone" ]; then
|
||||
if [[ ${dev} != *"[bot]"* ]]; then
|
||||
DEVELOPER=$dev
|
||||
continue
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
PARTS=$(cut -d"." -f1 <<< $BASE_URL)
|
||||
SERVER_NAME=$(cut -d"/" -f3 <<< ${PARTS[0]})
|
||||
|
||||
SERVER_NAME=":$SERVER_EMOJI: $SERVER_NAME"
|
||||
|
||||
wget $SLACK_DEPLOY_URL --post-data="{\"server_name\": \"$SERVER_NAME\", \"developer\": \"$DEVELOPER\", \"base_url\": \"$BASE_URL\"}" -O /dev/null
|
||||
@@ -1,12 +1,18 @@
|
||||
node_modules/**
|
||||
content_cache
|
||||
content_cache/**
|
||||
.bower-cache/**
|
||||
.bower-tmp/**
|
||||
.bower-registry/**
|
||||
website/client-old/**
|
||||
website/client/**
|
||||
website/views/**
|
||||
website/build/**
|
||||
dist/**
|
||||
test/**
|
||||
.git/**
|
||||
Gruntfile.js
|
||||
CHANGELOG.md
|
||||
.idea*
|
||||
*.log
|
||||
newrelic_agent.log
|
||||
*.swp
|
||||
*.swx
|
||||
website/raw_sprites/**
|
||||
content_cache/**
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# Files not included in deployments to Heroku, to save on file size.
|
||||
|
||||
/habitica-images
|
||||
/test
|
||||
/migrations
|
||||
/scripts
|
||||
/database_reports
|
||||
38
.travis.yml
Normal file
38
.travis.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- '6'
|
||||
sudo: required
|
||||
dist: precise
|
||||
services:
|
||||
- mongodb
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
before_install:
|
||||
- $CXX --version
|
||||
- npm install -g npm@5
|
||||
- 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
|
||||
env:
|
||||
global:
|
||||
- CXX=g++-4.8
|
||||
- DISABLE_REQUEST_LOGGING=true
|
||||
matrix:
|
||||
- TEST="lint"
|
||||
- TEST="test:api-v3" REQUIRES_SERVER=true COVERAGE=true
|
||||
- TEST="test:sanity"
|
||||
- TEST="test:content" COVERAGE=true
|
||||
- TEST="test:common" COVERAGE=true
|
||||
- TEST="client:unit" COVERAGE=true
|
||||
- TEST="apidoc"
|
||||
22
Dockerfile
Normal file
22
Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
||||
FROM node:boron
|
||||
|
||||
# Upgrade NPM to v5 (Yarn is needed because of this bug https://github.com/npm/npm/issues/16807)
|
||||
# The used solution is suggested here https://github.com/npm/npm/issues/16807#issuecomment-313591975
|
||||
RUN yarn global add npm@5
|
||||
# Install global packages
|
||||
RUN npm install -g gulp grunt-cli bower mocha
|
||||
|
||||
# Clone Habitica repo and install dependencies
|
||||
RUN mkdir -p /usr/src/habitrpg
|
||||
WORKDIR /usr/src/habitrpg
|
||||
RUN git clone https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
|
||||
RUN cp config.json.example config.json
|
||||
RUN npm install
|
||||
RUN bower install --allow-root
|
||||
|
||||
# Create Build dir
|
||||
RUN mkdir -p ./website/build
|
||||
|
||||
# Start Habitica
|
||||
EXPOSE 3000
|
||||
CMD ["npm", "start"]
|
||||
@@ -1,15 +0,0 @@
|
||||
FROM node:20
|
||||
|
||||
# Install global packages
|
||||
RUN npm install -g gulp-cli mocha
|
||||
|
||||
# Copy package.json and package-lock.json into image
|
||||
WORKDIR /usr/src/habitica
|
||||
COPY ["package.json", "package-lock.json", "./"]
|
||||
# Copy the remaining source files in.
|
||||
COPY . /usr/src/habitica
|
||||
# Install dependencies
|
||||
RUN npm install
|
||||
RUN npm run postinstall
|
||||
RUN npm run client:build
|
||||
RUN gulp build:prod
|
||||
22
Dockerfile-Production
Normal file
22
Dockerfile-Production
Normal file
@@ -0,0 +1,22 @@
|
||||
FROM node:boron
|
||||
|
||||
# Upgrade NPM to v5 (Yarn is needed because of this bug https://github.com/npm/npm/issues/16807)
|
||||
# The used solution is suggested here https://github.com/npm/npm/issues/16807#issuecomment-313591975
|
||||
RUN yarn global add npm@5
|
||||
# Install global packages
|
||||
RUN npm install -g gulp grunt-cli bower mocha
|
||||
|
||||
# Clone Habitica repo and install dependencies
|
||||
RUN mkdir -p /usr/src/habitrpg
|
||||
WORKDIR /usr/src/habitrpg
|
||||
RUN git clone --branch release https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
|
||||
RUN npm install
|
||||
RUN bower install --allow-root
|
||||
RUN gulp build:prod --force
|
||||
|
||||
# Create Build dir
|
||||
RUN mkdir -p ./website/build
|
||||
|
||||
# Start Habitica
|
||||
EXPOSE 3000
|
||||
CMD ["node", "./website/transpiled-babel/index.js"]
|
||||
142
Gruntfile.js
Normal file
142
Gruntfile.js
Normal 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');
|
||||
|
||||
};
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
* Code is GPL v3 licensed:
|
||||
This Source Code is subject to the terms of the GNU General Public License, v. 3.0.
|
||||
If a copy of the GPL was not distributed with this file, you can obtain one at http://www.gnu.org/licenses/gpl-3.0.txt
|
||||
If a copy of the GPL was not distributed with this file, You can obtain one at http://www.gnu.org/licenses/gpl-3.0.txt
|
||||
|
||||
* Assets and content designed for Mozilla BrowserQuest are licensed under CC-BY-SA 3.0:
|
||||
http://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
||||
19
README.md
19
README.md
@@ -1,20 +1,11 @@
|
||||
Habitica 
|
||||
Habitica [](https://travis-ci.org/HabitRPG/habitica) [](https://codeclimate.com/github/HabitRPG/habitrpg) [](https://coveralls.io/github/HabitRPG/habitica?branch=develop) [](https://www.bountysource.com/trackers/68393-habitrpg?utm_source=68393&utm_medium=shield&utm_campaign=TRACKER_BADGE)
|
||||
===============
|
||||
|
||||
[Habitica](https://habitica.com) is an open-source habit-building program that treats your life like a role-playing game. Level up as you succeed, lose HP as you fail, and earn Gold to buy weapons and armor!
|
||||
[Habitica](https://habitica.com) is an open source habit building program which treats your life like a Role Playing Game. Level up as you succeed, lose HP as you fail, earn money to buy weapons and armor.
|
||||
|
||||
**Want to contribute code to Habitica?** We're always looking for assistance on any issues in our repo with the "Help Wanted" label. The wiki pages below and the additional linked pages will tell you how to start contributing code and where you can seek further help or ask questions:
|
||||
* [Guidance for Blacksmiths](https://habitica.fandom.com/wiki/Guidance_for_Blacksmiths) - an introduction to the technologies used and how the software is organized.
|
||||
* [Setting up Habitica Locally](https://github.com/HabitRPG/habitica/wiki/Setting-Up-Habitica-for-Local-Development) - how to set up a local install of Habitica for development and testing.
|
||||
We need more programmers! Your assistance will be greatly appreciated.
|
||||
|
||||
**Interested in contributing to Habitica’s mobile apps?** Visit the links below for our mobile repositories.
|
||||
* **Android:** https://github.com/HabitRPG/habitica-android
|
||||
* **iOS:** https://github.com/HabitRPG/habitica-ios
|
||||
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).
|
||||
|
||||
Habitica's code is licensed as described at https://github.com/HabitRPG/habitica/blob/develop/LICENSE
|
||||
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).
|
||||
|
||||
**Found a bug?** Please report it to [admin email](mailto:admin@habitica.com) rather than create an issue (an admin will advise you if a new issue is necessary; usually it is not).
|
||||
|
||||
**Creating a third-party tool?** Please review our [API Usage Guidelines](https://github.com/HabitRPG/habitica/wiki/API-Usage-Guidelines) to ensure that your tool is compliant and maintains the best experience for Habitica players.
|
||||
|
||||
**Have any questions about Habitica or contributing?** See the links in the [Habitica](https://habitica.com) website's Help menu. There’s FAQ’s, guides, and the option to reach out to us with any further questions!
|
||||
|
||||
11
VAGRANT.md
Normal file
11
VAGRANT.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Vagrant #
|
||||
|
||||
Vagrant is a system to create reproducible and portable development
|
||||
environments. Because of the variety of systems used for Habitica
|
||||
development and the various issues developers may encounter setting up
|
||||
Habitica on them, vagrant provides a single development enviroment with
|
||||
minimal dependencies on the developer's local platform. It can be used
|
||||
on a variety of systems including Windows, Mac OS X, and Linux.
|
||||
|
||||
Instructions for using the Habitica Vagrant environment are in
|
||||
[Setting up Habitica Locally](http://habitica.wikia.com/wiki/Setting_up_Habitica_Locally).
|
||||
20
Vagrantfile.example
Normal file
20
Vagrantfile.example
Normal file
@@ -0,0 +1,20 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
|
||||
VAGRANTFILE_API_VERSION = "2"
|
||||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
config.vm.provider "virtualbox" do |v|
|
||||
v.memory = 4096
|
||||
v.cpus = 1
|
||||
v.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/vagrant", "1"]
|
||||
end
|
||||
config.vm.box = "thepeopleseason/habitrpg"
|
||||
config.ssh.forward_agent = true
|
||||
|
||||
config.vm.hostname = "habitrpg"
|
||||
config.vm.network "forwarded_port", guest: 3000, host: 3000, auto_correct: true
|
||||
config.vm.usable_port_range = (3000..3050)
|
||||
config.vm.provision :shell, :path => "vagrant_scripts/vagrant.sh"
|
||||
end
|
||||
8
apidoc.json
Normal file
8
apidoc.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "Habitica V3 API Documentation",
|
||||
"title": "Habitica",
|
||||
"url": "https://habitica.com",
|
||||
"template": {
|
||||
"withCompare": false
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "Habitica V3 API Documentation",
|
||||
"title": "Habitica",
|
||||
"url": "https://habitica.com",
|
||||
"version": "3.0.0",
|
||||
"sampleUrl": null,
|
||||
"header": {
|
||||
"title": "Introduction",
|
||||
"filename": "apidoc/header.md"
|
||||
},
|
||||
"template": {
|
||||
"withCompare": false
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
# Introduction
|
||||
|
||||
This webpage includes the documentation for version 3 of the [Habitica](https://habitica.com) API.
|
||||
|
||||
If you're developing a 3rd party tool that uses the Habitica API, read the [API Usage Guidelines](https://github.com/HabitRPG/habitica/wiki/API-Usage-Guidelines), which describe how to be a responsible user of our server resources!
|
||||
56
bower.json
Normal file
56
bower.json
Normal 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.14.1",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -1,98 +1,106 @@
|
||||
{
|
||||
"ACCOUNT_MIN_CHAT_AGE": "0",
|
||||
"ADMIN_EMAIL": "you@example.com",
|
||||
"AMAZON_PAYMENTS_CLIENT_ID": "CLIENT_ID",
|
||||
"AMAZON_PAYMENTS_MODE": "sandbox",
|
||||
"AMAZON_PAYMENTS_MWS_KEY": "MWS_KEY",
|
||||
"AMAZON_PAYMENTS_MWS_SECRET": "MWS_SECRET",
|
||||
"AMAZON_PAYMENTS_SELLER_ID": "SELLER_ID",
|
||||
"AMPLITUDE_KEY": "AMPLITUDE_KEY",
|
||||
"AMPLITUDE_SECRET": "AMPLITUDE_SECRET",
|
||||
"BASE_URL": "http://localhost:3000",
|
||||
"CRON_SAFE_MODE": "false",
|
||||
"CRON_SEMI_SAFE_MODE": "false",
|
||||
"DISABLE_REQUEST_LOGGING": "true",
|
||||
"EMAILS_COMMUNITY_MANAGER_EMAIL": "admin@habitica.com",
|
||||
"EMAILS_PRESS_ENQUIRY_EMAIL": "admin@habitica.com",
|
||||
"EMAILS_TECH_ASSISTANCE_EMAIL": "admin@habitica.com",
|
||||
"EMAIL_SERVER_AUTH_PASSWORD": "password",
|
||||
"EMAIL_SERVER_AUTH_USER": "user",
|
||||
"EMAIL_SERVER_URL": "http://example.com",
|
||||
"ENABLE_CONSOLE_LOGS_IN_PROD": "false",
|
||||
"ENABLE_CONSOLE_LOGS_IN_TEST": "false",
|
||||
"FACEBOOK_KEY": "123456789012345",
|
||||
"FACEBOOK_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
||||
"FLAG_REPORT_EMAIL": "email@example.com, email2@example.com",
|
||||
"GA_ID": "GA_ID",
|
||||
"GOOGLE_CLIENT_ID": "123456789012345",
|
||||
"GOOGLE_CLIENT_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
||||
"IAP_GOOGLE_KEYDIR": "/path/to/google/public/key/dir/",
|
||||
"IGNORE_REDIRECT": "true",
|
||||
"ITUNES_SHARED_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
||||
"LOGGLY_CLIENT_TOKEN": "token",
|
||||
"LOGGLY_SUBDOMAIN": "example-subdomain",
|
||||
"LOGGLY_TOKEN": "example-token",
|
||||
"LOG_REQUESTS_EXCESSIVE_MODE": "false",
|
||||
"MAINTENANCE_MODE": "false",
|
||||
"NODE_DB_URI": "mongodb://localhost:27017/habitica-dev?replicaSet=rs",
|
||||
"TEST_DB_URI": "mongodb://localhost:27017/habitica-test?replicaSet=rs",
|
||||
"MONGODB_POOL_SIZE": "10",
|
||||
"MONGODB_SOCKET_TIMEOUT": "20000",
|
||||
"NODE_ENV": "development",
|
||||
"PATH": "bin:node_modules/.bin:/usr/local/bin:/usr/bin:/bin",
|
||||
"PAYPAL_BILLING_PLANS_basic_12mo": "basic_12mo",
|
||||
"PAYPAL_BILLING_PLANS_basic_3mo": "basic_3mo",
|
||||
"PAYPAL_BILLING_PLANS_basic_6mo": "basic_6mo",
|
||||
"PAYPAL_BILLING_PLANS_basic_earned": "basic_earned",
|
||||
"PAYPAL_BILLING_PLANS_google_6mo": "google_6mo",
|
||||
"PAYPAL_CLIENT_ID": "client_id",
|
||||
"PAYPAL_CLIENT_SECRET": "client_secret",
|
||||
"PAYPAL_EXPERIENCE_PROFILE_ID": "xp_profile_id",
|
||||
"PAYPAL_MODE": "sandbox",
|
||||
"PLAY_API_ACCESS_TOKEN": "aaaabbbbccccddddeeeeffff00001111",
|
||||
"PLAY_API_CLIENT_ID": "aaaabbbbccccddddeeeeffff00001111",
|
||||
"PLAY_API_CLIENT_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
||||
"PLAY_API_REFRESH_TOKEN": "aaaabbbbccccddddeeeeffff00001111",
|
||||
"PORT": 3000,
|
||||
"PUSH_CONFIGS_APN_ENABLED": "false",
|
||||
"PUSH_CONFIGS_APN_KEY": "xxxxxxxxxx",
|
||||
"PUSH_CONFIGS_APN_KEY_ID": "xxxxxxxxxx",
|
||||
"PUSH_CONFIGS_APN_TEAM_ID": "aaabbbcccd",
|
||||
"PUSH_CONFIGS_FCM_SERVER_API_KEY": "aaabbbcccd",
|
||||
"S3_ACCESS_KEY_ID": "accessKeyId",
|
||||
"S3_BUCKET": "bucket",
|
||||
"S3_SECRET_ACCESS_KEY": "secretAccessKey",
|
||||
"SESSION_SECRET": "YOUR SECRET HERE",
|
||||
"SESSION_SECRET_IV": "12345678912345678912345678912345",
|
||||
"SESSION_SECRET_KEY": "1234567891234567891234567891234567891234567891234567891234567891",
|
||||
"SITE_HTTP_AUTH_ENABLED": "false",
|
||||
"SITE_HTTP_AUTH_PASSWORDS": "password,wordpass,passkey",
|
||||
"SITE_HTTP_AUTH_USERNAMES": "admin,tester,contributor",
|
||||
"SLACK_FLAGGING_FOOTER_LINK": "https://habitrpg.github.io/flag-o-rama/",
|
||||
"SLACK_FLAGGING_URL": "https://hooks.slack.com/services/id/id/id",
|
||||
"SLACK_SUBSCRIPTIONS_URL": "https://hooks.slack.com/services/id/id/id",
|
||||
"SLACK_URL": "https://hooks.slack.com/services/some-url",
|
||||
"STRIPE_API_KEY": "aaaabbbbccccddddeeeeffff00001111",
|
||||
"STRIPE_PUB_KEY": "22223333444455556666777788889999",
|
||||
"STRIPE_WEBHOOKS_ENDPOINT_SECRET": "111111",
|
||||
"TRANSIFEX_SLACK_CHANNEL": "transifex",
|
||||
"WEB_CONCURRENCY": 1,
|
||||
"SKIP_SSL_CHECK_KEY": "key",
|
||||
"ENABLE_STACKDRIVER_TRACING": "false",
|
||||
"APPLE_AUTH_PRIVATE_KEY": "",
|
||||
"APPLE_TEAM_ID": "",
|
||||
"APPLE_AUTH_CLIENT_ID": "",
|
||||
"APPLE_AUTH_KEY_ID": "",
|
||||
"BLOCKED_IPS": "",
|
||||
"LOG_AMPLITUDE_EVENTS": "false",
|
||||
"RATE_LIMITER_ENABLED": "false",
|
||||
"LIVELINESS_PROBE_KEY": "",
|
||||
"REDIS_HOST": "aaabbbcccdddeeefff",
|
||||
"REDIS_PORT": "1234",
|
||||
"REDIS_PASSWORD": "12345678",
|
||||
"TRUSTED_DOMAINS": "localhost,https://habitica.com",
|
||||
"TIME_TRAVEL_ENABLED": "false",
|
||||
"DEBUG_ENABLED": "false",
|
||||
"CONTENT_SWITCHOVER_TIME_OFFSET": 8,
|
||||
"SLOW_REQUEST_THRESHOLD": 1000
|
||||
"PORT":3000,
|
||||
"ENABLE_CONSOLE_LOGS_IN_PROD":"false",
|
||||
"IP":"0.0.0.0",
|
||||
"WEB_CONCURRENCY":1,
|
||||
"BASE_URL":"http://localhost:3000",
|
||||
"FACEBOOK_KEY":"123456789012345",
|
||||
"FACEBOOK_SECRET":"aaaabbbbccccddddeeeeffff00001111",
|
||||
"GOOGLE_CLIENT_ID":"123456789012345",
|
||||
"GOOGLE_CLIENT_SECRET":"aaaabbbbccccddddeeeeffff00001111",
|
||||
"PLAY_API": {
|
||||
"CLIENT_ID": "aaaabbbbccccddddeeeeffff00001111",
|
||||
"CLIENT_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
||||
"ACCESS_TOKEN":"aaaabbbbccccddddeeeeffff00001111",
|
||||
"REFRESH_TOKEN":"aaaabbbbccccddddeeeeffff00001111"
|
||||
},
|
||||
"NODE_DB_URI":"mongodb://localhost/habitrpg",
|
||||
"TEST_DB_URI":"mongodb://localhost/habitrpg_test",
|
||||
"NODE_ENV":"development",
|
||||
"ENABLE_CONSOLE_LOGS_IN_TEST": false,
|
||||
"CRON_SAFE_MODE":"false",
|
||||
"CRON_SEMI_SAFE_MODE":"false",
|
||||
"MAINTENANCE_MODE": "false",
|
||||
"SESSION_SECRET":"YOUR SECRET HERE",
|
||||
"ADMIN_EMAIL": "you@example.com",
|
||||
"SMTP_USER":"user@example.com",
|
||||
"SMTP_PASS":"password",
|
||||
"SMTP_SERVICE":"Gmail",
|
||||
"SMTP_HOST":"example.com",
|
||||
"SMTP_PORT": 587,
|
||||
"SMTP_TLS": true,
|
||||
"STRIPE_API_KEY":"aaaabbbbccccddddeeeeffff00001111",
|
||||
"STRIPE_PUB_KEY":"22223333444455556666777788889999",
|
||||
"NEW_RELIC_LICENSE_KEY":"NEW_RELIC_LICENSE_KEY",
|
||||
"NEW_RELIC_NO_CONFIG_FILE":"true",
|
||||
"NEW_RELIC_APPLICATION_ID":"NEW_RELIC_APPLICATION_ID",
|
||||
"NEW_RELIC_API_KEY":"NEW_RELIC_API_KEY",
|
||||
"GA_ID": "GA_ID",
|
||||
"AMPLITUDE_KEY": "AMPLITUDE_KEY",
|
||||
"AMAZON_PAYMENTS": {
|
||||
"SELLER_ID": "SELLER_ID",
|
||||
"CLIENT_ID": "CLIENT_ID",
|
||||
"MWS_KEY": "",
|
||||
"MWS_SECRET": ""
|
||||
},
|
||||
"FLAG_REPORT_EMAIL": "email@mod.com,email2@mod.com",
|
||||
"EMAIL_SERVER": {
|
||||
"url": "http://example.com",
|
||||
"authUser": "user",
|
||||
"authPassword": "password"
|
||||
},
|
||||
"S3":{
|
||||
"bucket":"bucket",
|
||||
"accessKeyId":"accessKeyId",
|
||||
"secretAccessKey":"secretAccessKey"
|
||||
},
|
||||
"SLACK_URL": "https://hooks.slack.com/services/some-url",
|
||||
"TRANSIFEX_SLACK_CHANNEL": "transifex",
|
||||
"PAYPAL":{
|
||||
"billing_plans": {
|
||||
"basic_earned":"basic_earned",
|
||||
"basic_3mo":"basic_3mo",
|
||||
"basic_6mo":"basic_6mo",
|
||||
"google_6mo":"google_6mo",
|
||||
"basic_12mo":"basic_12mo"
|
||||
},
|
||||
"mode":"sandbox",
|
||||
"client_id":"client_id",
|
||||
"client_secret":"client_secret",
|
||||
"experience_profile_id": ""
|
||||
},
|
||||
"IAP_GOOGLE_KEYDIR": "/path/to/google/public/key/dir/",
|
||||
"LOGGLY_TOKEN": "token",
|
||||
"LOGGLY_ACCOUNT": "account",
|
||||
"PUSH_CONFIGS": {
|
||||
"GCM_SERVER_API_KEY": "",
|
||||
"APN_ENABLED": "false",
|
||||
"FCM_SERVER_API_KEY": ""
|
||||
},
|
||||
"SITE_HTTP_AUTH": {
|
||||
"ENABLED": "false",
|
||||
"USERNAME": "admin",
|
||||
"PASSWORD": "password"
|
||||
},
|
||||
"PUSHER": {
|
||||
"ENABLED": "false",
|
||||
"APP_ID": "appId",
|
||||
"KEY": "key",
|
||||
"SECRET": "secret"
|
||||
},
|
||||
"SLACK": {
|
||||
"FLAGGING_URL": "https://hooks.slack.com/services/id/id/id",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
import max from 'lodash/max';
|
||||
import mean from 'lodash/mean';
|
||||
import monk from 'monk';
|
||||
import round from 'lodash/round';
|
||||
import sum from 'lodash/sum';
|
||||
|
||||
/*
|
||||
* Output data on subscribers' task histories, formatted for CSV.
|
||||
* User ID,Count of Dailies,Count of Habits,Total History Size,Max History Size,Mean History Size,Median History Size
|
||||
*/
|
||||
const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
let dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
let dbTasks = monk(connectionString).get('tasks', { castIds: false });
|
||||
|
||||
function usersReport () {
|
||||
let allHistoryLengths = [];
|
||||
|
||||
console.info('User ID,Count of Dailies,Count of Habits,Total History Size,Max History Size,Mean History Size,Median History Size');
|
||||
|
||||
dbUsers.find(
|
||||
{
|
||||
$and:
|
||||
[
|
||||
{'purchased.plan.planId': {$ne:null}},
|
||||
{'purchased.plan.planId': {$ne:''}},
|
||||
],
|
||||
$or:
|
||||
[
|
||||
{'purchased.plan.dateTerminated': null},
|
||||
{'purchased.plan.dateTerminated': ''},
|
||||
{'purchased.plan.dateTerminated': {$gt:new Date()}},
|
||||
],
|
||||
},
|
||||
{
|
||||
fields: {_id: 1},
|
||||
}
|
||||
).each((user, {close, pause, resume}) => {
|
||||
let historyLengths = [];
|
||||
let habitCount = 0;
|
||||
let dailyCount = 0;
|
||||
|
||||
pause();
|
||||
return dbTasks.find(
|
||||
{
|
||||
userId: user._id,
|
||||
$or:
|
||||
[
|
||||
{type: 'habit'},
|
||||
{type: 'daily'},
|
||||
],
|
||||
},
|
||||
{
|
||||
fields: {
|
||||
type: 1,
|
||||
history: 1,
|
||||
},
|
||||
}
|
||||
).each((task) => {
|
||||
if (task.type === 'habit') {
|
||||
habitCount++;
|
||||
}
|
||||
if (task.type === 'daily') {
|
||||
dailyCount++;
|
||||
}
|
||||
if (task.history.length > 0) {
|
||||
allHistoryLengths.push(task.history.length);
|
||||
historyLengths.push(task.history.length);
|
||||
}
|
||||
}).then(() => {
|
||||
const totalHistory = sum(historyLengths);
|
||||
const maxHistory = historyLengths.length > 0 ? max(historyLengths) : 0;
|
||||
const meanHistory = historyLengths.length > 0 ? round(mean(historyLengths)) : 0;
|
||||
const medianHistory = historyLengths.length > 0 ? median(historyLengths) : 0;
|
||||
console.info(`${user._id},${dailyCount},${habitCount},${totalHistory},${maxHistory},${meanHistory},${medianHistory}`);
|
||||
resume();
|
||||
});
|
||||
}).then(() => {
|
||||
console.info(`Total Subscriber History Entries: ${sum(allHistoryLengths)}`);
|
||||
console.info(`Largest History Size: ${max(allHistoryLengths)}`);
|
||||
console.info(`Mean History Size: ${round(mean(allHistoryLengths))}`);
|
||||
console.info(`Median History Size: ${median(allHistoryLengths)}`);
|
||||
return process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
function median(values) { // https://gist.github.com/caseyjustus/1166258
|
||||
values.sort( function(a,b) {return a - b;} );
|
||||
|
||||
var half = Math.floor(values.length/2);
|
||||
|
||||
if (values.length % 2) {
|
||||
return values[half];
|
||||
}
|
||||
else {
|
||||
return (values[half-1] + values[half]) / 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = usersReport;
|
||||
@@ -1,48 +0,0 @@
|
||||
import monk from 'monk';
|
||||
import nconf from 'nconf';
|
||||
|
||||
/*
|
||||
* Output data on users who completed all the To-Do tasks in the 2018 Back-to-School Challenge.
|
||||
* User ID,Profile Name
|
||||
*/
|
||||
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
|
||||
const CHALLENGE_ID = '0acb1d56-1660-41a4-af80-9259f080b62b';
|
||||
|
||||
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
|
||||
let dbTasks = monk(CONNECTION_STRING).get('tasks', { castIds: false });
|
||||
|
||||
function usersReport() {
|
||||
console.info('User ID,Profile Name');
|
||||
let userCount = 0;
|
||||
|
||||
dbUsers.find(
|
||||
{challenges: CHALLENGE_ID},
|
||||
{fields:
|
||||
{_id: 1, 'profile.name': 1}
|
||||
},
|
||||
).each((user, {close, pause, resume}) => {
|
||||
pause();
|
||||
userCount++;
|
||||
let completedTodos = 0;
|
||||
return dbTasks.find(
|
||||
{
|
||||
userId: user._id,
|
||||
'challenge.id': CHALLENGE_ID,
|
||||
type: 'todo',
|
||||
},
|
||||
{fields: {completed: 1}}
|
||||
).each((task) => {
|
||||
if (task.completed) completedTodos++;
|
||||
}).then(() => {
|
||||
if (completedTodos >= 7) {
|
||||
console.info(`${user._id},${user.profile.name}`);
|
||||
}
|
||||
resume();
|
||||
});
|
||||
}).then(() => {
|
||||
console.info(`${userCount} users reviewed`);
|
||||
return process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = usersReport;
|
||||
@@ -119,7 +119,7 @@ function path(obj, path, def) {
|
||||
* @param {String} path dot separated
|
||||
* @param {*} def default value ( if result undefined )
|
||||
* @returns {*}
|
||||
* https://stackoverflow.com/a/16190716
|
||||
* http://stackoverflow.com/a/16190716
|
||||
* Usage: console.log(path(someObject, pathname));
|
||||
*/
|
||||
for(var i = 0,path = path.split('.'),len = path.length; i < len; i++){
|
||||
|
||||
@@ -1,53 +1,3 @@
|
||||
services:
|
||||
client:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./Dockerfile-Dev
|
||||
command: ["npm", "run", "client:dev"]
|
||||
depends_on:
|
||||
- server
|
||||
environment:
|
||||
- BASE_URL=http://server:3000
|
||||
networks:
|
||||
- habitica
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- .:/usr/src/habitica
|
||||
- /usr/src/habitica/node_modules
|
||||
- /usr/src/habitica/website/client/node_modules
|
||||
server:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./Dockerfile-Dev
|
||||
command: ["npm", "start"]
|
||||
depends_on:
|
||||
mongo:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- NODE_DB_URI=mongodb://mongo/habitrpg
|
||||
networks:
|
||||
- habitica
|
||||
ports:
|
||||
- "3000:3000"
|
||||
volumes:
|
||||
- .:/usr/src/habitica
|
||||
- /usr/src/habitica/node_modules
|
||||
mongo:
|
||||
image: mongo:5.0.23
|
||||
restart: unless-stopped
|
||||
command: ["--replSet", "rs", "--bind_ip_all", "--port", "27017"]
|
||||
healthcheck:
|
||||
test: echo "try { rs.status() } catch (err) { rs.initiate() }" | mongosh --port 27017 --quiet
|
||||
interval: 10s
|
||||
timeout: 30s
|
||||
start_period: 0s
|
||||
start_interval: 1s
|
||||
retries: 30
|
||||
networks:
|
||||
- habitica
|
||||
ports:
|
||||
- "27017:27017"
|
||||
networks:
|
||||
habitica:
|
||||
driver: bridge
|
||||
web:
|
||||
volumes:
|
||||
- '.:/usr/src/habitrpg'
|
||||
|
||||
@@ -1,36 +1,13 @@
|
||||
version: "3"
|
||||
services:
|
||||
web:
|
||||
build: .
|
||||
ports:
|
||||
- "3000:3000"
|
||||
links:
|
||||
- mongo
|
||||
environment:
|
||||
- NODE_DB_URI=mongodb://mongo/habitrpg
|
||||
|
||||
client:
|
||||
build: .
|
||||
networks:
|
||||
- habitica
|
||||
environment:
|
||||
- BASE_URL=http://server:3000
|
||||
ports:
|
||||
- "8080:8080"
|
||||
command: ["npm", "run", "client:dev"]
|
||||
depends_on:
|
||||
- server
|
||||
|
||||
server:
|
||||
build: .
|
||||
ports:
|
||||
- "3000:3000"
|
||||
networks:
|
||||
- habitica
|
||||
environment:
|
||||
- NODE_DB_URI=mongodb://mongo/habitrpg
|
||||
depends_on:
|
||||
- mongo
|
||||
|
||||
mongo:
|
||||
image: mongo:3.6
|
||||
ports:
|
||||
- "27017:27017"
|
||||
networks:
|
||||
- habitica
|
||||
|
||||
networks:
|
||||
habitica:
|
||||
driver: bridge
|
||||
mongo:
|
||||
image: mongo
|
||||
ports:
|
||||
- "27017:27017"
|
||||
|
||||
10
gulp/.eslintrc
Normal file
10
gulp/.eslintrc
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true,
|
||||
},
|
||||
"extends": [
|
||||
"habitrpg/server",
|
||||
"habitrpg/babel"
|
||||
],
|
||||
}
|
||||
@@ -2,18 +2,16 @@ import gulp from 'gulp';
|
||||
import clean from 'rimraf';
|
||||
import apidoc from 'apidoc';
|
||||
|
||||
const APIDOC_DEST_PATH = './apidoc/html';
|
||||
const APIDOC_DEST_PATH = './website/build/apidoc';
|
||||
const APIDOC_SRC_PATH = './website/server';
|
||||
const APIDOC_CONFIG_PATH = './apidoc/apidoc.json';
|
||||
gulp.task('apidoc:clean', done => {
|
||||
gulp.task('apidoc:clean', (done) => {
|
||||
clean(APIDOC_DEST_PATH, done);
|
||||
});
|
||||
|
||||
gulp.task('apidoc', gulp.series('apidoc:clean', done => {
|
||||
const result = apidoc.createDoc({
|
||||
gulp.task('apidoc', ['apidoc:clean'], (done) => {
|
||||
let result = apidoc.createDoc({
|
||||
src: APIDOC_SRC_PATH,
|
||||
dest: APIDOC_DEST_PATH,
|
||||
config: APIDOC_CONFIG_PATH,
|
||||
});
|
||||
|
||||
if (result === false) {
|
||||
@@ -21,6 +19,8 @@ gulp.task('apidoc', gulp.series('apidoc:clean', done => {
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
gulp.task('apidoc:watch', gulp.series('apidoc', done => gulp.watch(`${APIDOC_SRC_PATH}/**/*.js`, gulp.series('apidoc', done))));
|
||||
gulp.task('apidoc:watch', ['apidoc'], () => {
|
||||
return gulp.watch(APIDOC_SRC_PATH + '/**/*.js', ['apidoc']);
|
||||
});
|
||||
|
||||
31
gulp/gulp-babelify.js
Normal file
31
gulp/gulp-babelify.js
Normal 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
gulp/gulp-bootstrap.js
Normal file
36
gulp/gulp-bootstrap.js
Normal file
@@ -0,0 +1,36 @@
|
||||
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,
|
||||
);
|
||||
});
|
||||
@@ -1,99 +1,56 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import gulp from 'gulp';
|
||||
import path from 'path';
|
||||
import runSequence from 'run-sequence';
|
||||
import babel from 'gulp-babel';
|
||||
import os from 'os';
|
||||
import fs from 'fs';
|
||||
import spawn from 'cross-spawn'; // eslint-disable-line import/no-extraneous-dependencies
|
||||
import clean from 'rimraf';
|
||||
import webpackProductionBuild from '../webpack/build';
|
||||
require('gulp-grunt')(gulp);
|
||||
|
||||
gulp.task('build:babel:server', () => gulp.src('website/server/**/*.js')
|
||||
.pipe(babel())
|
||||
.pipe(gulp.dest('website/transpiled-babel/')));
|
||||
|
||||
gulp.task('build:babel:common', () => gulp.src('website/common/script/**/*.js')
|
||||
.pipe(babel())
|
||||
.pipe(gulp.dest('website/common/transpiled-babel/')));
|
||||
|
||||
gulp.task('build:babel', gulp.parallel('build:babel:server', 'build:babel:common', done => done()));
|
||||
|
||||
gulp.task('build:cache', gulp.parallel(
|
||||
'cache:content',
|
||||
'cache:i18n',
|
||||
done => done(),
|
||||
));
|
||||
|
||||
gulp.task('build:prod', gulp.series(
|
||||
'build:babel',
|
||||
'apidoc',
|
||||
'build:cache',
|
||||
done => done(),
|
||||
));
|
||||
|
||||
// Due to this issue https://github.com/vkarpov15/run-rs/issues/45
|
||||
// When used on windows `run-rs` must first be run without the `--keep` option
|
||||
// in order to be setup correctly, afterwards it can be used.
|
||||
|
||||
const MONGO_PATH = path.join(__dirname, '/../mongodb-data/');
|
||||
|
||||
gulp.task('build:prepare-mongo', async () => {
|
||||
if (fs.existsSync(MONGO_PATH)) {
|
||||
// console.log('MongoDB data folder exists, skipping setup.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (os.platform() !== 'win32') {
|
||||
// console.log('Not on Windows, skipping MongoDB setup.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('MongoDB data folder is missing, setting up.'); // eslint-disable-line no-console
|
||||
|
||||
// use run-rs without --keep, kill it as soon as the replica set starts
|
||||
const runRsProcess = spawn('run-rs', ['-v', '4.1.1', '-l', 'ubuntu1804', '--dbpath', 'mongodb-data', '--number', '1', '--quiet']);
|
||||
|
||||
for await (const chunk of runRsProcess.stdout) {
|
||||
const stringChunk = chunk.toString();
|
||||
console.log(stringChunk); // eslint-disable-line no-console
|
||||
// kills the process after the replica set is setup
|
||||
if (stringChunk.includes('Started replica set')) {
|
||||
console.log('MongoDB setup correctly.'); // eslint-disable-line no-console
|
||||
runRsProcess.kill();
|
||||
}
|
||||
}
|
||||
|
||||
let error = '';
|
||||
for await (const chunk of runRsProcess.stderr) {
|
||||
const stringChunk = chunk.toString();
|
||||
error += stringChunk;
|
||||
}
|
||||
|
||||
const exitCode = await new Promise(resolve => {
|
||||
runRsProcess.on('close', resolve);
|
||||
});
|
||||
|
||||
if (exitCode || error.length > 0) {
|
||||
// remove any leftover files
|
||||
clean.sync(MONGO_PATH);
|
||||
|
||||
throw new Error(`Error running run-rs: ${error}`);
|
||||
gulp.task('build', () => {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
gulp.start('build:prod');
|
||||
} else {
|
||||
gulp.start('build:dev');
|
||||
}
|
||||
});
|
||||
|
||||
gulp.task('build:dev', gulp.series(
|
||||
'build:prepare-mongo',
|
||||
done => done(),
|
||||
));
|
||||
gulp.task('build:src', () => {
|
||||
return gulp.src('website/server/**/*.js')
|
||||
.pipe(babel())
|
||||
.pipe(gulp.dest('website/transpiled-babel/'));
|
||||
});
|
||||
|
||||
const buildArgs = [];
|
||||
gulp.task('build:common', () => {
|
||||
return gulp.src('website/common/script/**/*.js')
|
||||
.pipe(babel())
|
||||
.pipe(gulp.dest('website/common/transpiled-babel/'));
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV === 'production') { // eslint-disable-line no-process-env
|
||||
buildArgs.push('build:prod');
|
||||
} else if (process.env.NODE_ENV !== 'test') { // eslint-disable-line no-process-env
|
||||
buildArgs.push('build:dev');
|
||||
}
|
||||
gulp.task('build:server', ['build:src', 'build:common']);
|
||||
|
||||
gulp.task('build', gulp.series(buildArgs, done => {
|
||||
done();
|
||||
}));
|
||||
// Client Production Build
|
||||
gulp.task('build:client', ['bootstrap'], (done) => {
|
||||
webpackProductionBuild((err, output) => {
|
||||
if (err) return done(err);
|
||||
console.log(output);
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('build:dev', ['browserify', 'prepare:staticNewStuff'], (done) => {
|
||||
gulp.start('grunt-build:dev', done);
|
||||
});
|
||||
|
||||
gulp.task('build:dev:watch', ['build:dev'], () => {
|
||||
gulp.watch(['website/client-old/**/*.styl', 'website/common/script/*']);
|
||||
});
|
||||
|
||||
gulp.task('build:prod', [
|
||||
'browserify',
|
||||
'build:server',
|
||||
'prepare:staticNewStuff',
|
||||
'build:client',
|
||||
], (done) => {
|
||||
runSequence(
|
||||
'grunt-build:prod',
|
||||
'apidoc',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import gulp from 'gulp';
|
||||
import fs from 'fs';
|
||||
|
||||
// TODO parallelize, use gulp file helpers
|
||||
gulp.task('cache:content', done => {
|
||||
// Requiring at runtime because these files access `common`
|
||||
// code which in production works only if transpiled so after
|
||||
// gulp build:babel:common has run
|
||||
const { CONTENT_CACHE_PATH, getLocalizedContentResponse } = require('../website/server/libs/content'); // eslint-disable-line global-require
|
||||
const { langCodes } = require('../website/server/libs/i18n'); // eslint-disable-line global-require
|
||||
|
||||
try {
|
||||
// create the cache folder (if it doesn't exist)
|
||||
try {
|
||||
fs.mkdirSync(CONTENT_CACHE_PATH);
|
||||
} catch (err) {
|
||||
if (err.code !== 'EEXIST') throw err;
|
||||
}
|
||||
|
||||
// clone the content for each language and save
|
||||
// localize it
|
||||
// save the result
|
||||
langCodes.forEach(langCode => {
|
||||
fs.writeFileSync(
|
||||
`${CONTENT_CACHE_PATH}${langCode}.json`,
|
||||
getLocalizedContentResponse(langCode),
|
||||
'utf8',
|
||||
);
|
||||
});
|
||||
done();
|
||||
} catch (err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
|
||||
gulp.task('cache:i18n', done => {
|
||||
// Requiring at runtime because these files access `common`
|
||||
// code which in production works only if transpiled so after
|
||||
// gulp build:babel:common has run
|
||||
const { BROWSER_SCRIPT_CACHE_PATH, geti18nBrowserScript } = require('../website/server/libs/i18n'); // eslint-disable-line global-require
|
||||
const { langCodes } = require('../website/server/libs/i18n'); // eslint-disable-line global-require
|
||||
|
||||
try {
|
||||
// create the cache folder (if it doesn't exist)
|
||||
try {
|
||||
fs.mkdirSync(BROWSER_SCRIPT_CACHE_PATH);
|
||||
} catch (err) {
|
||||
if (err.code !== 'EEXIST') throw err;
|
||||
}
|
||||
|
||||
// create and save the i18n browser script for each language
|
||||
langCodes.forEach(languageCode => {
|
||||
fs.writeFileSync(
|
||||
`${BROWSER_SCRIPT_CACHE_PATH}${languageCode}.js`,
|
||||
geti18nBrowserScript(languageCode),
|
||||
'utf8',
|
||||
);
|
||||
});
|
||||
done();
|
||||
} catch (err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
@@ -1,49 +1,47 @@
|
||||
import mongoose from 'mongoose';
|
||||
import nconf from 'nconf';
|
||||
import repl from 'repl';
|
||||
import gulp from 'gulp';
|
||||
import {
|
||||
getDevelopmentConnectionUrl,
|
||||
getDefaultConnectionOptions,
|
||||
} from '../website/server/libs/mongodb';
|
||||
import autoinc from 'mongoose-id-autoinc';
|
||||
import logger from '../website/server/libs/logger';
|
||||
import nconf from 'nconf';
|
||||
import repl from 'repl';
|
||||
import gulp from 'gulp';
|
||||
|
||||
// Add additional properties to the repl's context
|
||||
const improveRepl = context => {
|
||||
let improveRepl = (context) => {
|
||||
|
||||
// Let "exit" and "quit" terminate the console
|
||||
['exit', 'quit'].forEach(term => {
|
||||
Object.defineProperty(context, term, {
|
||||
get () { // eslint-disable-line getter-return
|
||||
process.exit();
|
||||
},
|
||||
});
|
||||
['exit', 'quit'].forEach((term) => {
|
||||
Object.defineProperty(context, term, { get () { process.exit(); }});
|
||||
});
|
||||
|
||||
// "clear" clears the screen
|
||||
Object.defineProperty(context, 'clear', {
|
||||
get () { // eslint-disable-line getter-return
|
||||
process.stdout.write('\u001B[2J\u001B[0;0f');
|
||||
},
|
||||
});
|
||||
Object.defineProperty(context, 'clear', { get () {
|
||||
process.stdout.write('\u001B[2J\u001B[0;0f');
|
||||
}});
|
||||
|
||||
context.Challenge = require('../website/server/models/challenge').model; // eslint-disable-line global-require
|
||||
context.Group = require('../website/server/models/group').model; // eslint-disable-line global-require
|
||||
context.User = require('../website/server/models/user').model; // eslint-disable-line global-require
|
||||
context.Challenge = require('../website/server/models/challenge').model;
|
||||
context.Group = require('../website/server/models/group').model;
|
||||
context.User = require('../website/server/models/user').model;
|
||||
|
||||
const IS_PROD = nconf.get('NODE_ENV') === 'production';
|
||||
const NODE_DB_URI = nconf.get('NODE_DB_URI');
|
||||
|
||||
const mongooseOptions = getDefaultConnectionOptions();
|
||||
const connectionUrl = IS_PROD ? NODE_DB_URI : getDevelopmentConnectionUrl(NODE_DB_URI);
|
||||
|
||||
mongoose.connect(
|
||||
connectionUrl,
|
||||
mongooseOptions,
|
||||
var isProd = nconf.get('NODE_ENV') === 'production';
|
||||
var mongooseOptions = !isProd ? {} : {
|
||||
replset: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } },
|
||||
server: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } },
|
||||
};
|
||||
autoinc.init(
|
||||
mongoose.connect(
|
||||
nconf.get('NODE_DB_URI'),
|
||||
mongooseOptions,
|
||||
function (err) {
|
||||
if (err) throw err;
|
||||
logger.info('Connected with Mongoose');
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
gulp.task('console', done => {
|
||||
gulp.task('console', (cb) => {
|
||||
improveRepl(repl.start({
|
||||
prompt: 'Habitica > ',
|
||||
}).context);
|
||||
done();
|
||||
});
|
||||
|
||||
10
gulp/gulp-newstuff.js
Normal file
10
gulp/gulp-newstuff.js
Normal 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')()
|
||||
);
|
||||
});
|
||||
@@ -1,115 +1,161 @@
|
||||
import gulp from 'gulp';
|
||||
import imagemin from 'gulp-imagemin';
|
||||
import spritesmith from 'gulp.spritesmith';
|
||||
import clean from 'rimraf';
|
||||
import sizeOf from 'image-size';
|
||||
import mergeStream from 'merge-stream';
|
||||
import { sync } from 'glob';
|
||||
import {basename} from 'path';
|
||||
import {sync} from 'glob';
|
||||
import {each} from 'lodash';
|
||||
|
||||
const IMG_DIST_PATH = 'website/client/src/assets/images/sprites/';
|
||||
const CSS_DIST_PATH = 'website/client/src/assets/css/sprites/';
|
||||
// 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_NEW_CLIENT = 'website/static/sprites/';
|
||||
const CSS_DIST_PATH_NEW_CLIENT = 'website/client/assets/css/sprites/';
|
||||
|
||||
gulp.task('sprites:compile', ['sprites:clean', 'sprites:main', 'sprites:largeSprites', 'sprites:checkCompiledDimensions']);
|
||||
|
||||
gulp.task('sprites:main', () => {
|
||||
let mainSrc = sync('website/assets/sprites/spritesmith/**/*.png');
|
||||
return createSpritesStream('main', mainSrc);
|
||||
});
|
||||
|
||||
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/habitica/pull/6683#issuecomment-185462180
|
||||
} else {
|
||||
console.log('All images are within the correct dimensions');
|
||||
}
|
||||
});
|
||||
|
||||
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/assets/sprites/css/css.template.handlebars',
|
||||
cssVarMap: cssVarMap,
|
||||
}));
|
||||
|
||||
let imgStream = spriteData.img
|
||||
.pipe(imagemin())
|
||||
.pipe(gulp.dest(IMG_DIST_PATH_NEW_CLIENT))
|
||||
.pipe(gulp.dest(DIST_PATH));
|
||||
|
||||
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) {
|
||||
let totalPixels = 0;
|
||||
let slices = [0];
|
||||
|
||||
each(src, (img, index) => {
|
||||
let imageSize = calculateImgDimensions(img, true);
|
||||
totalPixels += imageSize;
|
||||
|
||||
if (totalPixels > MAX_SPRITESHEET_SIZE) {
|
||||
slices.push(index - 1);
|
||||
totalPixels = imageSize;
|
||||
}
|
||||
});
|
||||
|
||||
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) {
|
||||
const regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame|^eyewear_special_\w+HalfMoon/;
|
||||
let regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame/;
|
||||
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.
|
||||
// For hair, skins, beards, etc. we want to output a '.customize-options.WHATEVER' class, which works as a
|
||||
// 60x60 image pointing at the proper part of the 90x90 sprite.
|
||||
// We set up the custom info here, and the template makes use of it.
|
||||
const requiresSpecialTreatment = checkForSpecialTreatment(sprite.name);
|
||||
let requiresSpecialTreatment = checkForSpecialTreatment(sprite.name);
|
||||
if (requiresSpecialTreatment) {
|
||||
sprite.custom = {
|
||||
px: {
|
||||
offsetX: '-25px',
|
||||
offsetY: '-15px',
|
||||
offset_x: `-${ sprite.x + 25 }px`,
|
||||
offset_y: `-${ sprite.y + 15 }px`,
|
||||
width: '60px',
|
||||
height: '60px',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// even more for shirts
|
||||
if (sprite.name.indexOf('shirt') !== -1) {
|
||||
sprite.custom.px.offsetX = '-29px';
|
||||
sprite.custom.px.offsetY = '-42px';
|
||||
}
|
||||
|
||||
if (sprite.name.indexOf('hair_base') !== -1) {
|
||||
const styleArray = sprite.name.split('_').slice(2, 3);
|
||||
if (Number(styleArray[0]) > 14) {
|
||||
sprite.custom.px.offsetY = '0'; // don't crop updos
|
||||
}
|
||||
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.offset_y = `-${ sprite.y }px`; // don't crop updos
|
||||
}
|
||||
}
|
||||
|
||||
function filterFile (file) {
|
||||
if (file.relative.indexOf('Mount_Icon_') !== -1) {
|
||||
return false;
|
||||
}
|
||||
if (file.path.indexOf('shop/') !== -1) {
|
||||
return false;
|
||||
}
|
||||
if (file.path.indexOf('stable/eggs') !== -1) {
|
||||
return false;
|
||||
}
|
||||
if (file.path.indexOf('stable/food') !== -1) {
|
||||
return false;
|
||||
}
|
||||
if (file.path.indexOf('stable/potions') !== -1) {
|
||||
return false;
|
||||
}
|
||||
if (file.relative.indexOf('shop_') === 0) {
|
||||
return false;
|
||||
}
|
||||
if (file.relative.indexOf('icon_background') === 0) {
|
||||
return false;
|
||||
}
|
||||
if (file.relative.indexOf('notif_') === 0) {
|
||||
return false;
|
||||
}
|
||||
if (file.relative.indexOf('quest_') === 0) {
|
||||
return false;
|
||||
}
|
||||
if (file.relative.indexOf('inventory_quest_') === 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function createSpritesStream (name, src) {
|
||||
const stream = mergeStream();
|
||||
// need to import this way bc of weird dependency things
|
||||
// eslint-disable-next-line global-require
|
||||
const filter = require('gulp-filter');
|
||||
|
||||
const f = filter(filterFile);
|
||||
|
||||
const spriteData = gulp.src(src)
|
||||
.pipe(f)
|
||||
.pipe(spritesmith({
|
||||
imgName: `spritesmith-${name}.png`,
|
||||
cssName: `spritesmith-${name}.css`,
|
||||
algorithm: 'binary-tree',
|
||||
padding: 1,
|
||||
cssTemplate: 'website/raw_sprites/css/css.template.handlebars',
|
||||
cssVarMap,
|
||||
}));
|
||||
|
||||
const cssStream = spriteData.css
|
||||
.pipe(gulp.dest(CSS_DIST_PATH));
|
||||
|
||||
stream.add(cssStream);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
gulp.task('sprites:main', async () => {
|
||||
const mainSrc = sync('habitica-images/**/*.png');
|
||||
return createSpritesStream('main', mainSrc);
|
||||
});
|
||||
|
||||
gulp.task('sprites:clean', done => {
|
||||
clean(`${IMG_DIST_PATH}spritesmith*,${CSS_DIST_PATH}spritesmith*}`, done);
|
||||
});
|
||||
|
||||
gulp.task('sprites:compile', gulp.series('sprites:clean', 'sprites:main', done => done()));
|
||||
|
||||
17
gulp/gulp-start.js
Normal file
17
gulp/gulp-start.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import gulp from 'gulp';
|
||||
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,
|
||||
ignore: [
|
||||
'website/client-old/*',
|
||||
'website/views/*',
|
||||
'common/dist/script/content/*',
|
||||
],
|
||||
});
|
||||
});
|
||||
@@ -1,105 +1,139 @@
|
||||
import mongoose from 'mongoose';
|
||||
import { exec } from 'child_process';
|
||||
import gulp from 'gulp';
|
||||
import os from 'os';
|
||||
import nconf from 'nconf';
|
||||
import { pipe } from './taskHelper';
|
||||
import {
|
||||
getDevelopmentConnectionUrl,
|
||||
getDefaultConnectionOptions,
|
||||
} from '../website/server/libs/mongodb';
|
||||
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
|
||||
|
||||
const TEST_SERVER_PORT = 3003;
|
||||
const TEST_SERVER_PORT = 3003;
|
||||
let server;
|
||||
|
||||
const TEST_DB_URI = nconf.get('TEST_DB_URI');
|
||||
const TEST_DB_URI = nconf.get('TEST_DB_URI');
|
||||
|
||||
const 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 LIMIT_MAX_BUFFER_OPTIONS = { maxBuffer: 1024 * 500 };
|
||||
const CONTENT_OPTIONS = {maxBuffer: 1024 * 500};
|
||||
const KARMA_TEST_COMMAND = 'npm run test:karma';
|
||||
|
||||
/* Helper method for reporting test summary */
|
||||
const testResults = [];
|
||||
const testCount = (stdout, regexp) => {
|
||||
const match = stdout.match(regexp);
|
||||
return parseInt(match && (match[1] || 0), 10);
|
||||
/* Helper methods for reporting test summary */
|
||||
let testResults = [];
|
||||
let testCount = (stdout, regexp) => {
|
||||
let match = stdout.match(regexp);
|
||||
return parseInt(match && match[1] || 0);
|
||||
};
|
||||
|
||||
/* Helper methods to correctly run child test processes */
|
||||
const testBin = (string, additionalEnvVariables = '') => {
|
||||
let testBin = (string, additionalEnvVariables = '') => {
|
||||
if (os.platform() === 'win32') {
|
||||
if (additionalEnvVariables !== '') {
|
||||
additionalEnvVariables = additionalEnvVariables.split(' ').join('&&set '); // eslint-disable-line no-param-reassign
|
||||
additionalEnvVariables = `set ${additionalEnvVariables}&&`; // eslint-disable-line no-param-reassign
|
||||
if (additionalEnvVariables != '') {
|
||||
additionalEnvVariables = additionalEnvVariables.split(' ').join('&&set ');
|
||||
additionalEnvVariables = 'set ' + additionalEnvVariables + '&&';
|
||||
}
|
||||
return `set NODE_ENV=test&&${additionalEnvVariables}${string}`;
|
||||
} else {
|
||||
return `NODE_ENV=test ${additionalEnvVariables} ${string}`;
|
||||
}
|
||||
return `NODE_ENV=test ${additionalEnvVariables} ${string}`;
|
||||
};
|
||||
|
||||
function runInChildProcess (command, options = {}, envVariables = '') {
|
||||
return done => pipe(exec(testBin(command, envVariables), options, done));
|
||||
}
|
||||
gulp.task('test:nodemon', (done) => {
|
||||
process.env.PORT = TEST_SERVER_PORT;
|
||||
process.env.NODE_DB_URI = TEST_DB_URI;
|
||||
|
||||
function integrationTestCommand (testDir) {
|
||||
return `nyc --silent --no-clean mocha ${testDir} --recursive --require ./test/helpers/start-server`;
|
||||
}
|
||||
|
||||
/* Test task definitions */
|
||||
gulp.task('test:prepare:mongo', cb => {
|
||||
const mongooseOptions = getDefaultConnectionOptions();
|
||||
const connectionUrl = getDevelopmentConnectionUrl(TEST_DB_URI);
|
||||
|
||||
mongoose.connect(connectionUrl, mongooseOptions)
|
||||
.then(() => mongoose.connection.dropDatabase())
|
||||
.then(() => mongoose.connection.close()).then(() => {
|
||||
cb();
|
||||
})
|
||||
.catch(err => {
|
||||
if (err) return cb(`Unable to connect to mongo database. Are you sure it's running? \n\n${err}`);
|
||||
throw err;
|
||||
});
|
||||
runSequence('nodemon');
|
||||
});
|
||||
|
||||
gulp.task('test:prepare:server', gulp.series('test:prepare:mongo', done => {
|
||||
gulp.task('test:prepare:mongo', (cb) => {
|
||||
mongoose.connect(TEST_DB_URI, (err) => {
|
||||
if (err) return cb(`Unable to connect to mongo database. Are you sure it's running? \n\n${err}`);
|
||||
mongoose.connection.db.dropDatabase();
|
||||
mongoose.connection.close();
|
||||
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
|
||||
}
|
||||
done();
|
||||
if (error) { throw `Problem with the server: ${error}`; }
|
||||
if (stderr) { console.error(stderr); }
|
||||
});
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
gulp.task('test:prepare:build', gulp.series('build', done => done()));
|
||||
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', gulp.series(
|
||||
});
|
||||
|
||||
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',
|
||||
done => done(),
|
||||
));
|
||||
'test:prepare:webdriver',
|
||||
]);
|
||||
|
||||
gulp.task('test:sanity', runInChildProcess(SANITY_TEST_COMMAND));
|
||||
gulp.task('test:sanity', (cb) => {
|
||||
let runner = exec(
|
||||
testBin(SANITY_TEST_COMMAND),
|
||||
(err, stdout, stderr) => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
cb();
|
||||
}
|
||||
);
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:common', gulp.series('test:prepare:build', runInChildProcess(COMMON_TEST_COMMAND)));
|
||||
gulp.task('test:common', ['test:prepare:build'], (cb) => {
|
||||
let runner = exec(
|
||||
testBin(COMMON_TEST_COMMAND),
|
||||
(err, stdout, stderr) => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
cb();
|
||||
}
|
||||
);
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:common:clean', cb => {
|
||||
gulp.task('test:common:clean', (cb) => {
|
||||
pipe(exec(testBin(COMMON_TEST_COMMAND), () => cb()));
|
||||
});
|
||||
|
||||
gulp.task('test:common:watch', gulp.series('test:common:clean', () => gulp.watch(['common/script/**/*', 'test/common/**/*'], gulp.series('test:common:clean', done => done()))));
|
||||
gulp.task('test:common:watch', ['test:common:clean'], () => {
|
||||
gulp.watch(['common/script/**/*', 'test/common/**/*'], ['test:common:clean']);
|
||||
});
|
||||
|
||||
gulp.task('test:common:safe', gulp.series('test:prepare:build', cb => {
|
||||
const runner = exec(
|
||||
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/),
|
||||
@@ -107,27 +141,38 @@ gulp.task('test:common:safe', gulp.series('test:prepare:build', cb => {
|
||||
pend: testCount(stdout, /(\d+) pending/),
|
||||
});
|
||||
cb();
|
||||
},
|
||||
}
|
||||
);
|
||||
pipe(runner);
|
||||
}));
|
||||
|
||||
gulp.task('test:content', gulp.series(
|
||||
'test:prepare:build',
|
||||
runInChildProcess(CONTENT_TEST_COMMAND, LIMIT_MAX_BUFFER_OPTIONS),
|
||||
));
|
||||
|
||||
gulp.task('test:content:clean', cb => {
|
||||
pipe(exec(testBin(CONTENT_TEST_COMMAND), LIMIT_MAX_BUFFER_OPTIONS, () => cb()));
|
||||
});
|
||||
|
||||
gulp.task('test:content:watch', gulp.series('test:content:clean', () => gulp.watch(['common/script/content/**', 'test/**'], gulp.series('test:content:clean', done => done()))));
|
||||
|
||||
gulp.task('test:content:safe', gulp.series('test:prepare:build', cb => {
|
||||
const runner = exec(
|
||||
gulp.task('test:content', ['test:prepare:build'], (cb) => {
|
||||
let runner = exec(
|
||||
testBin(CONTENT_TEST_COMMAND),
|
||||
LIMIT_MAX_BUFFER_OPTIONS,
|
||||
(err, stdout) => { // eslint-disable-line handle-callback-err
|
||||
CONTENT_OPTIONS,
|
||||
(err, stdout, stderr) => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
cb();
|
||||
}
|
||||
);
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:content:clean', (cb) => {
|
||||
pipe(exec(testBin(CONTENT_TEST_COMMAND), CONTENT_OPTIONS, () => cb()));
|
||||
});
|
||||
|
||||
gulp.task('test:content:watch', ['test:content:clean'], () => {
|
||||
gulp.watch(['common/script/content/**', 'test/**'], ['test:content:clean']);
|
||||
});
|
||||
|
||||
gulp.task('test:content:safe', ['test:prepare:build'], (cb) => {
|
||||
let runner = exec(
|
||||
testBin(CONTENT_TEST_COMMAND),
|
||||
CONTENT_OPTIONS,
|
||||
(err, stdout, stderr) => {
|
||||
testResults.push({
|
||||
suite: 'Content Specs\t',
|
||||
pass: testCount(stdout, /(\d+) passing/),
|
||||
@@ -135,69 +180,168 @@ gulp.task('test:content:safe', gulp.series('test:prepare:build', cb => {
|
||||
pend: testCount(stdout, /(\d+) pending/),
|
||||
});
|
||||
cb();
|
||||
},
|
||||
}
|
||||
);
|
||||
pipe(runner);
|
||||
}));
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
'test:api:unit:run',
|
||||
runInChildProcess(integrationTestCommand('test/api/unit')),
|
||||
);
|
||||
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:api:unit:watch', () => gulp.watch(['website/server/libs/*', 'test/api/unit/**/*', 'website/server/controllers/**/*'], gulp.series('test:api:unit:run', done => done())));
|
||||
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:api-v3:integration', gulp.series(
|
||||
'test:prepare:mongo',
|
||||
runInChildProcess(
|
||||
integrationTestCommand('test/api/v3/integration'),
|
||||
LIMIT_MAX_BUFFER_OPTIONS,
|
||||
),
|
||||
));
|
||||
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:api-v3:integration:watch', () => gulp.watch([
|
||||
'website/server/controllers/api-v3/**/*', 'common/script/ops/*', 'website/server/libs/*.js',
|
||||
'test/api/v3/integration/**/*',
|
||||
], gulp.series('test:api-v3:integration', done => done())));
|
||||
gulp.task('test: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);
|
||||
|
||||
gulp.task('test:api-v3:integration:separate-server', runInChildProcess(
|
||||
'mocha test/api/v3/integration --recursive --require ./test/helpers/start-server',
|
||||
LIMIT_MAX_BUFFER_OPTIONS,
|
||||
'LOAD_SERVER=0',
|
||||
));
|
||||
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:api-v4:integration', gulp.series(
|
||||
'test:prepare:mongo',
|
||||
runInChildProcess(
|
||||
integrationTestCommand('test/api/v4'),
|
||||
LIMIT_MAX_BUFFER_OPTIONS,
|
||||
),
|
||||
));
|
||||
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);
|
||||
|
||||
gulp.task('test:api-v4:integration:separate-server', runInChildProcess(
|
||||
'mocha test/api/v4 --recursive --require ./test/helpers/start-server',
|
||||
LIMIT_MAX_BUFFER_OPTIONS,
|
||||
'LOAD_SERVER=0',
|
||||
));
|
||||
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?/);
|
||||
|
||||
gulp.task('test:api:unit', gulp.series(
|
||||
'test:prepare:mongo',
|
||||
'test:api:unit:run',
|
||||
done => done(),
|
||||
));
|
||||
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', gulp.series(
|
||||
'test:sanity',
|
||||
'test:content',
|
||||
'test:common',
|
||||
'test:api:unit:run',
|
||||
'test:api-v3:integration',
|
||||
'test:api-v4:integration',
|
||||
done => done(),
|
||||
));
|
||||
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, stdout, stderr) => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
gulp.task('test:api-v3', gulp.series(
|
||||
'test:api:unit',
|
||||
'test:api-v3:integration',
|
||||
done => done(),
|
||||
));
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:api-v3:unit:watch', () => {
|
||||
gulp.watch(['website/server/libs/*', 'test/api/v3/unit/**/*', 'website/server/controllers/**/*'], ['test:api-v3:unit']);
|
||||
});
|
||||
|
||||
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'),
|
||||
{maxBuffer: 500 * 1024},
|
||||
(err, stdout, stderr) => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:api-v3:integration:watch', () => {
|
||||
gulp.watch(['website/server/controllers/api-v3/**/*', 'common/script/ops/*', 'website/server/libs/*.js',
|
||||
'test/api/v3/integration/**/*'], ['test:api-v3:integration']);
|
||||
});
|
||||
|
||||
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, stdout, stderr) => done(err)
|
||||
);
|
||||
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test', (done) => {
|
||||
runSequence(
|
||||
'test:sanity',
|
||||
'test:content',
|
||||
'test:common',
|
||||
'test:karma',
|
||||
'test:api-v3:unit',
|
||||
'test:api-v3:integration',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
gulp.task('test:api-v3', (done) => {
|
||||
runSequence(
|
||||
'test:api-v3:unit',
|
||||
'test:api-v3:integration',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import fs from 'fs';
|
||||
import _ from 'lodash';
|
||||
import gulp from 'gulp';
|
||||
import fs from 'fs';
|
||||
import _ from 'lodash';
|
||||
import nconf from 'nconf';
|
||||
import gulp from 'gulp';
|
||||
import { postToSlack, conf } from './taskHelper';
|
||||
|
||||
const SLACK_CONFIG = {
|
||||
@@ -11,39 +12,121 @@ const SLACK_CONFIG = {
|
||||
|
||||
const LOCALES = './website/common/locales/';
|
||||
const ENGLISH_LOCALE = `${LOCALES}en/`;
|
||||
const ALL_LANGUAGES = getArrayOfLanguages();
|
||||
|
||||
const malformedStringExceptions = {
|
||||
messageDropFood: true,
|
||||
armoireFood: true,
|
||||
feedPet: true,
|
||||
};
|
||||
|
||||
gulp.task('transifex', ['transifex:missingFiles', 'transifex:missingStrings', 'transifex:malformedStrings']);
|
||||
|
||||
gulp.task('transifex:missingFiles', () => {
|
||||
|
||||
let missingStrings = [];
|
||||
|
||||
eachTranslationFile(ALL_LANGUAGES, (error) => {
|
||||
if (error) {
|
||||
missingStrings.push(error.path);
|
||||
}
|
||||
});
|
||||
|
||||
if (!_.isEmpty(missingStrings)) {
|
||||
let message = 'the following files were missing from the translations folder';
|
||||
let formattedMessage = formatMessageForPosting(message, missingStrings);
|
||||
postToSlack(formattedMessage, SLACK_CONFIG);
|
||||
}
|
||||
});
|
||||
|
||||
gulp.task('transifex:missingStrings', () => {
|
||||
|
||||
let missingStrings = [];
|
||||
|
||||
eachTranslationString(ALL_LANGUAGES, (language, filename, key, englishString, translationString) => {
|
||||
if (!translationString) {
|
||||
let errorString = `${language} - ${filename} - ${key} - ${englishString}`;
|
||||
missingStrings.push(errorString);
|
||||
}
|
||||
});
|
||||
|
||||
if (!_.isEmpty(missingStrings)) {
|
||||
let message = 'The following strings are not translated';
|
||||
let formattedMessage = formatMessageForPosting(message, missingStrings);
|
||||
postToSlack(formattedMessage, SLACK_CONFIG);
|
||||
}
|
||||
});
|
||||
|
||||
gulp.task('transifex:malformedStrings', () => {
|
||||
|
||||
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
|
||||
let interpolationRegex = /<%= [a-zA-Z]* %>/g;
|
||||
let stringsToLookFor = getStringsWith(jsonFiles, interpolationRegex);
|
||||
|
||||
let stringsWithMalformedInterpolations = [];
|
||||
let stringsWithIncorrectNumberOfInterpolations = [];
|
||||
|
||||
let count = 0;
|
||||
_.each(ALL_LANGUAGES, function (lang) {
|
||||
|
||||
_.each(stringsToLookFor, function (strings, file) {
|
||||
let translationFile = fs.readFileSync(LOCALES + lang + '/' + file);
|
||||
let parsedTranslationFile = JSON.parse(translationFile);
|
||||
|
||||
_.each(strings, function (value, key) {
|
||||
let translationString = parsedTranslationFile[key];
|
||||
if (!translationString) return;
|
||||
|
||||
let englishOccurences = stringsToLookFor[file][key];
|
||||
let translationOccurences = translationString.match(interpolationRegex);
|
||||
|
||||
if (!translationOccurences) {
|
||||
let malformedString = `${lang} - ${file} - ${key} - ${translationString}`;
|
||||
stringsWithMalformedInterpolations.push(malformedString);
|
||||
} else if (englishOccurences.length !== translationOccurences.length && !malformedStringExceptions[key]) {
|
||||
let missingInterpolationString = `${lang} - ${file} - ${key} - ${translationString}`;
|
||||
stringsWithIncorrectNumberOfInterpolations.push(missingInterpolationString);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (!_.isEmpty(stringsWithMalformedInterpolations)) {
|
||||
let message = 'The following strings have malformed or missing interpolations';
|
||||
let formattedMessage = formatMessageForPosting(message, stringsWithMalformedInterpolations);
|
||||
postToSlack(formattedMessage, SLACK_CONFIG);
|
||||
}
|
||||
|
||||
if (!_.isEmpty(stringsWithIncorrectNumberOfInterpolations)) {
|
||||
let message = 'The following strings have a different number of string interpolations';
|
||||
let formattedMessage = formatMessageForPosting(message, stringsWithIncorrectNumberOfInterpolations);
|
||||
postToSlack(formattedMessage, SLACK_CONFIG);
|
||||
}
|
||||
});
|
||||
|
||||
function getArrayOfLanguages () {
|
||||
const languages = fs.readdirSync(LOCALES);
|
||||
let languages = fs.readdirSync(LOCALES);
|
||||
languages.shift(); // Remove README.md from array of languages
|
||||
|
||||
return languages;
|
||||
}
|
||||
|
||||
const ALL_LANGUAGES = getArrayOfLanguages();
|
||||
|
||||
function stripOutNonJsonFiles (collection) {
|
||||
const onlyJson = _.filter(collection, file => file.match(/[a-zA-Z]*\.json/));
|
||||
|
||||
return onlyJson;
|
||||
}
|
||||
|
||||
function eachTranslationFile (languages, cb) {
|
||||
const jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
|
||||
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
|
||||
|
||||
_.each(languages, lang => {
|
||||
_.each(jsonFiles, filename => {
|
||||
let parsedTranslationFile;
|
||||
_.each(languages, (lang) => {
|
||||
_.each(jsonFiles, (filename) => {
|
||||
try {
|
||||
const translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`);
|
||||
parsedTranslationFile = JSON.parse(translationFile);
|
||||
var translationFile = fs.readFileSync(LOCALES + lang + '/' + filename);
|
||||
var parsedTranslationFile = JSON.parse(translationFile);
|
||||
} catch (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
const englishFile = fs.readFileSync(ENGLISH_LOCALE + filename);
|
||||
const parsedEnglishFile = JSON.parse(englishFile);
|
||||
let englishFile = fs.readFileSync(ENGLISH_LOCALE + filename);
|
||||
let parsedEnglishFile = JSON.parse(englishFile);
|
||||
|
||||
return cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile);
|
||||
cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -52,7 +135,7 @@ function eachTranslationString (languages, cb) {
|
||||
eachTranslationFile(languages, (error, language, filename, englishJSON, translationJSON) => {
|
||||
if (error) return;
|
||||
_.each(englishJSON, (string, key) => {
|
||||
const translationString = translationJSON[key];
|
||||
var translationString = translationJSON[key];
|
||||
cb(language, filename, key, string, translationString);
|
||||
});
|
||||
});
|
||||
@@ -68,116 +151,26 @@ function formatMessageForPosting (msg, items) {
|
||||
}
|
||||
|
||||
function getStringsWith (json, interpolationRegex) {
|
||||
const strings = {};
|
||||
var strings = {};
|
||||
|
||||
_.each(json, fileName => {
|
||||
const rawFile = fs.readFileSync(ENGLISH_LOCALE + fileName);
|
||||
const parsedJson = JSON.parse(rawFile);
|
||||
_.each(json, function (file_name) {
|
||||
var raw_file = fs.readFileSync(ENGLISH_LOCALE + file_name);
|
||||
var parsed_json = JSON.parse(raw_file);
|
||||
|
||||
strings[fileName] = {};
|
||||
_.each(parsedJson, (value, key) => {
|
||||
const match = value.match(interpolationRegex);
|
||||
if (match) strings[fileName][key] = match;
|
||||
strings[file_name] = {};
|
||||
_.each(parsed_json, function (value, key) {
|
||||
var match = value.match(interpolationRegex);
|
||||
if (match) strings[file_name][key] = match;
|
||||
});
|
||||
});
|
||||
|
||||
return strings;
|
||||
}
|
||||
|
||||
const malformedStringExceptions = {
|
||||
messageDropFood: true,
|
||||
armoireFood: true,
|
||||
feedPet: true,
|
||||
};
|
||||
|
||||
gulp.task('transifex:missingFiles', done => {
|
||||
const missingStrings = [];
|
||||
|
||||
eachTranslationFile(ALL_LANGUAGES, error => {
|
||||
if (error) {
|
||||
missingStrings.push(error.path);
|
||||
}
|
||||
function stripOutNonJsonFiles (collection) {
|
||||
let onlyJson = _.filter(collection, (file) => {
|
||||
return file.match(/[a-zA-Z]*\.json/);
|
||||
});
|
||||
|
||||
if (!_.isEmpty(missingStrings)) {
|
||||
const message = 'the following files were missing from the translations folder';
|
||||
const formattedMessage = formatMessageForPosting(message, missingStrings);
|
||||
postToSlack(formattedMessage, SLACK_CONFIG);
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task('transifex:missingStrings', done => {
|
||||
const missingStrings = [];
|
||||
|
||||
eachTranslationString(ALL_LANGUAGES, (lang, filename, key, englishString, translationString) => {
|
||||
if (!translationString) {
|
||||
const errorString = `${lang} - ${filename} - ${key} - ${englishString}`;
|
||||
missingStrings.push(errorString);
|
||||
}
|
||||
});
|
||||
|
||||
if (!_.isEmpty(missingStrings)) {
|
||||
const message = 'The following strings are not translated';
|
||||
const formattedMessage = formatMessageForPosting(message, missingStrings);
|
||||
postToSlack(formattedMessage, SLACK_CONFIG);
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task('transifex:malformedStrings', done => {
|
||||
const jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
|
||||
const interpolationRegex = /<%= [a-zA-Z]* %>/g;
|
||||
const stringsToLookFor = getStringsWith(jsonFiles, interpolationRegex);
|
||||
|
||||
const stringsWithMalformedInterpolations = [];
|
||||
const stringsWithIncorrectNumberOfInterpolations = [];
|
||||
|
||||
_.each(ALL_LANGUAGES, lang => {
|
||||
_.each(stringsToLookFor, (strings, filename) => {
|
||||
const translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`);
|
||||
const parsedTranslationFile = JSON.parse(translationFile);
|
||||
|
||||
_.each(strings, (value, key) => { // eslint-disable-line max-nested-callbacks
|
||||
const translationString = parsedTranslationFile[key];
|
||||
if (!translationString) return;
|
||||
|
||||
const englishOccurences = stringsToLookFor[filename][key];
|
||||
const translationOccurences = translationString.match(interpolationRegex);
|
||||
|
||||
if (!translationOccurences) {
|
||||
const malformedString = `${lang} - ${filename} - ${key} - ${translationString}`;
|
||||
stringsWithMalformedInterpolations.push(malformedString);
|
||||
} else if (
|
||||
englishOccurences.length !== translationOccurences.length
|
||||
&& !malformedStringExceptions[key]
|
||||
) {
|
||||
const missingInterpolationString = `${lang} - ${filename} - ${key} - ${translationString}`;
|
||||
stringsWithIncorrectNumberOfInterpolations.push(missingInterpolationString);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (!_.isEmpty(stringsWithMalformedInterpolations)) {
|
||||
const message = 'The following strings have malformed or missing interpolations';
|
||||
const formattedMessage = formatMessageForPosting(message, stringsWithMalformedInterpolations);
|
||||
postToSlack(formattedMessage, SLACK_CONFIG);
|
||||
}
|
||||
|
||||
if (!_.isEmpty(stringsWithIncorrectNumberOfInterpolations)) {
|
||||
const message = 'The following strings have a different number of string interpolations';
|
||||
const formattedMessage = formatMessageForPosting(
|
||||
message,
|
||||
stringsWithIncorrectNumberOfInterpolations,
|
||||
);
|
||||
postToSlack(formattedMessage, SLACK_CONFIG);
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
'transifex',
|
||||
gulp.series('transifex:missingFiles', 'transifex:missingStrings', 'transifex:malformedStrings'),
|
||||
done => done(),
|
||||
);
|
||||
return onlyJson;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { exec } from 'child_process';
|
||||
import psTree from 'ps-tree';
|
||||
import nconf from 'nconf';
|
||||
import net from 'net';
|
||||
import { post } from 'superagent';
|
||||
import { sync as glob } from 'glob';
|
||||
import Mocha from 'mocha'; // eslint-disable-line import/no-extraneous-dependencies
|
||||
import { resolve } from 'path';
|
||||
import { exec } from 'child_process';
|
||||
import psTree from 'ps-tree';
|
||||
import nconf from 'nconf';
|
||||
import net from 'net';
|
||||
import Bluebird from 'bluebird';
|
||||
import { post } from 'superagent';
|
||||
import { sync as glob } from 'glob';
|
||||
import Mocha from 'mocha';
|
||||
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.
|
||||
@@ -19,7 +20,7 @@ export const conf = nconf;
|
||||
* its tasks.
|
||||
*/
|
||||
export function kill (proc) {
|
||||
const killProcess = pid => {
|
||||
let killProcess = (pid) => {
|
||||
psTree(pid, (_, pids) => {
|
||||
if (pids.length) {
|
||||
pids.forEach(kill); return;
|
||||
@@ -28,9 +29,8 @@ export function kill (proc) {
|
||||
exec(/^win/.test(process.platform)
|
||||
? `taskkill /PID ${pid} /T /F`
|
||||
: `kill -9 ${pid}`);
|
||||
} catch (e) {
|
||||
console.log(e); // eslint-disable-line no-console
|
||||
}
|
||||
catch (e) { console.log(e); }
|
||||
});
|
||||
};
|
||||
|
||||
@@ -44,24 +44,21 @@ export function kill (proc) {
|
||||
* before failing.
|
||||
*/
|
||||
export function awaitPort (port, max = 60) {
|
||||
return new Promise((rej, res) => {
|
||||
let socket;
|
||||
let interval;
|
||||
return new Bluebird((reject, resolve) => {
|
||||
let socket, timeout, interval;
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
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);
|
||||
});
|
||||
}
|
||||
@@ -70,23 +67,19 @@ 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); });
|
||||
}
|
||||
|
||||
/*
|
||||
* Post request to notify configured slack channel
|
||||
*/
|
||||
export function postToSlack (msg, config = {}) {
|
||||
const slackUrl = nconf.get('SLACK_URL');
|
||||
let slackUrl = nconf.get('SLACK_URL');
|
||||
|
||||
if (!slackUrl) {
|
||||
console.error('No slack post url specified. Your message was:'); // eslint-disable-line no-console
|
||||
console.log(msg); // eslint-disable-line no-console
|
||||
console.error('No slack post url specified. Your message was:');
|
||||
console.log(msg);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -96,26 +89,26 @@ 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');
|
||||
|
||||
const mocha = new Mocha({ reporter: 'spec' });
|
||||
const tests = glob(files);
|
||||
let mocha = new Mocha({reporter: 'spec'});
|
||||
let tests = glob(files);
|
||||
|
||||
tests.forEach(test => {
|
||||
tests.forEach((test) => {
|
||||
delete require.cache[resolve(test)];
|
||||
mocha.addFile(test);
|
||||
});
|
||||
|
||||
mocha.run(numberOfFailures => {
|
||||
if (!process.env.RUN_INTEGRATION_TEST_FOREVER) { // eslint-disable-line no-process-env
|
||||
mocha.run((numberOfFailures) => {
|
||||
if (!process.env.RUN_INTEGRATION_TEST_FOREVER) {
|
||||
if (server) kill(server);
|
||||
process.exit(numberOfFailures);
|
||||
}
|
||||
|
||||
25
gulpfile.js
25
gulpfile.js
@@ -6,22 +6,15 @@
|
||||
* directory, and it will automatically be included.
|
||||
*/
|
||||
|
||||
/* eslint-disable import/no-commonjs */
|
||||
require('@babel/register');
|
||||
require('babel-register');
|
||||
|
||||
const gulp = require('gulp');
|
||||
|
||||
if (process.env.NODE_ENV === 'production') { // eslint-disable-line no-process-env
|
||||
require('./gulp/gulp-apidoc'); // eslint-disable-line global-require
|
||||
require('./gulp/gulp-cache'); // eslint-disable-line global-require
|
||||
require('./gulp/gulp-build'); // eslint-disable-line global-require
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
require('./gulp/gulp-apidoc');
|
||||
require('./gulp/gulp-newstuff');
|
||||
require('./gulp/gulp-build');
|
||||
require('./gulp/gulp-babelify');
|
||||
require('./gulp/gulp-bootstrap');
|
||||
} else {
|
||||
require('./gulp/gulp-apidoc'); // eslint-disable-line global-require
|
||||
require('./gulp/gulp-cache'); // eslint-disable-line global-require
|
||||
require('./gulp/gulp-build'); // eslint-disable-line global-require
|
||||
require('./gulp/gulp-console'); // eslint-disable-line global-require
|
||||
require('./gulp/gulp-sprites'); // eslint-disable-line global-require
|
||||
require('./gulp/gulp-tests'); // eslint-disable-line global-require
|
||||
require('./gulp/gulp-transifex-test'); // eslint-disable-line global-require
|
||||
require('gulp').task('default', gulp.series('test')); // eslint-disable-line global-require
|
||||
require('glob').sync('./gulp/gulp-*').forEach(require);
|
||||
require('gulp').task('default', ['test']);
|
||||
}
|
||||
|
||||
Submodule habitica-images deleted from 992d838120
@@ -1,8 +0,0 @@
|
||||
/* eslint-disable import/no-commonjs */
|
||||
module.exports = {
|
||||
root: false,
|
||||
rules: {
|
||||
'no-console': 0,
|
||||
'no-use-before-define': ['error', { functions: false }],
|
||||
},
|
||||
};
|
||||
5
migrations/20130128_add_missing_crons.js
Normal file
5
migrations/20130128_add_missing_crons.js
Normal file
@@ -0,0 +1,5 @@
|
||||
db.users.update(
|
||||
{ lastCron: { $exists: false} },
|
||||
{ $set: { lastCron: +new Date } },
|
||||
{ multi: true }
|
||||
);
|
||||
15
migrations/20130128_merge_completed_todo_ids.js
Normal file
15
migrations/20130128_merge_completed_todo_ids.js
Normal file
@@ -0,0 +1,15 @@
|
||||
db.users.find({ completedIds: { $exists: true } }).forEach(function(user) {
|
||||
var newTodoIds = user.todoIds;
|
||||
user.completedIds.forEach(function(value) {
|
||||
if (newTodoIds.indexOf(value) === -1) {
|
||||
newTodoIds.push(value)
|
||||
}
|
||||
});
|
||||
db.users.update(
|
||||
{ _id: user._id },
|
||||
{
|
||||
$set: { todoIds: newTodoIds },
|
||||
$unset: { completedIds: 1 }
|
||||
}
|
||||
);
|
||||
});
|
||||
5
migrations/20130129_add_missing_preferences.js
Normal file
5
migrations/20130129_add_missing_preferences.js
Normal file
@@ -0,0 +1,5 @@
|
||||
db.users.update(
|
||||
{preferences:{$exists:false}},
|
||||
{$set:{preferences:{gender: 'm', armorSet: 'v1'}}},
|
||||
{multi:true}
|
||||
)
|
||||
20
migrations/20130204_count_habits.js
Normal file
20
migrations/20130204_count_habits.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// %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,
|
||||
registered = { $or: [ { 'auth.local': { $exists: true } }, { 'auth.facebook': { $exists: true} } ]};
|
||||
|
||||
db.user.find(registered).forEach(function(u){
|
||||
//TODO this isn't working??
|
||||
habits += _.where(u.tasks, {type:'habit'}).length;
|
||||
dailies += _.where(u.tasks, {type:'daily'}).length;
|
||||
todos += _.where(u.tasks, {type:'todo'}).length;
|
||||
})
|
||||
102
migrations/20130204_user_public_private_paths.js
Normal file
102
migrations/20130204_user_public_private_paths.js
Normal file
@@ -0,0 +1,102 @@
|
||||
// %mongo server:27017/dbname underscore.js my_commands.js
|
||||
// %mongo server:27017/dbname underscore.js --shell
|
||||
|
||||
//db.users.find({'auth.facebook.email': 'tylerrenelle@gmail.com'}).forEach(function(user){
|
||||
db.users.find().forEach(function(user){
|
||||
|
||||
if (!user._id) {
|
||||
print("User has null _id");
|
||||
return; // need to figure out how to delete these buggers if they don't have an id to delete from
|
||||
}
|
||||
|
||||
if (!!user.idLists) {
|
||||
print("User " + user._id + " has already been migrated")
|
||||
return
|
||||
}
|
||||
|
||||
if (user._id.indexOf("$") === 0) {
|
||||
print("User id starts with $ (" + user._id + ")")
|
||||
return;
|
||||
}
|
||||
|
||||
// even though we're clobbering user later, sometimes these are undefined and crash the script
|
||||
// this saves us some ternaries
|
||||
user.stats = user.stats || {};
|
||||
user.items = user.items || {};
|
||||
user.preferences = user.preferences || {};
|
||||
user.notifications = user.notifications || {};
|
||||
user.flags = user.flags || {};
|
||||
user.habitIds = user.habitIds || [];
|
||||
user.dailyIds = user.dailyIds || [];
|
||||
user.todoIds = user.todoIds || [];
|
||||
user.rewardIds = user.rewardIds|| [];
|
||||
|
||||
_.each(user.tasks, function(task, key){
|
||||
if (!task.type) {
|
||||
delete user.tasks[key];
|
||||
// idList will take care of itself on page-load
|
||||
return
|
||||
}
|
||||
if (key == '$spec') {
|
||||
print("$spec was found: " + user._id);
|
||||
return
|
||||
}
|
||||
if (key.indexOf("$_") === 0) {
|
||||
var newKey = key.replace("$_", ''),
|
||||
index = user[task.type + "Ids"].indexOf(key)
|
||||
user[task.type + "Ids"][index] = newKey;
|
||||
task.id = newKey
|
||||
user.tasks[newKey] = task
|
||||
// TODO make sure this is ok, that we're not deleting the original
|
||||
// Otherwise use lodash.cloneDeep
|
||||
delete user.tasks[key]
|
||||
}
|
||||
});
|
||||
|
||||
// New user schema has public and private paths, so we can setup proper access control with racer
|
||||
// Note 'public' and 'private' are reserved words
|
||||
var newUser = {
|
||||
auth: user.auth, // we need this top-level due to derby-auth
|
||||
apiToken: user.preferences.api_token || null, // set on update, we need derby.uuid()
|
||||
preferences: {
|
||||
armorSet: user.preferences.armorSet || 'v1',
|
||||
gender: user.preferences.gender || 'm'
|
||||
},
|
||||
balance: user.balance || 2,
|
||||
lastCron: user.lastCron || +new Date,
|
||||
history: user.history || [],
|
||||
stats: {
|
||||
gp: user.stats.money || 0,
|
||||
hp: user.stats.hp || 50,
|
||||
exp: user.stats.exp || 0,
|
||||
lvl: user.stats.lvl || 1
|
||||
},
|
||||
items: {
|
||||
armor: user.items.armor || 0,
|
||||
weapon: user.items.weapon || 0
|
||||
},
|
||||
tasks: user.tasks || {},
|
||||
idLists: {
|
||||
habit: user.habitIds || [],
|
||||
daily: user.dailyIds || [],
|
||||
todo: user.todoIds || [],
|
||||
reward: user.rewardIds || []
|
||||
},
|
||||
flags: {
|
||||
partyEnabled: false,
|
||||
itemsEnabled: user.items.itemsEnabled || false,
|
||||
kickstarter: user.notifications.kickstarter || 'show',
|
||||
ads: user.flags.ads || null // null because it's set on registration
|
||||
},
|
||||
party: {
|
||||
current: null,
|
||||
invitation: null
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
db.users.update({_id:user._id}, newUser);
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
})
|
||||
19
migrations/20130208_idLists_to_typeIds.js
Normal file
19
migrations/20130208_idLists_to_typeIds.js
Normal file
@@ -0,0 +1,19 @@
|
||||
// move idList back to root-level, is what's causing the sort bug - see https://github.com/codeparty/racer/pull/73
|
||||
|
||||
// We could just delete user.idLists, since it's re-created on refresh. However, users's first refresh will scare them
|
||||
// since everything will dissappear - second refresh will bring everything back.
|
||||
db.users.find().forEach(function(user){
|
||||
if (!user.idLists) return;
|
||||
db.users.update(
|
||||
{_id:user._id},
|
||||
{
|
||||
$set:{
|
||||
'habitIds':user.idLists.habit,
|
||||
'dailyIds':user.idLists.daily,
|
||||
'todoIds':user.idLists.todo,
|
||||
'rewardIds':user.idLists.reward
|
||||
}
|
||||
//$unset:{idLists:true} // run this after the code has been pushed
|
||||
}
|
||||
)
|
||||
})
|
||||
20
migrations/20130208_user_customizations.js
Normal file
20
migrations/20130208_user_customizations.js
Normal file
@@ -0,0 +1,20 @@
|
||||
db.users.update(
|
||||
{items:{$exists:0}},
|
||||
{$set:{items:{weapon: 0, armor: 0, head: 0, shield: 0 }}},
|
||||
{multi:true}
|
||||
);
|
||||
|
||||
db.users.find().forEach(function(user){
|
||||
|
||||
var updates = {
|
||||
// I'm not racist, these were just the defaults before ;)
|
||||
'preferences.skin': 'white',
|
||||
'preferences.hair': 'blond',
|
||||
|
||||
'items.head': user.items.armor,
|
||||
'items.shield': user.items.armor,
|
||||
}
|
||||
|
||||
db.users.update({_id:user._id}, {$set:updates});
|
||||
|
||||
})
|
||||
39
migrations/20130307_exp_overflow.js
Normal file
39
migrations/20130307_exp_overflow.js
Normal file
@@ -0,0 +1,39 @@
|
||||
// mongo habitrpg ./node_modules/underscore/underscore.js ./migrations/20130307_normalize_algo_values.js
|
||||
|
||||
/**
|
||||
* Make sure people aren't overflowing their exp with the new system
|
||||
*/
|
||||
db.users.find().forEach(function(user){
|
||||
function oldTnl(level) {
|
||||
return (Math.pow(level,2)*10)+(level*10)+80
|
||||
}
|
||||
|
||||
function newTnl(level) {
|
||||
var value = 0;
|
||||
if (level >= 100) {
|
||||
value = 0
|
||||
} else {
|
||||
value = Math.round(((Math.pow(level,2)*0.25)+(10 * level) + 139.75)/10)*10; // round to nearest 10
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
var newTnl = newTnl(user.stats.lvl);
|
||||
if (user.stats.exp > newTnl) {
|
||||
var percent = user.stats.exp / oldTnl(user.stats.lvl);
|
||||
percent = (percent>1) ? 1 : percent;
|
||||
user.stats.exp = newTnl * percent;
|
||||
|
||||
try {
|
||||
db.users.update(
|
||||
{_id:user._id},
|
||||
{$set: {'stats.exp': user.stats.exp}},
|
||||
{multi:true}
|
||||
);
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
47
migrations/20130307_normalize_algo_values.js
Normal file
47
migrations/20130307_normalize_algo_values.js
Normal file
@@ -0,0 +1,47 @@
|
||||
// mongo habitrpg ./node_modules/underscore/underscore.js ./migrations/20130307_normalize_algo_values.js
|
||||
|
||||
/**
|
||||
* Users were experiencing a lot of extreme Exp multiplication (https://github.com/lefnire/habitrpg/issues/594).
|
||||
* This sets things straight, and in preparation for another algorithm overhaul
|
||||
*/
|
||||
db.users.find().forEach(function(user){
|
||||
if (user.stats.exp >= 3580) {
|
||||
user.stats.exp = 0;
|
||||
}
|
||||
|
||||
if (user.stats.lvl > 100) {
|
||||
user.stats.lvl = 100;
|
||||
}
|
||||
|
||||
_.each(user.tasks, function(task, key){
|
||||
// remove corrupt tasks
|
||||
if (!task) {
|
||||
delete user.tasks[key];
|
||||
return;
|
||||
}
|
||||
|
||||
// Fix busted values
|
||||
if (task.value > 21.27) {
|
||||
task.value = 21.27;
|
||||
}
|
||||
else if (task.value < -47.27) {
|
||||
task.value = -47.27;
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
db.users.update(
|
||||
{_id:user._id},
|
||||
{$set:
|
||||
{
|
||||
'stats.lvl': user.stats.lvl,
|
||||
'stats.exp': user.stats.exp,
|
||||
'tasks' : user.tasks
|
||||
}
|
||||
},
|
||||
{multi:true}
|
||||
);
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
})
|
||||
28
migrations/20130307_remove_duff_histories.js
Normal file
28
migrations/20130307_remove_duff_histories.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Remove duff histories for dailies
|
||||
*/
|
||||
// mongo habitrpg ./node_modules/underscore/underscore.js ./migrations/20130307_remove_duff_histories.js
|
||||
db.users.find().forEach(function(user){
|
||||
|
||||
|
||||
_.each(user.tasks, function(task, key){
|
||||
if (task.type === "daily") {
|
||||
// remove busted history entries
|
||||
task.history = _.filter(task.history, function(h){return !!h.value})
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
db.users.update(
|
||||
{_id:user._id},
|
||||
{$set:
|
||||
{
|
||||
'tasks' : user.tasks
|
||||
}
|
||||
},
|
||||
{multi:true}
|
||||
);
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
})
|
||||
98
migrations/20130326_migrate_pets.js
Normal file
98
migrations/20130326_migrate_pets.js
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Migrate old pets to new system
|
||||
*/
|
||||
// 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'},
|
||||
dragon: {name:'Dragon', modifier:'Base'},
|
||||
flyingpig: {name:'FlyingPig', modifier:'Base'},
|
||||
fox: {name:'Fox', modifier:'Base'},
|
||||
lioncub: {name:'LionCub', modifier:'Base'},
|
||||
pandacub: {name:'PandaCub', modifier:'Base'},
|
||||
tigercub: {name:'TigerCub', modifier:'Base'},
|
||||
wolfBorder: {name:'Wolf', modifier:'Base'},
|
||||
wolfDesert: {name:'Wolf', modifier:'Desert'},
|
||||
wolfGolden: {name:'Wolf', modifier:'Golden'},
|
||||
wolfRed: {name:'Wolf', modifier:'Red'},
|
||||
wolfShade: {name:'Wolf', modifier:'Shade'},
|
||||
wolfSkeleton: {name:'Wolf', modifier:'Skeleton'},
|
||||
wolfVeteran: {name:'Wolf', modifier:'Veteran'},
|
||||
wolfWhite: {name:'Wolf', modifier:'White'},
|
||||
wolfZombie: {name:'Wolf', modifier:'Zombie'}
|
||||
}
|
||||
|
||||
/**
|
||||
== Old Style ==
|
||||
pet: Object
|
||||
icon: "Pet-Wolf-White.png"
|
||||
index: 14
|
||||
name: "wolfWhite"
|
||||
text: "White Wolf"
|
||||
value: 3
|
||||
pets: Object
|
||||
bearcub: true
|
||||
cactus: true
|
||||
|
||||
== New Style ==
|
||||
currentPet: Object
|
||||
modifier: "Red"
|
||||
name: "Wolf"
|
||||
notes: "Find some Hatching Powder to sprinkle on this egg, and one day it will hatch into a loyal pet."
|
||||
str: "Wolf-Red"
|
||||
text: "Wolf"
|
||||
value: 3
|
||||
pets: Array
|
||||
0: "PandaCub-Base"
|
||||
1: "Wolf-Base"
|
||||
*/
|
||||
|
||||
|
||||
db.users.find().forEach(function(user){
|
||||
if (!user.items || (!user.items.pets && !user.items.pet)) return;
|
||||
|
||||
// migrate items.pet to items.currentPet
|
||||
if (!!user.items.pet) {
|
||||
var mapped = mapping[user.items.pet.name];
|
||||
delete user.items.pet;
|
||||
user.items.currentPet = {
|
||||
modifier: mapped.modifier,
|
||||
name: mapped.name,
|
||||
str: mapped.name + "-" + mapped.modifier,
|
||||
text: '' // FIXME?
|
||||
}
|
||||
}
|
||||
|
||||
// migrate items.pets
|
||||
if (!!user.items.pets) {
|
||||
var newPets = [];
|
||||
_.each(user.items.pets, function(val, key){
|
||||
if (_.isNumber(key)) {
|
||||
newPets.push(val)
|
||||
//FIXME why is this happening? seems the user gets migrated already...
|
||||
//throw "Error: User appears already migrated, this shouldn't be happening!"
|
||||
} else {
|
||||
newPets.push(mapping[key].name + "-" + mapping[key].modifier);
|
||||
}
|
||||
});
|
||||
user.items.pets = newPets;
|
||||
}
|
||||
|
||||
try {
|
||||
db.users.update(
|
||||
{_id:user._id},
|
||||
{$set:
|
||||
{ 'items' : user.items }
|
||||
}
|
||||
);
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
})
|
||||
110
migrations/20130327_apply_tokens.js
Normal file
110
migrations/20130327_apply_tokens.js
Normal file
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Applies backer tokens & items (this file will be updated periodically
|
||||
*/
|
||||
|
||||
// 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,
|
||||
tokens: 0,
|
||||
users: []
|
||||
},
|
||||
{
|
||||
tier: 5,
|
||||
tokens: 20,
|
||||
users: []
|
||||
},
|
||||
{
|
||||
tier: 10,
|
||||
tokens: 50,
|
||||
users: []
|
||||
},
|
||||
{
|
||||
tier: 15,
|
||||
tokens: 100,
|
||||
users: []
|
||||
},
|
||||
{
|
||||
tier: 30,
|
||||
tokens: 150,
|
||||
users: []
|
||||
},
|
||||
{
|
||||
tier: 45,
|
||||
tokens: 170,
|
||||
users: []
|
||||
},
|
||||
{
|
||||
tier: 60,
|
||||
tokens: 200,
|
||||
users: []
|
||||
},
|
||||
{
|
||||
tier: 70,
|
||||
tokens: 240,
|
||||
users: []
|
||||
},
|
||||
{
|
||||
tier: 80,
|
||||
tokens: 240,
|
||||
users: []
|
||||
},
|
||||
{
|
||||
tier: 90,
|
||||
tokens: 280,
|
||||
users: []
|
||||
},
|
||||
{
|
||||
tier: 300,
|
||||
tokens: 500,
|
||||
users: []
|
||||
},
|
||||
{
|
||||
tier: 800,
|
||||
tokens: 500,
|
||||
users: []
|
||||
}
|
||||
];
|
||||
|
||||
db.users.find().forEach(function(user){
|
||||
if (!user._id) return;
|
||||
|
||||
var possibleUserIds = [user._id];
|
||||
if (!!user.local) {
|
||||
if (!!user.local.username) possibleUserIds.push(user.local.username);
|
||||
if (!!user.local.email) possibleUserIds.push(user.local.email);
|
||||
}
|
||||
|
||||
_.each(mapping, function(tier){
|
||||
var userInTier = !_.isEmpty(_.intersection(tier.users, possibleUserIds));
|
||||
if (userInTier) {
|
||||
var tokenInc = 0,
|
||||
backer = user.backer || {};
|
||||
if (!backer.tokensApplied) {
|
||||
tokenInc = tier.tokens;
|
||||
backer.tokensApplied = true;
|
||||
}
|
||||
backer.tier = tier.tier;
|
||||
|
||||
try {
|
||||
db.users.update(
|
||||
{_id:user._id},
|
||||
{
|
||||
$set: { backer: backer, 'flags.ads': 'hide' },
|
||||
$inc: { balance: (tokenInc/4) }
|
||||
}
|
||||
);
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
22
migrations/20130503_max_gear_achievement.js
Normal file
22
migrations/20130503_max_gear_achievement.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* For users who already have max gear, they earned the achievement
|
||||
*/
|
||||
// mongo habitrpg ./node_modules/underscore/underscore.js ./migrations/20130503_max_gear_achievement.js
|
||||
db.users.find().forEach(function(user){
|
||||
var items = user.items;
|
||||
if (!items) { return; }
|
||||
if ( parseInt(items.armor) == 5 &&
|
||||
parseInt(items.head) == 5 &&
|
||||
parseInt(items.shield) == 5 &&
|
||||
parseInt(items.weapon) == 6) {
|
||||
|
||||
try {
|
||||
db.users.update(
|
||||
{_id:user._id},
|
||||
{$set: {'achievements.ultimateGear':true}}
|
||||
);
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
})
|
||||
12
migrations/20130507_fix_broken_tags.js
Normal file
12
migrations/20130507_fix_broken_tags.js
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* users getting broken tags when they try to edit the first blank tag on accident
|
||||
*
|
||||
* mongo habitrpg ./node_modules/underscore/underscore.js ./migrations/20130507_fix_broken_tags.js
|
||||
*/
|
||||
|
||||
db.users.find().forEach(function(user){
|
||||
if(!_.isArray(user.tags)) {
|
||||
db.users.update({_id:user._id}, {$set:{tags:[]}});
|
||||
}
|
||||
|
||||
})
|
||||
1
migrations/20130508_add_backer_pets.js
Normal file
1
migrations/20130508_add_backer_pets.js
Normal file
@@ -0,0 +1 @@
|
||||
db.users.update({'backer.tier':{$gte:80}}, {$push:{'items.pets':'Wolf-Cerberus'}}, {multi:true});
|
||||
41
migrations/20130508_fix_duff_party_subscriptions.js
Normal file
41
migrations/20130508_fix_duff_party_subscriptions.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 745612d and fedc5b6 added a db-subscription optimization to the initial subscribe.
|
||||
* However, it requires the user only be to one party. That should be the case anyway, but user.party.current was letting
|
||||
* us look past the fact that a user was erroneously subscribed to multiple parties. This fixes
|
||||
*
|
||||
* 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} );
|
||||
|
||||
db.parties.find().forEach(function(party){
|
||||
|
||||
if(!party.members) {
|
||||
return db.parties.remove({_id:party._id});
|
||||
}
|
||||
|
||||
// Find all members
|
||||
db.users.find( {_id: {$in:party.members} }, {_id:1,party:1} ).forEach(function(user){
|
||||
// user somehow is subscribed to this party in the background, but they're it's not their primary party
|
||||
if (user.party && user.party.current !== party._id) {
|
||||
var i = party.members.indexOf(user._id);
|
||||
party.members.splice(i, 1);
|
||||
}
|
||||
|
||||
// if after we remove the user, the party is empty - delete this party
|
||||
if (_.isEmpty(party.members)) {
|
||||
db.parties.remove({_id:party._id});
|
||||
|
||||
// else just set it
|
||||
} else {
|
||||
db.parties.update({_id:party._id}, {$set:{members:party.members}});
|
||||
}
|
||||
})
|
||||
})
|
||||
48
migrations/20130518_setup_groups.js
Normal file
48
migrations/20130518_setup_groups.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* In adding the Guilds feature (which supports the Challenges feature), we are consolidating parties and guilds
|
||||
* into one collection: groups, with group.type either 'party' or 'guild'. We are also creating the 'habitrpg' guild,
|
||||
* which everyone is auto-subscribed to, and moving tavern chat into that guild
|
||||
*
|
||||
* mongo habitrpg ./node_modules/lodash/lodash.js ./migrations/20130518_setup_groups.js
|
||||
*/
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* 1) rename collection parties => groups
|
||||
* 2) add group.type = 'party' for each current group
|
||||
* 3) create habitrpg group, .type='guild'
|
||||
* 4) move tavern.chat.chat into habitrpg guild
|
||||
* 5) subscribe everyone to habitrpg (be sure to set that for default user too!)
|
||||
*/
|
||||
|
||||
db.parties.renameCollection('groups',true);
|
||||
//db.parties.dropCollection(); // doesn't seem to do this step during rename...
|
||||
//db.parties.ensureIndex( { 'members': 1, 'background': 1} );
|
||||
|
||||
db.groups.update({}, {$set:{type:'party'}}, {multi:true});
|
||||
|
||||
//migrate invitation mechanisms
|
||||
db.users.update(
|
||||
{},
|
||||
{
|
||||
$remove:{party:1},
|
||||
$set:{invitations:{party:null,guilds:[]}}
|
||||
},
|
||||
{multi:1}
|
||||
);
|
||||
|
||||
tavern = db.tavern.findOne();
|
||||
db.tavern.drop();
|
||||
|
||||
//TODO make as a callback of previous, or make sure group.type is still 'guild' for habitrpg in the end
|
||||
db.groups.insert({
|
||||
_id: "habitrpg",
|
||||
leader: '9',
|
||||
type: 'guild',
|
||||
name: "HabitRPG",
|
||||
chat: tavern.messages,
|
||||
info: {
|
||||
blurb: '',
|
||||
websites: []
|
||||
}
|
||||
});
|
||||
31
migrations/20130602_survey_rewards.js
Normal file
31
migrations/20130602_survey_rewards.js
Normal file
@@ -0,0 +1,31 @@
|
||||
//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);
|
||||
|
||||
var query = {
|
||||
_id: {$exists:1},
|
||||
$or:[
|
||||
{_id: {$in: members}},
|
||||
//{'profile.name': {$in: members}},
|
||||
{'auth.facebook.name': {$in: members}},
|
||||
{'auth.local.username': {$in: members}},
|
||||
{'auth.local.email': {$in: members}}
|
||||
]
|
||||
};
|
||||
|
||||
print(db.users.count(query));
|
||||
|
||||
db.users.update(query,
|
||||
{
|
||||
$set: { 'achievements.helpedHabit': true },
|
||||
$inc: { balance: 2.5 }
|
||||
},
|
||||
{multi:true}
|
||||
)
|
||||
9
migrations/20130612_survey_rewards_individual.js
Normal file
9
migrations/20130612_survey_rewards_individual.js
Normal file
@@ -0,0 +1,9 @@
|
||||
//mongo habitrpg migrations/20130612_survey_rewards_individual.js
|
||||
|
||||
var query = {_id: ""};
|
||||
|
||||
db.users.update(query,
|
||||
{
|
||||
$set: { 'achievements.helpedHabit': true },
|
||||
$inc: { balance: 2.5 }
|
||||
})
|
||||
4
migrations/20130615_add_extra_indexes.js
Normal file
4
migrations/20130615_add_extra_indexes.js
Normal file
@@ -0,0 +1,4 @@
|
||||
db.users.ensureIndex( { _id: 1, apiToken: 1 }, {background: true} )
|
||||
db.groups.ensureIndex( { members: 1 }, {background: true} )
|
||||
db.groups.ensureIndex( { type: 1 }, {background: true} )
|
||||
db.groups.ensureIndex( { type: 1, privacy: 1 }, {background: true} )
|
||||
16
migrations/20130908_cleanup_corrupt_tags.js
Normal file
16
migrations/20130908_cleanup_corrupt_tags.js
Normal file
@@ -0,0 +1,16 @@
|
||||
//mongo habitrpg ./node_modules/lodash/lodash.js migrations/20130908_cleanup_corrupt_tags.js
|
||||
|
||||
// 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
|
||||
|
||||
db.users.find().forEach(function(user){
|
||||
user.tags = _.filter(user.tags, (function(t) {
|
||||
return !!t ? t.id : false;
|
||||
}));
|
||||
|
||||
try {
|
||||
db.users.update({_id:user._id}, {$set:{tags:user.tags}});
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
})
|
||||
57
migrations/20130908_cleanup_derby_corruption.js
Normal file
57
migrations/20130908_cleanup_derby_corruption.js
Normal file
@@ -0,0 +1,57 @@
|
||||
//mongo habitrpg ./node_modules/lodash/lodash.js migrations/20130908_cleanup_derby_corruption.js
|
||||
|
||||
// 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
|
||||
user.tasks = _.reduce(user.tasks, function(m,task,k) {
|
||||
if (!task || !task.id) return m;
|
||||
if (isNaN(+task.value)) task.value = 0;
|
||||
m[k] = task;
|
||||
return m;
|
||||
}, {});
|
||||
|
||||
// fix NaN stats
|
||||
_.each(user.stats, function(v,k) {
|
||||
if (!v || isNaN(+v)) user.stats[k] = 0;
|
||||
return true;
|
||||
});
|
||||
|
||||
// remove duplicates, restore ghost tasks
|
||||
['habit', 'daily', 'todo', 'reward'].forEach(function(type) {
|
||||
var idList = user[type + "Ids"];
|
||||
var taskIds = _.pluck(_.where(user.tasks, {type: type}), 'id');
|
||||
var union = _.union(idList, taskIds);
|
||||
var preened = _.filter(union, function(id) {
|
||||
return id && _.contains(taskIds, id);
|
||||
});
|
||||
if (!_.isEqual(idList, preened)) {
|
||||
user[type + "Ids"] = preened;
|
||||
}
|
||||
});
|
||||
|
||||
// temporarily remove broken eggs. we'll need to write a migration script to grant gems for and remove these instead
|
||||
if (user.items && user.items.eggs) {
|
||||
user.items.eggs = _.filter(user.items.eggs,function(egg){
|
||||
if (_.isString(egg)) {
|
||||
user.balance += 0.75; // give them 3 gems for each broken egg
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
db.users.update({_id:user._id}, user);
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
})
|
||||
@@ -8,7 +8,7 @@
|
||||
* If we experience any troubles with removed staging users, come back to a snapshot and restore accounts. This will
|
||||
* give a peak into possible conflict accounts:
|
||||
*/
|
||||
/* db.users.count({
|
||||
/*db.users.count({
|
||||
"auth.local": {$exists: false},
|
||||
"auth.facebook": {$exists: false},
|
||||
"history.exp.5": {$exists: 1},
|
||||
@@ -22,9 +22,9 @@
|
||||
* in we'll be using localStorage anyway instead of creating a new database record
|
||||
*/
|
||||
db.users.remove({
|
||||
// Un-registered users
|
||||
'auth.local': {$exists: false},
|
||||
'auth.facebook': {$exists: false},
|
||||
// Un-registered users
|
||||
"auth.local": {$exists: false},
|
||||
"auth.facebook": {$exists: false}
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -32,6 +32,6 @@ db.users.remove({
|
||||
* Another vestige of Racer. Empty parties shouldn't be being created anymore in the new site
|
||||
*/
|
||||
db.groups.remove({
|
||||
type: 'party',
|
||||
$where: 'return this.members.length === 0',
|
||||
'type': 'party',
|
||||
$where: "return this.members.length === 0"
|
||||
});
|
||||
5
migrations/20131022_purchased_and_newStuff.js
Normal file
5
migrations/20131022_purchased_and_newStuff.js
Normal file
@@ -0,0 +1,5 @@
|
||||
db.users.find().forEach(function(user){
|
||||
if (!user.purchased) user.purchased = {hair: {}, skin: {}};
|
||||
user.purchased.ads = user.flags && !!user.flags.ads;
|
||||
db.users.update({_id:user._id}, {$set:{'purchased': user.purchased, 'flags.newStuff': true}, $unset: {'flags.ads':1}});
|
||||
});
|
||||
12
migrations/20131022_restore_ads.js
Normal file
12
migrations/20131022_restore_ads.js
Normal file
@@ -0,0 +1,12 @@
|
||||
// node .migrations/20131022_restore_ads.js
|
||||
var mongo = require('mongoskin');
|
||||
var _ = require('lodash');
|
||||
var dbBackup = mongo.db('localhost:27017/habitrpg?auto_reconnect');
|
||||
var dbLive = mongo.db('localhost:27017/habitrpg2?auto_reconnect');
|
||||
var count = 89474;
|
||||
dbBackup.collection('users').findEach({$or: [{'flags.ads':'show'}, {'flags.ads': null}]}, {batchSize:10}, function(err, item) {
|
||||
if (err) return console.error({err:err});
|
||||
if (!item || !item._id) return console.error('blank user');
|
||||
dbLive.collection('users').update({_id:item._id}, {$set:{'purchased.ads':false}, $unset: {'flags.ads': 1}});
|
||||
if (--count <= 0) console.log("DONE!");
|
||||
});
|
||||
132
migrations/20131028_task_subdocs_tags_invites.js
Normal file
132
migrations/20131028_task_subdocs_tags_invites.js
Normal file
@@ -0,0 +1,132 @@
|
||||
// mongo habitrpg ./node_modules/lodash/lodash.js ./migrations/20131028_task_subdocs_tags_invites.js
|
||||
|
||||
// TODO it might be better we just find() and save() all user objects using mongoose, and rely on our defined pre('save')
|
||||
// and default values to "migrate" users. This way we can make sure those parts are working properly too
|
||||
// @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
|
||||
// -------------------------
|
||||
if(user.invitations){
|
||||
if(user.invitations.party){
|
||||
db.groups.update({_id: user.invitations.party.id}, {$addToSet:{invites:user._id}});
|
||||
}
|
||||
|
||||
if(user.invitations.guilds){
|
||||
_.each(user.invitations.guilds, function(guild){
|
||||
db.groups.update({_id: guild.id}, {$addToSet:{invites:user._id}});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup broken tags
|
||||
// -------------------------
|
||||
_.each(user.tasks, function(task){
|
||||
_.each(task.tags, function(val, key){
|
||||
_.each(user.tags, function(tag){
|
||||
if(key == tag.id) delete task.tags[key];
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Fix corrupt dates
|
||||
// -------------------------
|
||||
user.lastCron = new Date(user.lastCron);
|
||||
if (user.lastCron == 'Invalid Date') user.lastCron = new Date();
|
||||
if (user.auth) { // what to do with !auth?
|
||||
_.defaults(user.auth, {timestamps: {created:undefined, loggedin: undefined}});
|
||||
_.defaults(user.auth.timestamps, {created: new Date(user.lastCron), loggedin: new Date(user.lastCron)});
|
||||
}
|
||||
|
||||
// Fix missing history
|
||||
// -------------------------
|
||||
_.defaults(user, {history:{}});
|
||||
_.defaults(user.history,{exp:[], todos:[]});
|
||||
|
||||
// Add username
|
||||
// -------------------------
|
||||
if (!user.profile) user.profile = {name:undefined};
|
||||
if (_.isEmpty(user.profile.name) && user.auth) {
|
||||
var fb = user.auth.facebook;
|
||||
user.profile.name =
|
||||
(user.auth.local && user.auth.local.username) ||
|
||||
(fb && (fb.displayName || fb.name || fb.username || (fb.first_name && fb.first_name + ' ' + fb.last_name))) ||
|
||||
'Anonymous';
|
||||
}
|
||||
|
||||
// Migrate to TaskSchema Sub-Docs!
|
||||
// -------------------------
|
||||
if (!user.tasks) {
|
||||
// So evidentaly users before 02/2013 were ALREADY setup based on habits[], dailys[], etcs... I don't remember our schema
|
||||
// ever being that way... Anyway, print busted users here (they don't have tasks, but also don't have the right schema)
|
||||
if (!user.habits || !user.dailys || !user.todos || !user.rewards) {
|
||||
print(user._id);
|
||||
}
|
||||
} else {
|
||||
_.each(['habit', 'daily', 'todo', 'reward'], function(type) {
|
||||
// we use _.transform instead of a simple _.where in order to maintain sort-order
|
||||
user[type + "s"] = _.reduce(user[type + "Ids"], function(m, tid) {
|
||||
var task = user.tasks[tid],
|
||||
newTask = {};
|
||||
if (!task) return m; // remove null tasks
|
||||
|
||||
// Cleanup tasks for TaskSchema
|
||||
newTask._id = newTask.id = task.id;
|
||||
newTask.text = (_.isString(task.text)) ? task.text : '';
|
||||
if (_.isString(task.notes)) newTask.notes = task.notes;
|
||||
newTask.tags = (_.isObject(task.tags)) ? task.tags : {};
|
||||
newTask.type = (_.isString(task.type)) ? task.type : 'habit';
|
||||
newTask.value = (_.isNumber(task.value)) ? task.value : 0;
|
||||
newTask.priority = (_.isString(task.priority)) ? task.priority : '!';
|
||||
switch (newTask.type) {
|
||||
case 'habit':
|
||||
newTask.up = (_.isBoolean(task.up)) ? task.up : true;
|
||||
newTask.down = (_.isBoolean(task.down)) ? task.down : true;
|
||||
newTask.history = (_.isArray(task.history)) ? task.history : [];
|
||||
break;
|
||||
case 'daily':
|
||||
newTask.repeat = (_.isObject(task.repeat)) ? task.repeat : {m:1, t:1, w:1, th:1, f:1, s:1, su:1};
|
||||
newTask.streak = (_.isNumber(task.streak)) ? task.streak : 0;
|
||||
newTask.completed = (_.isBoolean(task.completed)) ? task.completed : false;
|
||||
newTask.history = (_.isArray(task.history)) ? task.history : [];
|
||||
break;
|
||||
case 'todo':
|
||||
newTask.completed = (_.isBoolean(task.completed)) ? task.completed : false;
|
||||
break;
|
||||
}
|
||||
|
||||
m.push(newTask);
|
||||
return m;
|
||||
}, []);
|
||||
delete user[type + 'Ids'];
|
||||
});
|
||||
delete user.tasks;
|
||||
}
|
||||
|
||||
try {
|
||||
db.users.update({_id:user._id}, user);
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
});
|
||||
|
||||
// Remove old groups.*.challenges, they're not compatible with the new system, set member counts
|
||||
// -------------------------
|
||||
db.groups.find().forEach(function(group){
|
||||
db.groups.update({_id:group._id}, {
|
||||
$set:{memberCount: _.size(group.members)},
|
||||
$pull:{challenges:1}
|
||||
})
|
||||
});
|
||||
|
||||
// HabitRPG => Tavern
|
||||
// -------------------------
|
||||
db.groups.update({_id:'habitrpg'}, {$set:{name:'Tavern'}});
|
||||
25
migrations/20131102_restore_task_ids.js
Normal file
25
migrations/20131102_restore_task_ids.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// mongo habitrpg ./node_modules/lodash/lodash.js ./migrations/20131028_task_subdocs_tags_invites.js
|
||||
|
||||
db.challenges.find().forEach(function(chal){
|
||||
_.each(chal.habits.concat(chal.dailys).concat(chal.todos).concat(chal.rewards), function(task){
|
||||
task.id = task.id || task._id;
|
||||
})
|
||||
try {
|
||||
db.challenges.update({_id:chal._id}, chal);
|
||||
db.groups.update({_id:chal.group}, {$addToSet:{challenges:chal._id}})
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
});
|
||||
|
||||
db.users.find().forEach(function(user){
|
||||
_.each(user.habits.concat(user.dailys).concat(user.todos).concat(user.rewards), function(task){
|
||||
task.id = task.id || task._id;
|
||||
})
|
||||
try {
|
||||
db.users.update({_id:user._id}, user);
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
});
|
||||
|
||||
7
migrations/20131104_remove_invalid_dues.js
Normal file
7
migrations/20131104_remove_invalid_dues.js
Normal file
@@ -0,0 +1,7 @@
|
||||
db.users.find({},{todos:1}).forEach(function(user){
|
||||
_.each(user.todos, function(task){
|
||||
if (moment(task.date).toDate() == 'Invalid Date')
|
||||
task.date = moment().format('MM/DD/YYYY');
|
||||
})
|
||||
db.users.update({_id:user._id}, {$set:{todos: user.todos}});
|
||||
});
|
||||
60
migrations/20131104_restore_lost_task_data.js
Normal file
60
migrations/20131104_restore_lost_task_data.js
Normal file
@@ -0,0 +1,60 @@
|
||||
// node .migrations/20131104_restore_lost_task_data.js
|
||||
|
||||
/**
|
||||
* After the great challenges migration, quite a few things got inadvertently dropped from tasks since their
|
||||
* schemas became more strict. See conversation at https://github.com/HabitRPG/habitrpg/issues/1712 ,
|
||||
* this restores task tags, streaks, due-dates, values
|
||||
*/
|
||||
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');
|
||||
|
||||
backupUsers.count(function(err, count){
|
||||
if (err) return console.error(err);
|
||||
backupUsers.findEach({}, {batchSize:250}, function(err, before){
|
||||
if (err) return console.error(err);
|
||||
if (!before) return console.log('!before');
|
||||
liveUsers.findById(before._id, function(err, after){
|
||||
if (err) return console.error(err);
|
||||
if (!after) {
|
||||
count--;
|
||||
return console.log(before._id + ' deleted?');
|
||||
}
|
||||
if (before._id == '9') console.log('lefnire processed');
|
||||
_.each(before.tasks, function(tBefore){
|
||||
var tAfter = _.find(after[tBefore.type+'s'], {id:tBefore.id});
|
||||
if (!tAfter) return; // task has been deleted since launch
|
||||
|
||||
// Restore deleted tags
|
||||
if (!_.isEmpty(tBefore.tags) && _.isEmpty(tAfter.tags))
|
||||
tAfter.tags = tBefore.tags;
|
||||
// Except tags which are no longer available on the updated user
|
||||
_.each(tAfter.tags, function(v,k){ //value is true, key is tag.id
|
||||
if (!_.find(after.tags,{id:k})) delete tAfter.tags[k];
|
||||
})
|
||||
|
||||
// Restore deleted streaks
|
||||
if (+tBefore.streak > tAfter.streak)
|
||||
tAfter.streak = +tBefore.streak;
|
||||
|
||||
if (!!tBefore.date && !tAfter.date)
|
||||
tAfter.date = tBefore.date;
|
||||
|
||||
// Restore deleted values
|
||||
if (+tBefore.value != 0 && tAfter.value == 0)
|
||||
tAfter.value = +tBefore.value;
|
||||
})
|
||||
after._v++;
|
||||
liveUsers.update({_id:after._id}, after);
|
||||
if (--count <= 0) console.log("DONE!");
|
||||
})
|
||||
});
|
||||
});
|
||||
21
migrations/20131105_remove_history_ids.js
Normal file
21
migrations/20131105_remove_history_ids.js
Normal file
@@ -0,0 +1,21 @@
|
||||
function deleteId(h){
|
||||
delete h._id;
|
||||
}
|
||||
|
||||
db.users.find({},{habits:1,dailys:1,history:1}).forEach(function(user){
|
||||
if (user.history) {
|
||||
_.each(['todos','exp'], function(type){
|
||||
if (user.history[type]) {
|
||||
_.each(user.history.exp, deleteId);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
user.history = {exp:[],todos:[]};
|
||||
}
|
||||
|
||||
_.each(['habits', 'dailys'], function(type){
|
||||
_.each(user[type].history, deleteId);
|
||||
});
|
||||
|
||||
db.users.update({_id:user._id}, {$set:{history: user.history, habits: user.habits, dailys: user.dailys}});
|
||||
});
|
||||
18
migrations/20131107_from_backer_to_contributor.js
Normal file
18
migrations/20131107_from_backer_to_contributor.js
Normal file
@@ -0,0 +1,18 @@
|
||||
db.users.find({
|
||||
$or: [
|
||||
{'backer.admin':{$exists:1}},
|
||||
{'backer.contributor':{$exists:1}}
|
||||
]
|
||||
},{backer:1}).forEach(function(user){
|
||||
user.contributor = {};
|
||||
user.contributor.admin = user.backer.admin;
|
||||
delete user.backer.admin;
|
||||
|
||||
// this isnt' the proper storage format, but I'm going to be going through the admin utility manually and setting things properly
|
||||
if (user.backer.contributor) {
|
||||
user.contributor.text = user.backer.contributor;
|
||||
delete user.backer.contributor;
|
||||
}
|
||||
|
||||
db.users.update({_id:user._id}, {$set:{backer:user.backer, contributor:user.contributor}});
|
||||
});
|
||||
4
migrations/20131108_add_gems_for_contribs.js
Normal file
4
migrations/20131108_add_gems_for_contribs.js
Normal file
@@ -0,0 +1,4 @@
|
||||
// Increase everyone's gems per their contribution level
|
||||
db.users.find({'contributor.level':{$gt:0}},{contributor:1, balance:1}).forEach(function(user){
|
||||
db.users.update({_id:user._id}, {$inc: {balance: (user.contributor.level * .5)} });
|
||||
});
|
||||
39
migrations/20131109_refactor_pets.js
Normal file
39
migrations/20131109_refactor_pets.js
Normal file
@@ -0,0 +1,39 @@
|
||||
db.users.find(
|
||||
{$where: "Array.isArray(this.items.pets) || Array.isArray(this.items.eggs) || Array.isArray(this.items.hatchingPotions)"},
|
||||
{backer: 1, items:1}
|
||||
).forEach(function(user){
|
||||
|
||||
if (_.isArray(user.items.pets)) {
|
||||
user.items.pets = _.reduce(user.items.pets, function(m,v){ m[v] = 5; return m;}, {});
|
||||
}
|
||||
|
||||
if (!_.isString(user.items.currentPet)) {
|
||||
user.items.currentPet = user.items.currentPet ? user.items.currentPet.str : '';
|
||||
}
|
||||
|
||||
if (_.isArray(user.items.eggs)) {
|
||||
user.items.eggs = _.reduce(user.items.eggs, function(m,v){
|
||||
if (!m[v.name]) m[v.name] = 0;
|
||||
m[v.name]++;
|
||||
return m;
|
||||
}, {});
|
||||
}
|
||||
|
||||
if (_.isArray(user.items.hatchingPotions)) {
|
||||
user.items.hatchingPotions = _.reduce(user.items.hatchingPotions, function(m,v){
|
||||
if (!m[v]) m[v] = 0;
|
||||
m[v]++;
|
||||
return m;
|
||||
}, {});
|
||||
}
|
||||
|
||||
user.items.food = {};
|
||||
user.items.mounts = {};
|
||||
user.items.currentMount = '';
|
||||
|
||||
if (user.backer && user.backer.tier && user.backer.tier >= 90) {
|
||||
user.items.mounts['LionCub-Ethereal'] = true;
|
||||
}
|
||||
|
||||
db.users.update({_id:user._id}, {$set:{items:user.items}});
|
||||
});
|
||||
15
migrations/20131111_task_NaN.js
Normal file
15
migrations/20131111_task_NaN.js
Normal file
@@ -0,0 +1,15 @@
|
||||
// This migration has already been run in the past. It's vital to fix these users presently, but we need to find
|
||||
// out why task values are ever getting in as NaN. My guess is API PUT /tasks/:tid routes
|
||||
db.users.find({},{habits:1,dailys:1,todos:1,rewards:1}).forEach(function(user){
|
||||
_.each(['habits','dailys','todos','rewards'], function(type){
|
||||
_.each(user[type], function(task){
|
||||
task.value = +task.value;
|
||||
if (_.isNaN(task.value)) {
|
||||
task.value = 0;
|
||||
print(user._id);
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
db.users.update({_id:user._id}, {$set:{habits: user.habits, dailys: user.dailys, todos: user.todos, rewards: user.rewards}});
|
||||
});
|
||||
14
migrations/20131114_migrate_websites_to_blurb.js
Normal file
14
migrations/20131114_migrate_websites_to_blurb.js
Normal file
@@ -0,0 +1,14 @@
|
||||
// Migrate all users websites to the profile blurb field
|
||||
db.users.find({'profile.websites':{$exists: true}}).forEach(function(user){
|
||||
db.users.update({_id: user._id}, {
|
||||
$set: {"profile.blurb": user.profile.blurb + '\n * ' + user.profile.websites.join('\n * ')},
|
||||
$unset: {'profile.websites': 1}
|
||||
})
|
||||
})
|
||||
|
||||
db.groups.find({'websites.0':{$exists: true}}).forEach(function(group){
|
||||
db.groups.update({_id: group._id}, {
|
||||
$set: {"description": group.description + '\n * ' + group.websites.join('\n * ')},
|
||||
$unset: {websites: 1}
|
||||
})
|
||||
})
|
||||
10
migrations/20131115_update_gear_preferences.js
Normal file
10
migrations/20131115_update_gear_preferences.js
Normal file
@@ -0,0 +1,10 @@
|
||||
//Add defaults to show gears in all users
|
||||
db.users.update(
|
||||
{},
|
||||
{$set:{
|
||||
'preferences.showWeapon': true,
|
||||
'preferences.showShield': true,
|
||||
'preferences.showArmor': true,
|
||||
}},
|
||||
{multi:true}
|
||||
)
|
||||
18
migrations/20131117_fix_task_types.js
Normal file
18
migrations/20131117_fix_task_types.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// TODO figure out why this is happening in the first place
|
||||
|
||||
db.users.find({},{habits:1, dailys:1, todos:1, rewards:1}).forEach(function(user){
|
||||
_.each(user.habits, function(task){
|
||||
task.type = 'habit';
|
||||
})
|
||||
_.each(user.dailys, function(task){
|
||||
task.type = 'daily';
|
||||
})
|
||||
_.each(user.todos, function(task){
|
||||
task.type = 'todo';
|
||||
})
|
||||
_.each(user.rewards, function(task){
|
||||
task.type = 'reward';
|
||||
})
|
||||
|
||||
db.users.update({_id:user._id}, {$set:{habits: user.habits, dailys: user.dailys, todos: user.todos, rewards: user.rewards}});
|
||||
});
|
||||
12
migrations/20131117_remove_undefined_pets.js
Normal file
12
migrations/20131117_remove_undefined_pets.js
Normal file
@@ -0,0 +1,12 @@
|
||||
// once and for all!
|
||||
|
||||
db.users.find({'items.pets':{$exists:1}},{'items.pets':1}).forEach(function(user){
|
||||
_.reduce(user.items.pets, function(m,v,k){
|
||||
if (!k.indexOf('undefined')) m.push(k);
|
||||
return m;
|
||||
}, []).forEach(function(key){
|
||||
delete user.items.pets[key];
|
||||
})
|
||||
|
||||
db.users.update({_id:user._id}, { $set:{'items.pets':user.items.pets} });
|
||||
});
|
||||
13
migrations/20131122_deleted_tags.js
Normal file
13
migrations/20131122_deleted_tags.js
Normal file
@@ -0,0 +1,13 @@
|
||||
// Cleanup broken tags
|
||||
// -------------------------
|
||||
db.users.find().forEach(function(user){
|
||||
var tasks = user.habits.concat(user.dailys).concat(user.todos).concat(user.rewards);
|
||||
|
||||
_.each(tasks, function(task){
|
||||
_.each(task.tags, function(value, key){ //value is true, key is tag.id
|
||||
if (!_.find(user.tags,{id:key})) delete task.tags[key];
|
||||
});
|
||||
});
|
||||
|
||||
db.users.update({_id:user._id}, user);
|
||||
});
|
||||
8
migrations/20131123_set_default_party_order.js
Normal file
8
migrations/20131123_set_default_party_order.js
Normal file
@@ -0,0 +1,8 @@
|
||||
//Add default to randomize party members list
|
||||
db.users.update(
|
||||
{},
|
||||
{$set:{
|
||||
'party.order': 'random',
|
||||
}},
|
||||
{multi:true}
|
||||
)
|
||||
5
migrations/20131126_clean_dayStart.js
Normal file
5
migrations/20131126_clean_dayStart.js
Normal file
@@ -0,0 +1,5 @@
|
||||
db.users.find({'preferences.dayStart':{$exists:1}},{'preferences.dayStart':1}).forEach(function(user){
|
||||
var dayStart = +user.preferences.dayStart;
|
||||
dayStart = (_.isNaN(dayStart) || dayStart < 0 || dayStart > 24) ? 0 : dayStart;
|
||||
db.users.update({_id:user._id}, {$set:{'preferences.dayStart':dayStart}});
|
||||
});
|
||||
1
migrations/20131126_turkey_pet.js
Normal file
1
migrations/20131126_turkey_pet.js
Normal file
@@ -0,0 +1 @@
|
||||
db.users.update({},{$set:{'items.pets.Turkey-Base':5, 'flags.newStuff':true}}, {multi:true});
|
||||
38
migrations/20131127_restore_dayStart.js
Normal file
38
migrations/20131127_restore_dayStart.js
Normal file
@@ -0,0 +1,38 @@
|
||||
// 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');
|
||||
|
||||
var backupUsers = mongo.db('localhost:27017/habitrpg_old?auto_reconnect').collection('users');
|
||||
var liveUsers = mongo.db('localhost:27017/habitrpg_new?auto_reconnect').collection('users');
|
||||
|
||||
var query = {'preferences.dayStart':{$exists:1,$ne:0}};
|
||||
var select = {'preferences.dayStart': 1};
|
||||
|
||||
backupUsers.count(query, function(err, count){
|
||||
if (err) return console.error(err);
|
||||
backupUsers.findEach(query, select, {batchSize:20}, function(err, before){
|
||||
if (err) return console.error(err);
|
||||
if (!before) { count--; return console.log('!before'); }
|
||||
liveUsers.findById(before._id, function(err, after){
|
||||
if (err) return console.error(err);
|
||||
if (!after) { count--; return console.log(before._id + ' deleted?'); }
|
||||
|
||||
var dayStart = +before.preferences.dayStart;
|
||||
if (after.preferences.dayStart == 0 && dayStart != 0){
|
||||
dayStart = (_.isNaN(dayStart) || dayStart < 0 || dayStart > 24) ? 0 : dayStart;
|
||||
} else {
|
||||
dayStart = after.preferences.dayStart;
|
||||
}
|
||||
|
||||
liveUsers.update({_id:after._id}, {$inc:{_v:1}, $set:{'preferences.dayStart':dayStart}});
|
||||
if (--count <= 0) console.log("DONE!");
|
||||
})
|
||||
});
|
||||
});
|
||||
20
migrations/20131217_unearned_backer_gear.js
Normal file
20
migrations/20131217_unearned_backer_gear.js
Normal file
@@ -0,0 +1,20 @@
|
||||
var query = {
|
||||
'$or': [
|
||||
{'items.gear.owned.weapon_special_0': true},
|
||||
{'items.gear.owned.armor_special_0': true},
|
||||
{'items.gear.owned.head_special_0': true},
|
||||
{'items.gear.owned.shield_special_0': true}
|
||||
]
|
||||
};
|
||||
|
||||
db.users.find(query, {'items.gear.owned':1,backer:1}).forEach(function(user){
|
||||
var owned = user.items.gear.owned;
|
||||
var tier = (user.backer && user.backer.tier) || 0;
|
||||
if (tier < 70) delete owned.weapon_special_0;
|
||||
if (tier < 45) delete owned.armor_special_0;
|
||||
if (tier < 45) delete owned.head_special_0;
|
||||
if (tier < 45) delete owned.shield_special_0;
|
||||
|
||||
|
||||
db.users.update({_id:user._id}, {$set:{'items.gear.owned':owned}});
|
||||
});
|
||||
51
migrations/20131221_restore_NaN_history.js
Normal file
51
migrations/20131221_restore_NaN_history.js
Normal file
@@ -0,0 +1,51 @@
|
||||
// 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
|
||||
*/
|
||||
var mongo = require('mongoskin');
|
||||
var _ = require('lodash');
|
||||
|
||||
var backupUsers = mongo.db('localhost:27017/habitrpg_old?auto_reconnect').collection('users');
|
||||
var liveUsers = mongo.db('localhost:27017/habitrpg?auto_reconnect').collection('users');
|
||||
|
||||
function filterNaNs(h) {
|
||||
return h && _.isNumber(+h.value) && !_.isNaN(+h.value);
|
||||
}
|
||||
|
||||
var fields = {history:1,habits:1,dailys:1,migration:1};
|
||||
var count = 0;
|
||||
liveUsers.findEach({migration: {$ne:'20131221_restore_NaN_history'}}, fields, {batchSize:500}, function(err, after){
|
||||
if (!after) err = '!after';
|
||||
if (err) {count++;return console.error(err);}
|
||||
|
||||
backupUsers.findById(after._id, fields, function(err, before){
|
||||
if (err) {count++;return console.error(err);}
|
||||
|
||||
_.each(['todos','exp'],function(type){
|
||||
if (!_.isEmpty(after.history[type]))
|
||||
after.history[type] = _.filter(after.history[type], filterNaNs);
|
||||
if (before && !_.isEmpty(before.history[type]))
|
||||
after.history[type] = before.history[type].concat(after.history[type]);
|
||||
})
|
||||
|
||||
_.each(['habits','dailys'], function(type){
|
||||
_.each(after[type], function(t){
|
||||
t.history = _.filter(t.history, filterNaNs);
|
||||
var found = before && _.find(before[type],{id:t.id});
|
||||
if (found && found.history) t.history = found.history.concat(t.history);
|
||||
})
|
||||
})
|
||||
|
||||
liveUsers.update({_id:after._id}, {$set:{history:after.history, dailys:after.dailys, habits:after.habits, migration:'20131221_restore_NaN_history'}, $inc:{_v:1}});
|
||||
//if (--count <= 0) console.log("DONE! " + after._id);
|
||||
if (++count%1000 == 0) console.log(count);
|
||||
if (after._id == '9') console.log('lefnire processed');
|
||||
})
|
||||
});
|
||||
38
migrations/20131225_restore_streaks.js
Normal file
38
migrations/20131225_restore_streaks.js
Normal file
@@ -0,0 +1,38 @@
|
||||
// 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
|
||||
*/
|
||||
var mongo = require('mongoskin');
|
||||
var _ = require('lodash');
|
||||
|
||||
var backupUsers = mongo.db('localhost:27017/habitrpg_old?auto_reconnect').collection('users');
|
||||
var liveUsers = mongo.db('lefnire:mAdn3s5s@charlotte.mongohq.com:10015/habitrpg_large?auto_reconnect').collection('users');
|
||||
|
||||
var fields = {dailys:1,migration:1};
|
||||
var count = 0;
|
||||
liveUsers.findEach({migration: {$ne:'20131225_restore_streaks'}}, fields, {batchSize:250}, function(err, after){
|
||||
if (!after) err = '!after';
|
||||
if (err) {count++;return console.error(err);}
|
||||
|
||||
backupUsers.findById(after._id, fields, function(err, before){
|
||||
if (!before) err = '!before';
|
||||
if (err) {count++;return console.error(err);}
|
||||
|
||||
_.each(before.dailys,function(d){
|
||||
var found = _.find(after.dailys,{id: d.id});
|
||||
if (found && !found.streak) found.streak = d.streak;
|
||||
})
|
||||
|
||||
liveUsers.update({_id:after._id}, {$set:{dailys:after.dailys, migration:'20131225_restore_streaks'}, $inc:{_v:1}});
|
||||
//if (--count <= 0) console.log("DONE! " + after._id);
|
||||
if (++count%1000 == 0) console.log(count);
|
||||
if (after._id == '9') console.log('lefnire processed');
|
||||
})
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user