From 0049647c083b85a0b9651f3380bdb43f95ab9aa5 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Wed, 14 Feb 2018 11:47:29 +0100 Subject: [PATCH 01/49] upgrade autoprefixer --- package-lock.json | 82 ++++++++++++++++++++++++++++++++++++++++------- package.json | 6 ++-- 2 files changed, 74 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ac9f5e46e..3b0864f03f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -782,16 +782,60 @@ "integrity": "sha512-hIp37ojJRRW8ExWSxxLpkDHUufk/DFfsb7/cUC1cVbBg7JV4gJTkCTRa44dlL9e5jx1P3VNrjL7QOQfi4MyltA==" }, "autoprefixer": { - "version": "6.7.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", - "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-8.0.0.tgz", + "integrity": "sha512-XBEqAoESCyGu3daYmWcTC37Dwmjvs0y40UtUO3MMX+Pd/w7jwNFfUKNtxoMFu0u0wcotP+arDpU3JVH54UV79Q==", "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000803", + "browserslist": "3.0.0", + "caniuse-lite": "1.0.30000808", "normalize-range": "0.1.2", "num2fraction": "1.2.2", - "postcss": "5.2.18", + "postcss": "6.0.17", "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "browserslist": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.0.0.tgz", + "integrity": "sha512-5ArwxNIJxmBTUUTe+F7P2AM8wNf6zFa9mb3/o0JCrhGdd042PuVgAZg4M+A29NUpqFEYl+H3kQQYoZUXfuRS9g==", + "requires": { + "caniuse-lite": "1.0.30000808", + "electron-to-chromium": "1.3.33" + } + }, + "electron-to-chromium": { + "version": "1.3.33", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.33.tgz", + "integrity": "sha1-vwBwPWKnxlI4E2V4w1LWxcBCpUU=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "postcss": { + "version": "6.0.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.17.tgz", + "integrity": "sha512-Bl1nybsSzWYbP8O4gAVD8JIjZIul9hLNOPTGBIlVmZNUnNAGL+W0cpYWzVwfImZOwumct4c1SDvSbncVWKtXUw==", + "requires": { + "chalk": "2.3.0", + "source-map": "0.6.1", + "supports-color": "5.2.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", + "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", + "requires": { + "has-flag": "3.0.0" + } + } } }, "aws-sdk": { @@ -2364,6 +2408,11 @@ "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000803.tgz", "integrity": "sha1-Po0rr1bC/VpZyC4ieSig3CwmcC0=" }, + "caniuse-lite": { + "version": "1.0.30000808", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000808.tgz", + "integrity": "sha512-vT0JLmHdvq1UVbYXioxCXHYdNw55tyvi+IUWyX0Zeh1OFQi2IllYtm38IJnSgHWCv/zUnX1hdhy3vMJvuTNSqw==" + }, "capture-stack-trace": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", @@ -2470,7 +2519,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", - "dev": true, "requires": { "ansi-styles": "3.2.0", "escape-string-regexp": "1.0.5", @@ -2481,7 +2529,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, "requires": { "color-convert": "1.9.1" } @@ -2489,14 +2536,12 @@ "has-flag": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" }, "supports-color": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, "requires": { "has-flag": "2.0.0" } @@ -3561,6 +3606,21 @@ "postcss-unique-selectors": "2.0.2", "postcss-value-parser": "3.3.0", "postcss-zindex": "2.2.0" + }, + "dependencies": { + "autoprefixer": { + "version": "6.7.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "requires": { + "browserslist": "1.7.7", + "caniuse-db": "1.0.30000803", + "normalize-range": "0.1.2", + "num2fraction": "1.2.2", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + } + } } }, "csso": { diff --git a/package.json b/package.json index 972f95f6fe..dbe6fc7ef8 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "amplitude": "^3.5.0", "apidoc": "^0.17.5", "apn": "^1.7.6", - "autoprefixer": "^6.4.0", + "autoprefixer": "^8.0.0", "aws-sdk": "^2.0.25", "axios": "^0.16.0", "axios-progress-bar": "^0.1.7", @@ -36,8 +36,8 @@ "coupon-code": "^0.4.5", "cross-env": "^5.1.3", "css-loader": "^0.28.0", - "cwait": "^1.1.1", "csv-stringify": "^2.0.1", + "cwait": "^1.1.1", "domain-middleware": "~0.1.0", "express": "^4.16.2", "express-basic-auth": "^1.0.1", @@ -85,8 +85,8 @@ "request": "^2.83.0", "rimraf": "^2.4.3", "sass-loader": "^6.0.2", - "stripe": "^5.4.0", "shelljs": "^0.8.1", + "stripe": "^5.4.0", "superagent": "^3.4.3", "svg-inline-loader": "^0.7.1", "svg-url-loader": "^2.0.2", From 7c5d35cf77fdbdef83619036797f97e6db971c64 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Wed, 14 Feb 2018 11:48:05 +0100 Subject: [PATCH 02/49] upgrade url-loader --- package-lock.json | 19 ++++++++++--------- package.json | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3b0864f03f..819586284e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12315,6 +12315,11 @@ "brorand": "1.1.0" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, "mime-db": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", @@ -19343,12 +19348,13 @@ "integrity": "sha1-HbSK1CLTQCRpqH99l73r/k+x48g=" }, "url-loader": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.5.9.tgz", - "integrity": "sha512-B7QYFyvv+fOBqBVeefsxv6koWWtjmHaMFT6KZWti4KRw8YUD/hOU+3AECvXuzyVawIBx3z7zQRejXCDSO5kk1Q==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.6.2.tgz", + "integrity": "sha512-h3qf9TNn53BpuXTTcpC+UehiRrl0Cv45Yr/xWayApjw6G8Bg2dGke7rIwDQ39piciWCWrC+WiqLjOh3SUp9n0Q==", "requires": { "loader-utils": "1.1.0", - "mime": "1.3.6" + "mime": "1.6.0", + "schema-utils": "0.3.0" }, "dependencies": { "loader-utils": { @@ -19360,11 +19366,6 @@ "emojis-list": "2.1.0", "json5": "0.5.1" } - }, - "mime": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", - "integrity": "sha1-WR2E02U6awtKO5343lqoEI5y5eA=" } } }, diff --git a/package.json b/package.json index dbe6fc7ef8..ba292e3c2d 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "svg-url-loader": "^2.0.2", "svgo-loader": "^1.2.1", "universal-analytics": "^0.4.16", - "url-loader": "^0.5.7", + "url-loader": "^0.6.2", "useragent": "^2.1.9", "uuid": "^3.0.1", "validator": "^4.9.0", From bd90ffa6f86cfa4a30cee5a151ed1dc62bb5739b Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Wed, 14 Feb 2018 11:49:41 +0100 Subject: [PATCH 03/49] upgrade svg-inline-loader --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 819586284e..2c5b9810ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18178,9 +18178,9 @@ } }, "svg-inline-loader": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/svg-inline-loader/-/svg-inline-loader-0.7.1.tgz", - "integrity": "sha1-bQ4nKLfsNBTCGAs/eAvD9xVO8iY=", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/svg-inline-loader/-/svg-inline-loader-0.8.0.tgz", + "integrity": "sha512-rynplY2eXFrdNomL1FvyTFQlP+dx0WqbzHglmNtA9M4IHRC3no2aPAl3ny9lUpJzFzFMZfWRK5YIclNU+FRePA==", "requires": { "loader-utils": "0.2.17", "object-assign": "4.1.1", diff --git a/package.json b/package.json index ba292e3c2d..9613a71ac2 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "shelljs": "^0.8.1", "stripe": "^5.4.0", "superagent": "^3.4.3", - "svg-inline-loader": "^0.7.1", + "svg-inline-loader": "^0.8.0", "svg-url-loader": "^2.0.2", "svgo-loader": "^1.2.1", "universal-analytics": "^0.4.16", From 269dac1fe86c91d6e8345b2c4a329f7195fd1354 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Wed, 14 Feb 2018 11:51:01 +0100 Subject: [PATCH 04/49] upgrade postcss-easy-import --- package-lock.json | 101 ++++++++++++++++++++++++++++++++-------------- package.json | 2 +- 2 files changed, 72 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2c5b9810ce..84fd541d2e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -365,11 +365,6 @@ "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=" }, - "any-promise": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-0.1.0.tgz", - "integrity": "sha1-gwtoCqflbzNFHUsEnzvYBESY7ic=" - }, "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", @@ -14900,26 +14895,51 @@ } }, "postcss-easy-import": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-easy-import/-/postcss-easy-import-2.1.0.tgz", - "integrity": "sha512-LfHi1rFGZ//1idOauAzhK45vjruQJGuofmR0fjUx+2gToT9L1kYlOQN0Ke6rwvuEE/IY/CHK5XgbGfRhbx0Hbw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-easy-import/-/postcss-easy-import-3.0.0.tgz", + "integrity": "sha512-cfNsear/v8xlkl9v5Wm8y4Do/puiDQTFF+WX2Fo++h7oKt1fKWVVW/5Ca8hslYDQWnjndrg813cA23Pt1jsYdg==", "requires": { "globby": "6.1.0", - "is-glob": "3.1.0", + "is-glob": "4.0.0", "lodash": "4.17.5", "object-assign": "4.1.1", - "pify": "2.3.0", - "postcss": "5.2.18", - "postcss-import": "9.1.0", + "pify": "3.0.0", + "postcss": "6.0.17", + "postcss-import": "10.0.0", "resolve": "1.5.0" }, "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "postcss": { + "version": "6.0.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.17.tgz", + "integrity": "sha512-Bl1nybsSzWYbP8O4gAVD8JIjZIul9hLNOPTGBIlVmZNUnNAGL+W0cpYWzVwfImZOwumct4c1SDvSbncVWKtXUw==", "requires": { - "is-extglob": "2.1.1" + "chalk": "2.3.0", + "source-map": "0.6.1", + "supports-color": "5.2.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", + "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", + "requires": { + "has-flag": "3.0.0" } } } @@ -14934,16 +14954,45 @@ } }, "postcss-import": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-9.1.0.tgz", - "integrity": "sha1-lf6YdqHnmvSfvcNYnwH+WqfMHoA=", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-10.0.0.tgz", + "integrity": "sha1-TIXJewmRNsxeoCQNwd/b/eTi674=", "requires": { "object-assign": "4.1.1", - "postcss": "5.2.18", + "postcss": "6.0.17", "postcss-value-parser": "3.3.0", - "promise-each": "2.2.0", "read-cache": "1.0.0", "resolve": "1.5.0" + }, + "dependencies": { + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "postcss": { + "version": "6.0.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.17.tgz", + "integrity": "sha512-Bl1nybsSzWYbP8O4gAVD8JIjZIul9hLNOPTGBIlVmZNUnNAGL+W0cpYWzVwfImZOwumct4c1SDvSbncVWKtXUw==", + "requires": { + "chalk": "2.3.0", + "source-map": "0.6.1", + "supports-color": "5.2.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", + "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", + "requires": { + "has-flag": "3.0.0" + } + } } }, "postcss-load-config": { @@ -15487,14 +15536,6 @@ "asap": "2.0.6" } }, - "promise-each": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/promise-each/-/promise-each-2.2.0.tgz", - "integrity": "sha1-M1MXTv8mlEgQN+BOAfd6oPttG2A=", - "requires": { - "any-promise": "0.1.0" - } - }, "property-is-enumerable-x": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/property-is-enumerable-x/-/property-is-enumerable-x-1.1.0.tgz", diff --git a/package.json b/package.json index 9613a71ac2..7b4399ecc6 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "paypal-ipn": "3.0.0", "paypal-rest-sdk": "^1.8.1", "popper.js": "^1.13.0", - "postcss-easy-import": "^2.0.0", + "postcss-easy-import": "^3.0.0", "ps-tree": "^1.0.0", "pug": "^2.0.0-rc.4", "push-notify": "git://github.com/habitrpg/push-notify.git#6bc2b5fdb1bdc9649b9ec1964d79ca50187fc8a9", From d544a5be6f2b7a267a87d122bc27903738fdb4b9 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Wed, 14 Feb 2018 11:54:36 +0100 Subject: [PATCH 05/49] upgrade svgo-loader --- package-lock.json | 127 +++++++++++++++++++++++++++++++++++----------- package.json | 3 +- 2 files changed, 98 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index 84fd541d2e..c692f7d66b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2895,9 +2895,9 @@ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, "coa": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", - "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.1.tgz", + "integrity": "sha512-5wfTTO8E2/ja4jFSxePXlG5nRu5bBtL/r1HCIpJW/lzT6yDtKl0u0Z4o/Vpz32IpKmBn7HerheEZQgA9N2DarQ==", "requires": { "q": "1.5.1" } @@ -3502,7 +3502,6 @@ "version": "1.3.0-rc0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.3.0-rc0.tgz", "integrity": "sha1-b5MZaqrnN2ZuoQNqjLFKj8t6kjE=", - "optional": true, "requires": { "boolbase": "1.0.0", "css-what": "2.1.0", @@ -3513,8 +3512,7 @@ "css-select-base-adapter": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.0.tgz", - "integrity": "sha1-AQKz0UYw34bD65+p9UVicBBs+ZA=", - "optional": true + "integrity": "sha1-AQKz0UYw34bD65+p9UVicBBs+ZA=" }, "css-selector-tokenizer": { "version": "0.7.0", @@ -3542,7 +3540,6 @@ "version": "1.0.0-alpha25", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha25.tgz", "integrity": "sha512-XC6xLW/JqIGirnZuUWHXCHRaAjje2b3OIB0Vj5RIJo6mIi/AdJo30quQl5LxUl0gkXDIrTrFGbMlcZjyFplz1A==", - "optional": true, "requires": { "mdn-data": "1.1.0", "source-map": "0.5.7" @@ -3551,8 +3548,7 @@ "css-url-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/css-url-regex/-/css-url-regex-1.1.0.tgz", - "integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w=", - "optional": true + "integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w=" }, "css-what": { "version": "2.1.0", @@ -3619,12 +3615,22 @@ } }, "csso": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", - "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-3.5.0.tgz", + "integrity": "sha512-WtJjFP3ZsSdWhiZr4/k1B9uHPgYjFYnDxfbaJxk1hz5PDLIJ5BCRWkJqaztZ0DbP8d2ZIVwUPIJb2YmCwkPaMw==", "requires": { - "clap": "1.2.3", - "source-map": "0.5.7" + "css-tree": "1.0.0-alpha.27" + }, + "dependencies": { + "css-tree": { + "version": "1.0.0-alpha.27", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.27.tgz", + "integrity": "sha512-BAYp9FyN4jLXjfvRpTDchBllDptqlK9I7OsagXCG9Am5C+5jc8eRZHgqb9x500W2OKS14MMlpQc/nmh/aA7TEQ==", + "requires": { + "mdn-data": "1.1.0", + "source-map": "0.5.7" + } + } } }, "csv-stringify": { @@ -13759,7 +13765,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "optional": true, "requires": { "define-properties": "1.1.2", "es-abstract": "1.10.0" @@ -13814,7 +13819,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.0.4.tgz", "integrity": "sha1-5STaCbT2b/Bd9FdUbscqyZ8TBpo=", - "optional": true, "requires": { "define-properties": "1.1.2", "es-abstract": "1.10.0", @@ -15441,6 +15445,49 @@ "postcss": "5.2.18", "postcss-value-parser": "3.3.0", "svgo": "0.7.2" + }, + "dependencies": { + "coa": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", + "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", + "requires": { + "q": "1.5.1" + } + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=" + }, + "csso": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", + "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", + "requires": { + "clap": "1.2.3", + "source-map": "0.5.7" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "svgo": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", + "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", + "requires": { + "coa": "1.0.4", + "colors": "1.1.2", + "csso": "2.3.2", + "js-yaml": "3.7.0", + "mkdirp": "0.5.1", + "sax": "1.2.4", + "whet.extend": "0.9.9" + } + } } }, "postcss-unique-selectors": { @@ -17675,8 +17722,7 @@ "stable": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.6.tgz", - "integrity": "sha1-kQ9dKu17Ugxud3SZwfMuE5/eyxA=", - "optional": true + "integrity": "sha1-kQ9dKu17Ugxud3SZwfMuE5/eyxA=" }, "stack-trace": { "version": "0.0.10", @@ -18250,17 +18296,24 @@ } }, "svgo": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", - "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.0.4.tgz", + "integrity": "sha512-DR9ieoaBoeySY4pnIJMM3Hez6kcVj+Uzj8AUJkr3435qstozP9i+mZ9yzcbq64A44i9YKh3I1Laex+mAyc69Rg==", "requires": { - "coa": "1.0.4", + "coa": "2.0.1", "colors": "1.1.2", - "csso": "2.3.2", - "js-yaml": "3.7.0", + "css-select": "1.3.0-rc0", + "css-select-base-adapter": "0.1.0", + "css-tree": "1.0.0-alpha25", + "css-url-regex": "1.1.0", + "csso": "3.5.0", + "js-yaml": "3.10.0", "mkdirp": "0.5.1", + "object.values": "1.0.4", "sax": "1.2.4", - "whet.extend": "0.9.9" + "stable": "0.1.6", + "unquote": "1.1.1", + "util.promisify": "1.0.0" }, "dependencies": { "colors": { @@ -18268,6 +18321,20 @@ "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=" }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==" + }, + "js-yaml": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", + "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", + "requires": { + "argparse": "1.0.9", + "esprima": "4.0.0" + } + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -18276,9 +18343,9 @@ } }, "svgo-loader": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/svgo-loader/-/svgo-loader-1.2.1.tgz", - "integrity": "sha1-4lXN6/VnU/+DvSjR16IHYsDfUTA=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/svgo-loader/-/svgo-loader-2.1.0.tgz", + "integrity": "sha512-G9KGgXaSn+F05HtIViNmy3hT2TZsnqtq10QnmYlaoc+ITd5SGQckaH7v066Noq9cOjMqA6s2AXHDiNAUItfHuw==", "requires": { "loader-utils": "1.1.0" }, @@ -19248,8 +19315,7 @@ "unquote": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", - "optional": true + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" }, "unset-value": { "version": "1.0.0", @@ -19559,7 +19625,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "optional": true, "requires": { "define-properties": "1.1.2", "object.getownpropertydescriptors": "2.0.3" diff --git a/package.json b/package.json index 7b4399ecc6..753bb3dc25 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,8 @@ "superagent": "^3.4.3", "svg-inline-loader": "^0.8.0", "svg-url-loader": "^2.0.2", - "svgo-loader": "^1.2.1", + "svgo": "^1.0.4", + "svgo-loader": "^2.1.0", "universal-analytics": "^0.4.16", "url-loader": "^0.6.2", "useragent": "^2.1.9", From 4a7eb768dc95066f599841a516932a7808d8dbfa Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Wed, 14 Feb 2018 11:57:47 +0100 Subject: [PATCH 06/49] ugrade vue-loader --- package-lock.json | 61 ++++++++++++++++------------------------------- package.json | 2 +- 2 files changed, 22 insertions(+), 41 deletions(-) diff --git a/package-lock.json b/package-lock.json index c692f7d66b..c3551816ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19877,9 +19877,9 @@ "integrity": "sha512-e+ThJMYmZg4D9UnrLcr6LQxGu6YlcxkrmZGPCyIN4malcNhdeGGKxmFuM5y6ICMJJxQywLfT8MM1rYZr4LpeLw==" }, "vue-loader": { - "version": "13.7.1", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-13.7.1.tgz", - "integrity": "sha512-v6PbKMGl/hWHGPxB2uGHsA66vusrXF66J/h1QiFXtU6z5zVSK8jq5xl95M1p3QNXmuEJKNP3nxoXfbgQNs7hJg==", + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-14.1.1.tgz", + "integrity": "sha512-ZLu0yWohbWPayFhSPy80xnObCrPDLGrf/v9R5kJyUbVlcI46srnQlaG+HnTN5HAt5nV9iJiF4oJIjX0+jK+f0w==", "requires": { "consolidate": "0.14.5", "hash-sum": "1.0.2", @@ -19892,42 +19892,14 @@ "resolve": "1.5.0", "source-map": "0.6.1", "vue-hot-reload-api": "2.2.4", - "vue-style-loader": "3.1.2", + "vue-style-loader": "4.0.2", "vue-template-es2015-compiler": "1.6.0" }, "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "requires": { - "color-convert": "1.9.1" - } - }, - "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" - }, - "dependencies": { - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "requires": { - "has-flag": "2.0.0" - } - } - } - }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "loader-utils": { "version": "1.1.0", @@ -19946,7 +19918,7 @@ "requires": { "chalk": "2.3.0", "source-map": "0.6.1", - "supports-color": "5.1.0" + "supports-color": "5.2.0" } }, "source-map": { @@ -19955,11 +19927,20 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "supports-color": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz", - "integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", + "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "requires": { - "has-flag": "2.0.0" + "has-flag": "3.0.0" + } + }, + "vue-style-loader": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.0.2.tgz", + "integrity": "sha512-Bwf1Gf331Z5OTzMRAYQYiFpFbaCpaXQjQcSvWYsmEwSgOIVa+moXWoD8fQCNetcekbP3OSE5pyvomNKbvIUQtQ==", + "requires": { + "hash-sum": "1.0.2", + "loader-utils": "1.1.0" } } } diff --git a/package.json b/package.json index 753bb3dc25..cbd312d024 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "validator": "^4.9.0", "vinyl-buffer": "^1.0.1", "vue": "^2.5.2", - "vue-loader": "^13.3.0", + "vue-loader": "^14.1.1", "vue-mugen-scroll": "^0.2.1", "vue-router": "^3.0.0", "vue-style-loader": "^3.0.0", From adcd9a53c4ac7139dc33ef961593186881ff1db2 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Wed, 14 Feb 2018 11:58:12 +0100 Subject: [PATCH 07/49] upgrade babel-loader --- package-lock.json | 62 ++++++++++++++++++++++++++++++++++++----------- package.json | 2 +- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index c3551816ca..dd0b5ef53c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1087,14 +1087,51 @@ } }, "babel-loader": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-6.4.1.tgz", - "integrity": "sha1-CzQRLVsHSKjc2/Uaz2+b1C1QuMo=", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.2.tgz", + "integrity": "sha512-jRwlFbINAeyDStqK6Dd5YuY0k5YuzQUvlz2ZamuXrXmxav3pNqe9vfJ402+2G+OmlJSXxCOpB6Uz0INM7RQe2A==", "requires": { - "find-cache-dir": "0.1.1", - "loader-utils": "0.2.17", - "mkdirp": "0.5.1", - "object-assign": "4.1.1" + "find-cache-dir": "1.0.0", + "loader-utils": "1.1.0", + "mkdirp": "0.5.1" + }, + "dependencies": { + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "requires": { + "commondir": "1.0.1", + "make-dir": "1.1.0", + "pkg-dir": "2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "2.0.0" + } + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1" + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "requires": { + "find-up": "2.1.0" + } + } } }, "babel-messages": { @@ -6340,6 +6377,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", + "dev": true, "requires": { "commondir": "1.0.1", "mkdirp": "0.5.1", @@ -11389,7 +11427,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, "requires": { "p-locate": "2.0.0", "path-exists": "3.0.0" @@ -11398,8 +11435,7 @@ "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" } } }, @@ -14096,7 +14132,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", - "dev": true, "requires": { "p-try": "1.0.0" } @@ -14105,7 +14140,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, "requires": { "p-limit": "1.2.0" } @@ -14123,8 +14157,7 @@ "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" }, "pac-proxy-agent": { "version": "1.1.0", @@ -14715,6 +14748,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "dev": true, "requires": { "find-up": "1.1.2" } diff --git a/package.json b/package.json index cbd312d024..f6d0c1c7e8 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "axios-progress-bar": "^0.1.7", "babel-core": "^6.0.0", "babel-eslint": "^7.2.3", - "babel-loader": "^6.0.0", + "babel-loader": "^7.1.2", "babel-plugin-syntax-async-functions": "^6.13.0", "babel-plugin-syntax-dynamic-import": "^6.18.0", "babel-plugin-transform-async-to-module-method": "^6.8.0", From 2f3e6b82cf1f025e59cfde9baee60280747e19c3 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Wed, 14 Feb 2018 12:05:53 +0100 Subject: [PATCH 08/49] fix svgo options --- webpack/webpack.base.conf.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/webpack/webpack.base.conf.js b/webpack/webpack.base.conf.js index 773d3bbd80..5da0df5a57 100644 --- a/webpack/webpack.base.conf.js +++ b/webpack/webpack.base.conf.js @@ -98,7 +98,14 @@ const baseConfig = { test: /\.svg$/, use: [ { loader: 'svg-inline-loader' }, - { loader: 'svgo-loader' }, + { + loader: 'svgo-loader', + options: { + plugins: [ + {removeViewBox: false}, + ], + }, + }, ], exclude: [path.resolve(projectRoot, 'website/client/assets/svg/for-css')], }, @@ -112,7 +119,14 @@ const baseConfig = { name: utils.assetsPath('svg/[name].[hash:7].[ext]'), }, }, - { loader: 'svgo-loader' }, + { + loader: 'svgo-loader', + options: { + plugins: [ + {removeViewBox: false}, + ], + }, + }, ], include: [path.resolve(projectRoot, 'website/client/assets/svg/for-css')], }, From 8611bd97ae05f899a74fe085de14c30d47119efa Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Wed, 14 Feb 2018 12:06:30 +0100 Subject: [PATCH 09/49] upgrade webpack-dev-middleware --- package-lock.json | 63 ++++++++++++++++++++++++++++++++++++++--------- package.json | 2 +- 2 files changed, 52 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index dd0b5ef53c..7dce12f3d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10952,6 +10952,25 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", "dev": true + }, + "time-stamp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-2.0.0.tgz", + "integrity": "sha1-lcakRTDhW6jW9KPsuMOj+sRto1c=", + "dev": true + }, + "webpack-dev-middleware": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz", + "integrity": "sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==", + "dev": true, + "requires": { + "memory-fs": "0.4.1", + "mime": "1.6.0", + "path-is-absolute": "1.0.1", + "range-parser": "1.2.0", + "time-stamp": "2.0.0" + } } } }, @@ -11967,6 +11986,12 @@ } } }, + "loglevelnext": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.3.tgz", + "integrity": "sha512-OCxd/b78TijTB4b6zVqLbMrxhebyvdZKwqpL0VHUZ0pYhavXaPD4l6Xrr4n5xqTYWiqtb0i7ikSoJY/myQ/Org==", + "dev": true + }, "lolex": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz", @@ -20491,28 +20516,30 @@ } }, "webpack-dev-middleware": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz", - "integrity": "sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.5.tgz", + "integrity": "sha512-EPXudTrpQLksLt9klR0spnb7mt4dHtk3amGnohZNeQ+Y2QSqBbnWA7uNZ9+rqyfhEcYw18pUwcGIXuPFvIIELQ==", "dev": true, "requires": { + "loud-rejection": "1.6.0", "memory-fs": "0.4.1", - "mime": "1.6.0", + "mime": "2.2.0", "path-is-absolute": "1.0.1", "range-parser": "1.2.0", - "time-stamp": "2.0.0" + "url-join": "2.0.5", + "webpack-log": "1.1.2" }, "dependencies": { "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.2.0.tgz", + "integrity": "sha512-0Qz9uF1ATtl8RKJG4VRfOymh7PyEor6NbrI/61lRfuRe4vx9SNATrvAeTj2EWVRKjEQGskrzWkJBBY5NbaVHIA==", "dev": true }, - "time-stamp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-2.0.0.tgz", - "integrity": "sha1-lcakRTDhW6jW9KPsuMOj+sRto1c=", + "url-join": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", + "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", "dev": true } } @@ -20529,6 +20556,18 @@ "strip-ansi": "3.0.1" } }, + "webpack-log": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.1.2.tgz", + "integrity": "sha512-B53SD4N4BHpZdUwZcj4st2QT7gVfqZtqHDruC1N+K2sciq0Rt/3F1Dx6RlylVkcrToMLTaiaeT48k9Lq4iDVDA==", + "dev": true, + "requires": { + "chalk": "2.3.0", + "log-symbols": "2.2.0", + "loglevelnext": "1.0.3", + "uuid": "3.2.1" + } + }, "webpack-merge": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.1.tgz", diff --git a/package.json b/package.json index f6d0c1c7e8..ce65c69f0e 100644 --- a/package.json +++ b/package.json @@ -183,7 +183,7 @@ "sinon-chai": "^2.8.0", "sinon-stub-promise": "^4.0.0", "webpack-bundle-analyzer": "^2.2.1", - "webpack-dev-middleware": "^1.10.0", + "webpack-dev-middleware": "^2.0.5", "webpack-hot-middleware": "^2.6.1" } } From 16a227109ea76b61a6cee466b57f55d9bacd0bb6 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Wed, 14 Feb 2018 12:14:11 +0100 Subject: [PATCH 10/49] upgrade to webpack 3 and extract-text-webpack-plugin --- package-lock.json | 216 ++++++++++++++++++++++++++++++++++++---------- package.json | 4 +- 2 files changed, 172 insertions(+), 48 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7dce12f3d2..8b5b403b75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -167,7 +167,8 @@ "ajv-keywords": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", - "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=" + "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", + "dev": true }, "align-text": { "version": "0.1.4", @@ -5218,7 +5219,6 @@ "version": "0.1.5", "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", - "dev": true, "requires": { "d": "1.0.0", "es5-ext": "0.10.38", @@ -5237,7 +5237,6 @@ "version": "0.1.5", "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "dev": true, "requires": { "d": "1.0.0", "es5-ext": "0.10.38", @@ -5312,7 +5311,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", - "dev": true, "requires": { "es6-map": "0.1.5", "es6-weak-map": "2.0.2", @@ -5323,8 +5321,7 @@ "estraverse": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" } } }, @@ -5641,7 +5638,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", - "dev": true, "requires": { "estraverse": "4.2.0", "object-assign": "4.1.1" @@ -5650,8 +5646,7 @@ "estraverse": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" } } }, @@ -5674,7 +5669,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dev": true, "requires": { "d": "1.0.0", "es5-ext": "0.10.38" @@ -6115,9 +6109,9 @@ } }, "extract-text-webpack-plugin": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-2.1.2.tgz", - "integrity": "sha1-dW7076gVXDaBgz+8NNpTuUF0bWw=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz", + "integrity": "sha512-bt/LZ4m5Rqt/Crl2HiKuAl/oqg0psx1tsTLkvWbJen1CtD+fftkZhMaQ9HOtY2gWsl2Wq+sABmMVi9z3DhKWQQ==", "requires": { "async": "2.6.0", "loader-utils": "1.1.0", @@ -19228,6 +19222,16 @@ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "optional": true }, + "uglifyjs-webpack-plugin": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", + "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", + "requires": { + "source-map": "0.5.7", + "uglify-js": "2.8.29", + "webpack-sources": "1.1.0" + } + }, "uid-number": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", @@ -20215,42 +20219,54 @@ } }, "webpack": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-2.7.0.tgz", - "integrity": "sha512-MjAA0ZqO1ba7ZQJRnoCdbM56mmFpipOPUv/vQpwwfSI42p5PVDdoiuK2AL2FwFUVgT859Jr43bFZXRg/LNsqvg==", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.11.0.tgz", + "integrity": "sha512-3kOFejWqj5ISpJk4Qj/V7w98h9Vl52wak3CLiw/cDOfbVTq7FeoZ0SdoHHY9PYlHr50ZS42OfvzE2vB4nncKQg==", "requires": { "acorn": "5.4.1", "acorn-dynamic-import": "2.0.2", - "ajv": "4.11.8", - "ajv-keywords": "1.5.1", + "ajv": "6.1.1", + "ajv-keywords": "3.1.0", "async": "2.6.0", "enhanced-resolve": "3.4.1", + "escope": "3.6.0", "interpret": "1.1.0", "json-loader": "0.5.7", "json5": "0.5.1", "loader-runner": "2.3.0", - "loader-utils": "0.2.17", + "loader-utils": "1.1.0", "memory-fs": "0.4.1", "mkdirp": "0.5.1", "node-libs-browser": "2.1.0", "source-map": "0.5.7", - "supports-color": "3.2.3", + "supports-color": "4.5.0", "tapable": "0.2.8", - "uglify-js": "2.8.29", + "uglifyjs-webpack-plugin": "0.4.6", "watchpack": "1.4.0", "webpack-sources": "1.1.0", - "yargs": "6.6.0" + "yargs": "8.0.2" }, "dependencies": { "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.1.1.tgz", + "integrity": "sha1-l41Zf7wrfQ5aXD3esUmmgvKr+g4=", "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" } }, + "ajv-keywords": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.1.0.tgz", + "integrity": "sha1-rCsnk5xUPpXSwG5/f1wnvkqlQ74=" + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, "async": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", @@ -20259,40 +20275,148 @@ "lodash": "4.17.5" } }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "requires": { - "has-flag": "1.0.0" + "locate-path": "2.0.0" } }, - "yargs": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", - "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "requires": { - "camelcase": "3.0.0", + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "strip-bom": "3.0.0" + } + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1" + } + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "requires": { + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "requires": { + "pify": "2.3.0" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "requires": { + "load-json-file": "2.0.0", + "normalize-package-data": "2.4.0", + "path-type": "2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "requires": { + "find-up": "2.1.0", + "read-pkg": "2.0.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "requires": { + "has-flag": "2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "yargs": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", + "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", + "requires": { + "camelcase": "4.1.0", "cliui": "3.2.0", "decamelize": "1.2.0", "get-caller-file": "1.0.2", - "os-locale": "1.4.0", - "read-pkg-up": "1.0.1", + "os-locale": "2.1.0", + "read-pkg-up": "2.0.0", "require-directory": "2.1.1", "require-main-filename": "1.0.1", "set-blocking": "2.0.0", - "string-width": "1.0.2", - "which-module": "1.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", "y18n": "3.2.1", - "yargs-parser": "4.2.1" + "yargs-parser": "7.0.0" } }, "yargs-parser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", - "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", "requires": { - "camelcase": "3.0.0" + "camelcase": "4.1.0" } } } diff --git a/package.json b/package.json index ce65c69f0e..b255291f46 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "express": "^4.16.2", "express-basic-auth": "^1.0.1", "express-validator": "^2.18.0", - "extract-text-webpack-plugin": "^2.0.0-rc.3", + "extract-text-webpack-plugin": "^3.0.2", "glob": "^7.1.2", "got": "^6.1.1", "gulp": "^4.0.0", @@ -106,7 +106,7 @@ "vue-template-compiler": "^2.5.2", "vuedraggable": "^2.15.0", "vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#5d237615463a84a23dd6f3f77c6ab577d68593ec", - "webpack": "^2.2.1", + "webpack": "^3.11.0", "webpack-merge": "^4.0.0", "winston": "^2.1.0", "winston-loggly-bulk": "^2.0.2", From ac6a5ae8d241ad6c1f095a86f9f23f0d766bc1ff Mon Sep 17 00:00:00 2001 From: Keith Holliday Date: Thu, 15 Feb 2018 21:25:44 -0700 Subject: [PATCH 11/49] Registered new funtion as translation (#10005) --- website/common/script/content/gear/weapon.js | 1 + 1 file changed, 1 insertion(+) diff --git a/website/common/script/content/gear/weapon.js b/website/common/script/content/gear/weapon.js index d893e001ce..62578e6310 100644 --- a/website/common/script/content/gear/weapon.js +++ b/website/common/script/content/gear/weapon.js @@ -60,6 +60,7 @@ for (let key in weapon) { return `${oldnotes(lang)} ${twoHandedText}`; }; + item.notes.i18nLangFunc = true; // See https://github.com/HabitRPG/habitica/blob/develop/website/common/script/content/translation.js#L8 } } From 95179be3466eec83e727f577bd8b28cae3a3dd56 Mon Sep 17 00:00:00 2001 From: SabreCat Date: Fri, 16 Feb 2018 21:18:51 +0000 Subject: [PATCH 12/49] feat(world-boss): Rage Strike support --- .../seasonal_shop_broken_background.png | Bin 0 -> 7458 bytes .../npc/broken/seasonal_shop_broken_layer.png | Bin 0 -> 8184 bytes .../npc/broken/seasonal_shop_broken_npc.png | Bin 0 -> 8829 bytes .../world-boss/rage_strike-market@2x.png | Bin 0 -> 16831 bytes .../world-boss/rage_strike-quests@2x.png | Bin 0 -> 17443 bytes .../rage_strike-seasonalShop@2x.png | Bin 0 -> 18931 bytes .../rage_strike@2x.png} | Bin website/client/components/groups/tavern.vue | 20 +++++++---- .../components/shops/seasonal/index.vue | 31 ++++++++++++++++-- website/client/store/actions/index.js | 2 ++ website/client/store/actions/world-state.js | 7 ++++ website/common/locales/en/limited.json | 1 + website/common/locales/en/questsContent.json | 2 +- 13 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 website/client/assets/images/npc/broken/seasonal_shop_broken_background.png create mode 100644 website/client/assets/images/npc/broken/seasonal_shop_broken_layer.png create mode 100644 website/client/assets/images/npc/broken/seasonal_shop_broken_npc.png create mode 100644 website/client/assets/images/world-boss/rage_strike-market@2x.png create mode 100644 website/client/assets/images/world-boss/rage_strike-quests@2x.png create mode 100644 website/client/assets/images/world-boss/rage_strike-seasonalShop@2x.png rename website/client/assets/images/{rage_strike2x.png => world-boss/rage_strike@2x.png} (100%) create mode 100644 website/client/store/actions/world-state.js diff --git a/website/client/assets/images/npc/broken/seasonal_shop_broken_background.png b/website/client/assets/images/npc/broken/seasonal_shop_broken_background.png new file mode 100644 index 0000000000000000000000000000000000000000..0c4a79919c396969cf895f53c5bf59c5cb617c45 GIT binary patch literal 7458 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 auf8MqjNr{<|N0Cwmfa$NvM4B3JSN literal 0 HcmV?d00001 diff --git a/website/client/assets/images/npc/broken/seasonal_shop_broken_npc.png b/website/client/assets/images/npc/broken/seasonal_shop_broken_npc.png new file mode 100644 index 0000000000000000000000000000000000000000..897faf315421a67f8e34063104cccb23b443c10e GIT binary patch literal 8829 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 HdjJ0bMZgm~ literal 0 HcmV?d00001 diff --git a/website/client/assets/images/world-boss/rage_strike-market@2x.png b/website/client/assets/images/world-boss/rage_strike-market@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3f98a6ed9c0dcf8c7f0bc77c84a12cb095d8a8f1 GIT binary patch literal 16831 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

-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

RASa4UL6w; zN#xk0u^Z^iGWeMQ)GoNXS8abdwl6s$xg^9%4}l@4V~l$+)>n((LfsST742*D%Dz2V zdeEmP*`gXIPV;nPFjmpWo^xrpHECW|f}Ub7;VwT{;Ez>`OW?rw_V&5}Fv5EU$BtoH zC~Uh9QjYNA%OC`cKxUS4vQ)pBSC~70bXQMe&$_ZOD3`yX;|EnP9V;+$i@`LwC%LwB zg~kAMfLi=N`CRrlpeQ=rSRoD;kj5|30Jg7frd}sQqPbqyo^=z{o^>kUHvdFo?xo`} zor=@Xja8k)j}iQG0-o402d@tI2eMe}h4mu<437-E06;M?mjw|A>%*SL^k;dd&Y)`c z|4X)%dIfg>p_eiNAh42yuP?YBdsXF!O0k7b$(Hp}B2j;Ba`g+Z&?hh$_B|af13mVG z=%u%otSmFYWz5FOGq5oePl!lf`P8IzV&U5;oDY;OajLaTVFbUVFprZhL*V6U)9|9l zvF>Bq<~J{W>^`2>HowPs#@qQ9A0AAky9x&mSzwI8WCaJXU)dw!*eRIQdMq`;y2-kI zy9Y(MG=L;I)HzYyO#RH5`1I!R-5(7t*YW;U0s840p zc{kvqUWuKexXem~k!Pj~7}_)iZ65_W0KYl886-3T-l;`1Y6@47;GY9v6a+&NL;y26 zS;HNlEk6Tl>aWL)FFE)*2!N=@9%!qqlF`0@NnUvVUqu}e3jl!R>LmCmzCTJaFvO4X zVqjE=5-N5lX@pwP3bAHrpk8hG9&Xpx=dC4wo>g_uxvJPil?8IZFPJ;s)e*$Ogsj{> z$IslfWY~d_gjJ&^;%b*H5!4Zs=q73{ud^$uojnZHv*qV?Rz3@ebI-L#W-*fPbq?2* z35-_45C=z}4UGfmwbcsn?UHqCSTC4=M$IBRifPgjxDd^3PT zsCrTX1;Jzi3s4?32%SHUTu5URvV|$<0EmCa3q_A?8p@eT6;bq-l|?;2SP4ZRHV&oy z%Bgp$Jjx0`9h`$7Blu4Vb19(MSC)kSbVfb&^hQOSCThmpM|X8=5qB9aXWplZqA#O5 zfhc2!0sJL-IEQ@pVf6aB{y65hyXU~8X4ru&&RR28(QWMsat?ls&;){R5JaFD(*Jqr z7!p1p_I9tP=+{9;zq2fzM_NXIiYa<&S0(l-!vOwDmCHFux^g)Oi66!}_%VXS6+3*! z!YVhmoI@S<9BwGR=Rk`7 zg`d`0x#6*)=#_Ox^V-feVeLO1ey=G2kPaws&p~2y&!G)_4rNKDDb@q^QREyX7kl7X zzxC3uQXmM*>XV8odS!hGuZ;gl%6qNYWi|G6Rkm`@VH|cMaS^q=F;w>KOS`cfXqA2H zrV5H4oagn))t%3ru0AYP?rRzVr1E;tp$yJJ;_FK)WpEA>zm!dhB9$|jEu*)r&YFh0 zYfeT+U#d={6@EqlfHbx$&Vf(JQ$=hQrYefQapH~Esy~*wjaNmbMmfnD1pv}^M`2Y- z&OzdHGoYN)N)kT@CD0k1vnyLhudIJdUbyUIqbZ}8x{S4bMgoA@DHU-J*iC7G;)U8| z_zCNZW%RExir(y(a@}V<07ygAcecL^hg9$ob$KUkOwQr;LeWcKRARqw06-dDDd!;R zO4>Me^IE6qOST~-!s`wIq>Ytw4w5cllcG=5T-&sI+ulIf>%il63ji`Um2wV}4)znN zGmN4)>Rz_y68ldE0L)J5zhl9zIO^|Hq4L_2(VO-C|L!sB m0dfxMl^uUEM%kYc+5SJcK^#JqAQij-0000Pyg07*naRCodHeF>bLRh94ks<-N@UeZY?Aqz<$OOt&?5Ku%G*#-~?74^-GxIBFG zX5KOg1V<UP~ z{ce4=b-Dw=cYeRBd%wG{=bn4+J?AP_1Y$S0TvI5BQ^>RD=9#Yccbu(tE4Q`;g$)Hc z(oG^qCnKKg&XR9Cd|(0+SVvy=X{AjgQ8SU81Qfb)44j$O27A;z&VzHR|{=-kI zn%Y|JH+J_E%1xzIZ2vapOTaz#oQ`jQ|GrUuh)`2q$@F~i^7j2I zThj?Jlvj#O-Opb%OD$NiK)w0qn`&TSKyBQ#QGMahZ%kKZ&AqC3Z>y?XwaxL`fQisi z!IQwq_uhN2nmv2ATC--2`oP&A(C+kSxQdW-0Udk^NWatL!G|7H*Ijp=+P;0eHhX${ z=&VxXXLm4MGUfi7-#P&nKU5Xww}ubSpQ+aFJfeR1!yl@OiVF3}Baf)NAAU;R@U^e0 z(8)_iNx0tSvmW(Ys&@QTuls(pVVk<_{qxm1XP>P$ZQ7(RzW8Ew;RS=LdGahB*PZ#H z&Ztt2sLG|f7ck?#nNq1Q>nbe%_<*XYsyckQO?Tyl@nh9@?t55w;hlGUQ@O7_kaPwU z71D^_Y;;%5YVM7@hndJLyV|Za8<)J4=Ue(?)+!37tnAN>3&bP>~1otVh6VC#I>jKmp1{1VIq}vO=_^GJutG7PJSyB$?rSTKFqyX3G%i9BkF;L zQrcZ-7CfgL>FGswl&(J$%2yI!0uHbCS!%37l%10xZLk(Zq^UM*a> zP)%!DtTB<>d$ilx@_0SlB;=@%MOJQm$dS59Xjd*hr8pvp-Uk3CPwb*>Qc+5PmmJ&%ur>uy7o`LsEC!NXS_B@B?r`< zpIoSZ^xZqvLEwT@Pd!y#am5wdTrhuuTHX4V`pdsso{qe4o0qUofHSb~z?+`q{Q2|M z8*jX!Vkr6>t*xqgY_kg20_9p*VGn;SZT-sLS{_hQ3R7p+s5UUG>#a`dRW`l_qc z6TkXZ+8a~c3`e`K4P-s+L(U7Pj#qP9W~p`S*QrhWI@D$7EK?U=dUejapnD;Lk)~71 zbm6*}ev58$)!w)Es;Q?;RWHBvvU>52J?ifN`w!^?U~PyU+@;)s{%i?qnkT8saGkd* zGbFo`C~fwlvvfqgz;a>1k_L>W+rewZ{v-Qzs$6-9kw5j352&Y~ep*M|)w)iP0a=wK zGCh5bVh*b3kJft$oPYlL>fF)Y>7@^m*lRjv{CVSm4&uQP+yqPeef}X{4 zlBzn&`9XBm*3T9TALKuz0!YS8kZ6Q$OmG}05nZ{pMt_v}S5^V+jO{$6Aas`zWrwl9 zixz|Eg+YUZU>vo4dWV%PC0S0$>1LopRNKOqFzTbwjhx zz@2?@w-X&x&x|iSYl%8(T(hdLuUB_`?|zMd{@Xbd-O7qeoo*=w@hChgK8F7r82&BD zTt!e@2esC4`e$5HJV;q*&RS2JFkT%wazuNlKz9?{SB@Q{Gf(YWt*JnI_8s2iEvuo5 znkkW6H@>AK;m>P%QK(W1C(qC4JN6=HZ-k z&Qag~(L-wGSHG&`NPNM>3~zRg(E?e_qgQB#Gzu|y;waVBP^UV(yHyJGVm=tv*r@uj zzuLI>s7ef`bm7M~H>J=Dx-TVj=?bp^Owe61x4Ji;=%-x@;V!s3?o`)?+4Ps5xfo5C zRHw~qQB~E|4CaF^Schw>s@12H&n^V-c8aOL9B_zfE_B2W5 zy#A24fmyS5jp~p0s|2=Qq*=Hw5zKE1Bk9iILgXyfTrX)a4e`N04HTEOB?h6 z)n=!O4w3s;asec2P(cRcy{iAn-b@hZ@9I`atoN0QqbrwwFQP$0B*;*qc$@cY9X@o#!Rh%J@%DZAC=5WstK5bQ`|~1%W44( zyt{$2KgWd0q}6;@*n9VH2fj_y2+<{rDxHr%ke^_%N>y?JdX~@Hg5~!##`n>RnFRX| z?NO;jpZ=-{S5K{7vB}p(nP53aNnJojh-6M|=n<-*nA|y+XnLG1>#7XpoQx-S$G&pf z$8vHDa41>;_AM}+-tCyEnB*GakH7Tg`t_;Fy2-0CCc@;M13dC8Klj4&oAC-2!Z8U^uc=xt>QeP%8|=(E z<(M%x7fd?`xHsgCkU?q24=Mx~uu$zcTAz`WVh4Aot*uWsh}*@d&(<~nz?>wOBl5w5qGCQ%Au9BnR{i(Fq_&7G&;83AJpQ-oL1M z^XBPtY}~lfD-Ss(&p!LC<}OM+cjt?psHNUh*qJk;Hx27<+x9I>HGoSg4 z=S_=zvpXb=4nQR4Du-Mb&2;d^N`H8X^Anl((BuYHz^TY`BDnwKscJQx_~RidfAUQ~ zhVfwOr@cY0LYhtFu>d5D7XT|(42y=|?0GXGgHAaB|NFInq`QC-nGsKEO<*_30Oer( z=lg$=MnXgmYwy$f$T+C1teh48#uk0{;dOzXx!ZESH+q>7`~Qc(d`{>2;f2%n7?1(S zkw1R?c+dY&FaMAZr~eGB6~QK#C*XsFgE%utqX1np!2zUZNKj}n78Ah*%$S&t(1jBs z9x)Smz{8c1zy<1=^*gj(rlS7H%qVFEx@k<ogfz9rXIInbMDp)vxv59_JyXT*OUbn#9xpNhNYz?~6 zPcyG{R>q%^mX`;-ofT_idKSq1XB0DlSqRoh78z1LT>Y3af3(xh4C7^=Cv(Zun+1#@ zV}W+FsZ4@|l*q{0M2EGC?v3!O2*tgS*CNpP^NfVfZP~2Du zOf&ZfMLtzVY#Vg?p~p4bO=1Bc)&5L+=hTZEm3AV`M#1Apv}akZ**c5Sy_eEPwV zG}@tR;hvX8di*RQm_C_TyC4t?j4ZA{@?F^ZQ#I>qq?c@{F5^W$2LZE>EvLP;lZTB zH6TAup}Pr13&6l8-B?*S3JlEgI*kpn`p)^$o(Al}bUNk~mT6{Y7Gy7}0E{SCW@vY+ zYE)$WG+I3vO6H7QasebKZ-y8g)S+fRqyjLKR|qLp#w@kj&1hw}fJC+nF_g?Dts$E% zgk6ww6B}|1l$%jd_-x^wTfp9385Cxu-Hv+$xVN{|_qM0f-1XerJ%^!mUas@Vxg56! zz_HjeEFZ6wR29c*!@b<*lENNipb>9?p5-%dz}CpybV_+^Mo&V)n0Z{S7sCdrsjo@l6=+T@~y3@{VcXq9AvOVHQ52$1;7gZpQ_kpG@Z7SJ`27Kq1 zC?HTNPFaj@gf^%J!&TunbIMuralIY-%55uE+tGIQi3`u}KW|J=&h4W?G(tZbBL|$& zO?4|-p@&rb(zBPTD6TrbwP%+a1b^?8 zQ)am5kL$@PS1OrMeFt{tqzP{2vp|?RQ!O`OuuD!x!NL5l`7nA#q+W$bPxJ@ISL)=` zJ3`}U>8t5d@$;6>Q<0i#HLiKIzQ&GioaVfK|DnfKR}ZeRo;*(7aDHwhlF>ruMlLu< zKue6xbuaAVab^k!OZ)aLZ@Cc?3b$i9^98R#r7KbS99a0VxT*?=G1k3%`5f-N1u|w- zqv9Q?c$~*zu6pR{7gbNJPu=(zf1#R2jZ$B~;|}$i@BUVO^@bbN%iJL zVfU{bZnI3oa#oI#MnGPSKH(JX5_4}6fs4TP%$K(GybO6>eWL)wVcbCDbttweBR%%Q z8r9nuSO5F^>s1qWoMT2eArD=5mD>DdEEGGer;yp$vA1_(`ry_KE^w!ojl8$NS6qKGal+1wa&}c?V#B_&j z5-|ET=`J!oGIqP?Sy77Xkb}md&&spnTefV`ZYNKfrYhkkBjMItmg`XvkTfn-_w_sL z9|~AGy0LO(xcp|sfR)2*fT3b^si5Q|1r~Q3*=^puSyyzy~FLfPp`yo|dGE}~Z#@yok%DCZd&u!kfh)S$zO1U z>nSK=FC0B|i91iP-B3|JAu)W<0HGoTuGsZQlY9Pm#-q_{8 zE0_Wu{pwGAw03{TLGTDWeAz`uHH>m@UduCxEcD&n^68{X{V3~#%PHfgfo*S1Gn^er z&vUst+TM;r44~JPnm2j0s;&&F2n&v0@5)?kGt(@`-hk5*4vrhq;4*%`5Y=}Z0+7I^%5oDzP>`-UZF8N|HgfORk>5||RYq`VEpmNcgXrIg&N z@Zgw*AM>S=L}XrHUbtY9s*8lxKxSt*WBPPm@ha{Fjb@)acTV6G*|9S%r@3h1B3<5{ zPySXV`s2FtzU7hA@)bV<9O&&*iFW_~p(+$wSaVzJ>pC&x&sTWEU17&njlKLoAo>3( z?rem}#2Fac=@Un1AYQ3k-Z)N^u!A_2;7@w|sbBwE-}e>&%P+lDMR3AVg=^P@U*e3P zqEG@C;$Cnh5>bZ^g2$g6)c8dF#B>jAXm|F#o*fSG=!HC@vhJCiLtf&FnYRHL0K?^f z;qltUf4~G=#hGSYpZYEGN z)x02o_|Rcche_4CtyK*Gza+2}n&2MZkyH5?<;%*bJku-K0J!#*#9ZbpUdaSnwes(@KUu z?RweJq0kf94HB)(R5*Du4Yzdf|KmMb0Dc3YRxOJAY8wCdi8p&E=gabN?e+u zndjDnP#A>Jj8`Y#{!err#vP^f-6e)&BSgcs7Q4z>!bDrT|po|5;I(9H*eko_U<9fUuX_cFK&7h1@Fd^=&D+9fM(5_rRjm0v#^{R zdjVql0VsUfx^=78)A0Ojtq_Y!sCl3RB1lKVG9MoupmR7ahO|7C_% z?&tfL&-y!W1kew}&qJ6iA2q^hYc0-VLpYPofo0c%V$AGvGM+LLzWMEMtM-l#ABTq& z;9VDI&P({UYu0L?#x2%`THC>dR~&BG6pZ%$mNMY#L5fEwzv{%Sj^0%!V=DIkU6sioLCjL*xfd0;)^9JGuS{82%jW*mB{dLJ$fE3wzXPeWh;g}3qP6uT;=gMH zGZb@t+Z$kjfZd(OG1yIKdOi-x_f*Y&LBo zM<`1l*qLy1RBdd(8^Tqwv&QuKrWfN3V1J*$KHml`!C#2pwl!z^+Tr9oTQYGe(afwI zIYWz=5>4bL$^EpoA3TB@j}2Z&BxvBz9wI?3k-`8`od-HDUogRAr%O6hqnp5+QZ8_7`7bxIk$PFNAmAi7_ep9MD7Q@Z4^m;Gp2z~Ii%V)=@k8c_hMwhh8 zF^{0@Fi^!RVQkHVeoD9o}92B

uadNf(;Tq|Fbi31<;aWC9p{T!%I-4L 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 @@ -355,8 +374,14 @@ featuredGearBought: false, backgroundUpdate: new Date(), + + broken: false, }; }, + async mounted () { + let worldState = await this.$store.dispatch('worldState:getWorldState'); + this.broken = worldState.worldBoss.extra.worldDmg.seasonalShop; + }, computed: { ...mapState({ content: 'content', diff --git a/website/client/store/actions/index.js b/website/client/store/actions/index.js index bbf917552a..4fc0b035f3 100644 --- a/website/client/store/actions/index.js +++ b/website/client/store/actions/index.js @@ -15,6 +15,7 @@ import * as tags from './tags'; import * as hall from './hall'; import * as shops from './shops'; import * as snackbars from './snackbars'; +import * as worldState from './world-state'; // Actions should be named as 'actionName' and can be accessed as 'namespace:actionName' // Example: fetch in user.js -> 'user:fetch' @@ -35,6 +36,7 @@ const actions = flattenAndNamespace({ hall, shops, snackbars, + worldState, }); export default actions; diff --git a/website/client/store/actions/world-state.js b/website/client/store/actions/world-state.js new file mode 100644 index 0000000000..c5b09cea91 --- /dev/null +++ b/website/client/store/actions/world-state.js @@ -0,0 +1,7 @@ +import axios from 'axios'; + +export async function getWorldState () { + let url = '/api/v3/world-state'; + let response = await axios.get(url); + return response.data.data; +} diff --git a/website/common/locales/en/limited.json b/website/common/locales/en/limited.json index 1d68545b6c..52785e0b94 100644 --- a/website/common/locales/en/limited.json +++ b/website/common/locales/en/limited.json @@ -34,6 +34,7 @@ "seasonalShopFallText": "Happy Fall Festival!! Would you like to buy some rare items? They’ll only be available until October 31st!", "seasonalShopWinterText": "Happy Winter Wonderland!! Would you like to buy some rare items? They’ll only be available until January 31st!", "seasonalShopFallTextBroken": "Oh.... Welcome to the Seasonal Shop... We're stocking autumn Seasonal Edition goodies, or something... Everything here will be available to purchase during the Fall Festival event each year, but we're only open until October 31... I guess you should to stock up now, or you'll have to wait... and wait... and wait... *sigh*", + "seasonalShopBrokenText": "My pavilion!!!!!!! My decorations!!!! Oh, the Dysheartener's destroyed everything :( Please help defeat it in the Tavern so I can rebuild!", "seasonalShopRebirth": "If you bought any of this equipment in the past but don't currently own it, you can repurchase it in the Rewards Column. Initially, you'll only be able to purchase the items for your current class (Warrior by default), but fear not, the other class-specific items will become available if you switch to that class.", "candycaneSet": "Candy Cane (Mage)", "skiSet": "Ski-sassin (Rogue)", diff --git a/website/common/locales/en/questsContent.json b/website/common/locales/en/questsContent.json index 74354df408..6f16b9661c 100644 --- a/website/common/locales/en/questsContent.json +++ b/website/common/locales/en/questsContent.json @@ -662,7 +662,7 @@ "questDysheartenerCompletionChat": "`The Dysheartener is DEFEATED!`\n\nTogether, everyone in Habitica strikes a final blow to their tasks, and the Dysheartener rears back, shrieking with dismay. “What's wrong, Dysheartener?” AnnDeLune calls, eyes sparkling. “Feeling discouraged?”\n\nGlowing pink fractures crack across the Dysheartener's carapace, and it shatters in a puff of pink smoke. As a renewed sense of vigor and determination sweeps across the land, a flurry of delightful sweets rains down upon everyone.\n\nThe crowd cheers wildly, hugging each other as their pets happily chew on the belated Valentine's treats. Suddenly, a joyful chorus of song cascades through the air, and gleaming silhouettes soar across the sky.\n\nOur newly-invigorated optimism has attracted a flock of Hopeful Hippogriffs! The graceful creatures alight upon the ground, ruffling their feathers with interest and prancing about. “It looks like we've made some new friends to help keep our spirits high, even when our tasks are daunting,” Lemoness says.\n\nBeffymaroo already has her arms full with feathered fluffballs. “Maybe they'll help us rebuild the damaged areas of Habitica!”\n\nCrooning and singing, the Hippogriffs lead the way as all the Habitcans work together to restore our beloved home.", "questDysheartenerBossRageTitle": "Shattering Heartbreak", "questDysheartenerBossRageDescription": "The Rage Attack gauge fills when Habiticans miss their Dailies. If it fills up, the Dysheartener will unleash its Shattering Heartbreak attack on one of Habitica's shopkeepers, so be sure to do your tasks!", - "questDysheartenerBossRageSeasonal": "`The Dysheartener uses SHATTERING HEARTBREAK!`\n\nOh, no! After feasting on our undone Dailies, the Dysheartener has gained the strength to unleash its Shattering Heartbreak attack. With a shrill shriek, it brings its spiny forelegs down upon the gazebo that houses the Seasonal Shop! The concussive blast of magic shreds the wood, and the Seasonal Sorceress is overcome by sorrow at the sight.\n\nQuickly, let's keep doing our Dailies so that the beast won't strike again!", + "questDysheartenerBossRageSeasonal": "`The Dysheartener uses SHATTERING HEARTBREAK!`\n\nOh, no! After feasting on our undone Dailies, the Dysheartener has gained the strength to unleash its Shattering Heartbreak attack. With a shrill shriek, it brings its spiny forelegs down upon the pavilion that houses the Seasonal Shop! The concussive blast of magic shreds the wood, and the Seasonal Sorceress is overcome by sorrow at the sight.\n\nQuickly, let's keep doing our Dailies so that the beast won't strike again!", "questDysheartenerBossRageMarket": "`The Dysheartener uses SHATTERING HEARTBREAK!`\n\nHelp! After feasting on our incomplete Dailies, the Dysheartener lets out another Shattering Heartbreak attack, smashing the walls and floor of the Market! As stone rains down, Alex the Merchant weeps at his crushed merchandise, stricken by the destruction.\n\nWe can't let this happen again! Be sure to do all our your Dailies to prevent the Dysheartener from using its final strike.", "questDysheartenerBossRageQuests": "`The Dysheartener uses SHATTERING HEARTBREAK!`\n\nAaaah! We've left our Dailies undone again, and the Dysheartener has mustered the energy for one final blow against our beloved shopkeepers. The countryside around Ian the Quest Master is ripped apart by its Shattering Heartbreak attack, and Ian is struck to the core by the horrific vision. We're so close to defeating this monster.... Hurry! Don't stop now!", "questDysheartenerDropHippogriffPet": "Hopeful Hippogriff (Pet)", From ee087e5eeeb36bc2693345f41d965b1598bbbd25 Mon Sep 17 00:00:00 2001 From: SabreCat Date: Fri, 16 Feb 2018 21:36:10 +0000 Subject: [PATCH 13/49] refactor(rage-strike): consts --- website/client/components/shops/seasonal/index.vue | 2 +- website/client/store/actions/world-state.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/website/client/components/shops/seasonal/index.vue b/website/client/components/shops/seasonal/index.vue index 7ee8ae4f52..597937ff9c 100644 --- a/website/client/components/shops/seasonal/index.vue +++ b/website/client/components/shops/seasonal/index.vue @@ -379,7 +379,7 @@ }; }, async mounted () { - let worldState = await this.$store.dispatch('worldState:getWorldState'); + const worldState = await this.$store.dispatch('worldState:getWorldState'); this.broken = worldState.worldBoss.extra.worldDmg.seasonalShop; }, computed: { diff --git a/website/client/store/actions/world-state.js b/website/client/store/actions/world-state.js index c5b09cea91..924335992f 100644 --- a/website/client/store/actions/world-state.js +++ b/website/client/store/actions/world-state.js @@ -1,7 +1,7 @@ import axios from 'axios'; export async function getWorldState () { - let url = '/api/v3/world-state'; - let response = await axios.get(url); + const url = '/api/v3/world-state'; + const response = await axios.get(url); return response.data.data; } From 74ba55c20b28c19bcc6f5a7d6938b5a59fdfb116 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Sat, 17 Feb 2018 18:11:24 +0100 Subject: [PATCH 14/49] Upgrade tests tools and lint migrations and scripts (part 2) (#9998) * upgrade gulp-babel * upgrade babel-eslint * upgrade eslint-friendly-formatter * start upgrading chai * start to upgrade eslint * restore skipped tests * start to upgrqde monk * fix linting and remove unused file * fix mocha notifications, and common tests * fix unit tests * start to fix initrgration tests * more integration tests fixes * upgrade monk to latest version * lint /scripts * migrations: start moving to /archive unused migrations and run eslint with --fix * lint migrations * fix more integration tests * fix test --- .eslintignore | 11 +- migrations/.eslintrc | 7 + migrations/20130128_add_missing_crons.js | 5 - .../20130128_merge_completed_todo_ids.js | 15 - .../20130129_add_missing_preferences.js | 5 - .../20130204_user_public_private_paths.js | 102 --- migrations/20130208_idLists_to_typeIds.js | 19 - migrations/20130208_user_customizations.js | 20 - migrations/20130307_exp_overflow.js | 39 - migrations/20130307_normalize_algo_values.js | 47 -- migrations/20130307_remove_duff_histories.js | 28 - migrations/20130326_migrate_pets.js | 98 --- migrations/20130327_apply_tokens.js | 110 --- migrations/20130503_max_gear_achievement.js | 22 - migrations/20130508_add_backer_pets.js | 1 - migrations/20130602_survey_rewards.js | 31 - .../20130612_survey_rewards_individual.js | 9 - migrations/20130615_add_extra_indexes.js | 4 - migrations/20130908_cleanup_corrupt_tags.js | 16 - migrations/20131022_purchased_and_newStuff.js | 5 - migrations/20131022_restore_ads.js | 12 - migrations/20131102_restore_task_ids.js | 25 - migrations/20131104_remove_invalid_dues.js | 7 - migrations/20131105_remove_history_ids.js | 21 - migrations/20131108_add_gems_for_contribs.js | 4 - migrations/20131111_task_NaN.js | 15 - .../20131114_migrate_websites_to_blurb.js | 14 - .../20131115_update_gear_preferences.js | 10 - migrations/20131117_fix_task_types.js | 18 - migrations/20131117_remove_undefined_pets.js | 12 - migrations/20131122_deleted_tags.js | 13 - .../20131123_set_default_party_order.js | 8 - migrations/20131126_clean_dayStart.js | 5 - migrations/20131126_turkey_pet.js | 1 - migrations/20131127_restore_dayStart.js | 38 - migrations/20131221_restore_NaN_history.js | 51 -- migrations/20131225_restore_streaks.js | 38 - ...20140119_task_creation_completion_dates.js | 8 - migrations/20140130_birthdayEnd.js | 1 - migrations/20140130_birthdayStart.js | 12 - migrations/20140220_challenge_memberCount.js | 3 - migrations/20140301_missing_mysteries.js | 14 - migrations/20140610_missing_backer_mount.js | 1 - migrations/20140712_wiped_quest_membership.js | 11 - ...20140803_remove_undefined_notifications.js | 13 - ...0140829_change_headAccessory_to_eyewear.js | 81 -- ...ncrease_gems_for_previous_contributions.js | 131 ---- .../20140914_upgrade_admin_contrib_tiers.js | 79 -- migrations/20140922_free_candy.js | 18 - migrations/20141006_jackolantern_pet.js | 1 - migrations/20141126_turkey_mounts.js | 11 - migrations/20141211_NaN_consecutives.js | 4 - migrations/20141230_new_years_hats.js | 11 - migrations/20150107_plan_dateUpdated_null.js | 8 - migrations/20150124_mountmaster_fix.js | 88 --- migrations/20150130_birthday_goodies.js | 36 - ...150131_birthday_goodies_fix_remove_robe.js | 78 -- ...ert_creation_date_from_string_to_object.js | 112 --- .../20150201_recapture_emails_phase_update.js | 7 - migrations/20150218_interactive_tour.js | 10 - migrations/20150224_force_resting_in_inn.js | 64 -- migrations/20150310_survey_achievements.js | 5 - migrations/20150604_ultimateGearSets.js | 138 ---- migrations/20150731_purple_gryphon.js | 5 - migrations/20150731_veteran_tiger.js | 5 - migrations/20150731_veteran_wolf.js | 7 - migrations/20150906_groups_fix_leaders.js | 79 -- .../20150906_groups_remove_deleted_users.js | 87 --- migrations/20150906_groups_remove_empty.js | 21 - .../20150906_sync_groups_with_firebase.js | 43 -- migrations/20151013_jackolanterns.js | 67 -- .../20151021_usernames_emails_lowercase.js | 63 -- migrations/20151105_tutorial_flags.js | 63 -- migrations/20151116_costume_contest_award.js | 102 --- .../20151116_costume_contest_to_number.js | 64 -- migrations/20151125_turkey_ladder.js | 71 -- migrations/20151229_new_years_hats.js | 70 -- migrations/20160521_veteran_ladder.js | 82 -- migrations/20160527_fix_empty_checklist_id.js | 84 -- migrations/20160731_naming_day.js | 82 -- migrations/20160731_takeThis.js | 71 -- migrations/20160831_takeThis.js | 72 -- migrations/20161002_takeThis.js | 73 -- migrations/20161030-jackolanterns.js | 86 --- migrations/20161102_takeThis.js | 75 -- migrations/20161122_turkey_ladder.js | 74 -- migrations/20161230_nye_hats.js | 73 -- migrations/20170120_missing_incentive.js | 113 --- migrations/20170131_habit_birthday.js | 109 --- migrations/20170418_subscriber_jackalopes.js | 88 --- migrations/20170711_orcas.js | 90 --- migrations/20170731_naming_day.js | 109 --- migrations/20170928_redesign_guilds.js | 97 --- migrations/20171030_jackolanterns.js | 111 --- migrations/20171230_nye_hats.js | 103 --- migrations/api_v3/challenges.js | 218 ------ migrations/api_v3/challengesMembers.js | 149 ---- migrations/api_v3/coupons.js | 142 ---- migrations/api_v3/emailUnsubscriptions.js | 143 ---- migrations/api_v3/groups.js | 217 ------ migrations/api_v3/users.js | 268 ------- migrations/apology_gems.js | 1 - .../2013/20130128_add_missing_crons.js | 5 + .../2013/20130128_merge_completed_todo_ids.js | 15 + .../2013/20130129_add_missing_preferences.js | 5 + .../2013}/20130204_count_habits.js | 20 +- .../20130204_user_public_private_paths.js | 101 +++ .../2013/20130208_idLists_to_typeIds.js | 19 + .../2013/20130208_user_customizations.js | 18 + .../archive/2013/20130307_exp_overflow.js | 37 + .../2013/20130307_normalize_algo_values.js | 46 ++ .../2013/20130307_remove_duff_histories.js | 28 + .../archive/2013/20130326_migrate_pets.js | 98 +++ .../archive/2013/20130327_apply_tokens.js | 109 +++ .../2013/20130503_max_gear_achievement.js | 23 + .../2013}/20130507_fix_broken_tags.js | 11 +- .../archive/2013/20130508_add_backer_pets.js | 1 + .../20130508_fix_duff_party_subscriptions.js | 41 +- .../2013}/20130518_setup_groups.js | 42 +- .../archive/2013/20130602_survey_rewards.js | 31 + .../20130612_survey_rewards_individual.js | 9 + .../2013/20130615_add_extra_indexes.js | 4 + .../2013/20130908_cleanup_corrupt_tags.js | 16 + .../20130908_cleanup_derby_corruption.js | 35 +- .../2013}/20130908_remove_staged_users.js | 12 +- .../2013/20131022_purchased_and_newStuff.js | 5 + .../archive/2013/20131022_restore_ads.js | 12 + .../20131028_task_subdocs_tags_invites.js | 91 ++- .../archive/2013/20131102_restore_task_ids.js | 25 + .../2013/20131104_remove_invalid_dues.js | 7 + .../2013}/20131104_restore_lost_task_data.js | 46 +- .../2013/20131105_remove_history_ids.js | 21 + .../20131107_from_backer_to_contributor.js | 10 +- .../2013/20131108_add_gems_for_contribs.js | 4 + .../2013}/20131109_refactor_pets.js | 17 +- migrations/archive/2013/20131111_task_NaN.js | 15 + .../20131114_migrate_websites_to_blurb.js | 14 + .../2013/20131115_update_gear_preferences.js | 10 + .../archive/2013/20131117_fix_task_types.js | 18 + .../2013/20131117_remove_undefined_pets.js | 12 + .../archive/2013/20131122_deleted_tags.js | 13 + .../2013/20131123_set_default_party_order.js | 8 + .../archive/2013/20131126_clean_dayStart.js | 5 + .../archive/2013/20131126_turkey_pet.js | 1 + .../archive/2013/20131127_restore_dayStart.js | 42 + .../2013}/20131214_classes.coffee | 0 .../2013}/20131217_unearned_backer_gear.js | 16 +- .../2013/20131221_restore_NaN_history.js | 55 ++ .../archive/2013/20131225_restore_streaks.js | 42 + ...20140119_task_creation_completion_dates.js | 8 + .../archive/2014/20140130_birthdayEnd.js | 1 + .../archive/2014/20140130_birthdayStart.js | 12 + .../2014/20140220_challenge_memberCount.js | 3 + .../2014/20140301_missing_mysteries.js | 14 + .../2014/20140610_missing_backer_mount.js | 1 + .../2014/20140712_wiped_quest_membership.js | 11 + ...20140803_remove_undefined_notifications.js | 13 + ...emove_undefined_and_false_notifications.js | 38 +- ...0140829_change_headAccessory_to_eyewear.js | 83 ++ ...ncrease_gems_for_previous_contributions.js | 144 ++++ .../20140914_upgrade_admin_contrib_tiers.js | 86 +++ .../archive/2014/20140922_free_candy.js | 18 + .../archive/2014/20141006_jackolantern_pet.js | 1 + .../2014}/20141117_consecutive_months.js | 16 +- .../archive/2014/20141126_turkey_mounts.js | 11 + .../archive/2014/20141211_NaN_consecutives.js | 4 + .../archive/2014/20141230_new_years_hats.js | 11 + .../2015/20150107_plan_dateUpdated_null.js | 8 + .../archive/2015/20150124_mountmaster_fix.js | 95 +++ .../archive/2015/20150130_birthday_goodies.js | 36 + ...150131_birthday_goodies_fix_remove_robe.js | 85 +++ ...ert_creation_date_from_string_to_object.js | 119 +++ .../20150201_recapture_emails_phase_update.js | 7 + .../archive/2015/20150218_interactive_tour.js | 10 + .../2015/20150224_force_resting_in_inn.js | 71 ++ .../2015/20150310_survey_achievements.js | 5 + .../{ => archive/2015}/20150325_egg_quest.js | 6 +- .../archive/2015/20150604_ultimateGearSets.js | 141 ++++ .../2015}/20150706_orca_mounts.js | 4 +- .../archive/2015/20150731_purple_gryphon.js | 5 + .../archive/2015/20150731_veteran_tiger.js | 5 + .../archive/2015/20150731_veteran_wolf.js | 7 + .../2015/20150906_groups_fix_leaders.js | 79 ++ .../20150906_groups_remove_deleted_users.js | 86 +++ .../2015/20150906_groups_remove_empty.js | 21 + .../20150906_sync_groups_with_firebase.js | 43 ++ .../archive/2015/20151013_jackolanterns.js | 74 ++ .../20151021_usernames_emails_lowercase.js | 63 ++ .../archive/2015/20151105_tutorial_flags.js | 70 ++ .../2015/20151116_costume_contest_award.js | 109 +++ .../20151116_costume_contest_to_number.js | 71 ++ .../archive/2015/20151125_turkey_ladder.js | 78 ++ .../archive/2015/20151229_new_years_hats.js | 77 ++ ...enges_condense_same_day_history_entries.js | 81 +- .../2016}/20160129_habit_birthday.js | 83 +- .../archive/2016/20160521_veteran_ladder.js | 89 +++ .../2016/20160527_fix_empty_checklist_id.js | 91 +++ .../2016}/20160529_fix_challenges.js | 36 +- ...ks_from_null_value_in_challenges_broken.js | 34 +- .../20160602_convert_quest_collection.js | 32 +- ...20160605_convert_quest_collection_again.js | 30 +- .../2016}/20160615_fix_bad_emails.js | 24 +- .../archive/2016/20160731_naming_day.js | 89 +++ migrations/archive/2016/20160731_takeThis.js | 78 ++ migrations/archive/2016/20160831_takeThis.js | 79 ++ .../20161002_add_missing_webhook_type.js | 30 +- migrations/archive/2016/20161002_takeThis.js | 80 ++ .../archive/2016/20161030-jackolanterns.js | 93 +++ migrations/archive/2016/20161102_takeThis.js | 82 ++ .../archive/2016/20161122_turkey_ladder.js | 81 ++ migrations/archive/2016/20161230_nye_hats.js | 80 ++ ...unce_collection_quest_change_in_parties.js | 40 +- .../2017/20170120_missing_incentive.js | 118 +++ .../archive/2017/20170131_habit_birthday.js | 114 +++ .../2017/20170418_subscriber_jackalopes.js | 93 +++ .../2017}/20170425_missing_incentives.js | 101 +-- .../2017}/20170616_achievements.js | 79 +- migrations/archive/2017/20170711_orcas.js | 95 +++ .../archive/2017/20170731_naming_day.js | 114 +++ .../archive/2017/20170928_redesign_guilds.js | 102 +++ .../2017}/20170928_redesign_launch.js | 89 ++- .../archive/2017/20171030_jackolanterns.js | 116 +++ .../2017}/20171117_turkey_ladder.js | 81 +- migrations/archive/2017/20171230_nye_hats.js | 108 +++ .../2018}/20180110_nextPaymentProcessing.js | 61 +- .../2018}/20180125_clean_new_notifications.js | 0 .../2018}/20180125_notifications.js | 0 .../2018}/20180130_habit_birthday.js | 109 +-- migrations/archive/README.md | 4 + migrations/archive/api_v3/challenges.js | 218 ++++++ .../archive/api_v3/challengesMembers.js | 149 ++++ migrations/archive/api_v3/coupons.js | 142 ++++ .../archive/api_v3/emailUnsubscriptions.js | 143 ++++ migrations/archive/api_v3/groups.js | 217 ++++++ migrations/{ => archive}/api_v3/indexes.js | 0 migrations/archive/api_v3/users.js | 268 +++++++ .../{ => archive}/manual_password_reset.js | 34 +- migrations/archive/metrics.js | 85 +++ migrations/challenges/sync-all-challenges.js | 6 +- migrations/command-line/.eslintrc | 10 + migrations/command-line/apology_gems.js | 1 + .../{ => command-line}/cancelSubscription.js | 4 +- migrations/command-line/contribs_plan.js | 23 + .../{ => command-line}/current_period_end.js | 6 +- .../duplicatedTasksFindAndRemove.js | 44 +- .../{ => command-line}/facebook_to_local.js | 10 +- .../{ => command-line}/find_unique_user.js | 8 +- migrations/{ => command-line}/freeMonth.js | 20 +- migrations/command-line/habitica_day.js | 5 + migrations/command-line/missing_gems.js | 1 + .../{ => command-line}/mystery_items.js | 30 +- migrations/contribs_plan.js | 23 - .../groups/add-unlimited-subscription.js | 46 +- migrations/groups/create-group.js | 33 +- migrations/groups/habitrpg-jackalopes.js | 21 +- .../groups/update-groups-with-group-plans.js | 16 +- migrations/habitica_day.js | 5 - migrations/metrics.js | 86 --- migrations/migration-runner.js | 12 +- migrations/missing_gems.js | 1 - migrations/new_stuff.js | 73 +- migrations/restock_armoire.js | 71 +- .../restock_armoire_for_users_that_need_it.js | 89 ++- migrations/restore-profile-data.js | 84 +- migrations/s3-upload.js | 18 +- migrations/takeThis.js | 100 +-- migrations/tasks/tasks-set-everyX.js | 75 +- migrations/tasks/tasks-set-yesterdailies.js | 171 +++-- migrations/users/account-transfer.js | 18 +- migrations/users/achievement-restore.js | 20 +- migrations/users/users-to-test.js | 69 +- migrations/utils/connect.js | 8 +- migrations/utils/logger.js | 14 +- migrations/utils/timer.js | 2 +- package-lock.json | 715 ++++++++++-------- package.json | 22 +- scripts/paypalBillingSetup.js | 147 ++-- ...lenges_challengeId_winner_winnerId.test.js | 2 +- .../api/v3/integration/chat/POST-chat.test.js | 20 +- .../integration/content/GET-content.test.js | 6 +- .../debug/POST-debug_addTenGems.test.js | 10 +- .../debug/POST-debug_make-admin.test.js | 10 +- .../debug/POST-debug_modify-inventory.test.js | 10 +- .../debug/POST-debug_quest-progress.test.js | 20 +- .../debug/POST-debug_set-cron.test.js | 10 +- .../v3/integration/groups/GET-groups.test.js | 4 +- .../groups/POST-groups_groupId_join.test.js | 38 +- .../groups/POST-groups_groupId_leave.js | 4 +- .../groups/POST-groups_groupId_reject.test.js | 6 +- .../POST-groups_id_removeMember.test.js | 4 +- .../groups/POST-groups_invite.test.js | 206 ++--- .../news/POST-news_tell_me_later.test.js | 2 +- .../apple/POST-payments_apple_verifyiap.js | 6 +- .../POST-groups_groupId_quests_accept.test.js | 60 +- ...-groups_groupId_quests_force-start.test.js | 50 +- .../POST-groups_groupid_quests_abort.test.js | 20 +- .../POST-groups_groupid_quests_cancel.test.js | 40 +- .../POST-groups_groupid_quests_leave.test.js | 30 +- .../POST-groups_groupid_quests_reject.test.js | 60 +- .../POST-tasks_id_score_direction.test.js | 20 +- .../POST-tasks_unlink-all_challengeId.test.js | 32 +- .../POST-tasks_unlink-one_taskId.test.js | 50 +- .../v3/integration/tasks/PUT-tasks_id.test.js | 6 +- ...lenge_challengeId_taskId_checklist.test.js | 10 +- .../PUT-tasks_challenge_challengeId.test.js | 2 +- ...allengeId_tasksId_checklist_itemId.test.js | 20 +- .../tags/POST-tasks_taskId_tags.test.js | 1 + .../user/POST-user_release_both.test.js | 6 +- .../DELETE-user_auth_social_network.test.js | 8 +- .../user/auth/POST-register_local.test.js | 2 +- .../world-state/GET-world-state.test.js | 2 +- test/api/v3/unit/libs/apiMessages.js | 6 +- .../unit/libs/collectionManipulators.test.js | 2 +- test/api/v3/unit/libs/cron.test.js | 8 +- .../libs/payments/amazon/checkout.test.js | 10 +- .../libs/payments/amazon/subscribe.test.js | 40 +- .../group-plans/group-payments-create.test.js | 4 +- .../libs/payments/paypal/checkout.test.js | 10 +- .../stripe/cancel-subscription.test.js | 30 +- .../stripe/checkout-subscription.test.js | 30 +- .../libs/payments/stripe/checkout.test.js | 10 +- .../payments/stripe/edit-subscription.test.js | 40 +- .../payments/stripe/handle-webhook.test.js | 6 +- test/api/v3/unit/libs/taskManager.js | 14 +- test/api/v3/unit/middlewares/cors.test.js | 2 +- .../api/v3/unit/middlewares/cronMiddleware.js | 12 +- .../unit/middlewares/maintenanceMode.test.js | 2 +- test/api/v3/unit/middlewares/redirects.js | 16 +- test/api/v3/unit/models/challenge.test.js | 4 +- test/api/v3/unit/models/group_tasks.test.js | 6 +- test/api/v3/unit/models/task.test.js | 2 +- test/api/v3/unit/models/user.test.js | 2 +- test/client/e2e/specs/test.js | 2 +- test/common/libs/taskClasses.test.js | 82 -- test/common/ops/releaseBoth.js | 2 +- test/mocha.opts | 1 - .../client/components/groups/membersModal.vue | 8 +- .../components/inventory/items/index.vue | 2 +- website/client/components/static/faq.vue | 4 +- website/client/store/actions/common.js | 12 +- website/client/store/actions/shops.js | 20 +- .../common/script/content/appearance/skin.js | 2 +- .../script/content/shop-featuredItems.js | 2 +- website/common/script/index.js | 3 - website/common/script/libs/taskClasses.js | 83 -- website/server/controllers/api-v3/auth.js | 10 +- .../server/controllers/api-v3/challenges.js | 10 +- website/server/controllers/api-v3/groups.js | 6 +- website/server/controllers/api-v3/hall.js | 32 +- website/server/controllers/api-v3/i18n.js | 10 +- website/server/controllers/api-v3/members.js | 4 +- website/server/controllers/api-v3/quests.js | 4 +- .../server/controllers/api-v3/tasks/groups.js | 4 +- .../controllers/top-level/dataexport.js | 12 +- website/server/libs/logger.js | 8 +- website/server/libs/password.js | 12 +- website/server/libs/payments.js | 8 +- website/server/libs/pushNotifications.js | 44 +- website/server/middlewares/analytics.js | 6 +- website/server/middlewares/auth.js | 44 +- website/server/middlewares/language.js | 14 +- website/server/models/group.js | 18 +- 362 files changed, 8041 insertions(+), 7813 deletions(-) create mode 100644 migrations/.eslintrc delete mode 100644 migrations/20130128_add_missing_crons.js delete mode 100644 migrations/20130128_merge_completed_todo_ids.js delete mode 100644 migrations/20130129_add_missing_preferences.js delete mode 100644 migrations/20130204_user_public_private_paths.js delete mode 100644 migrations/20130208_idLists_to_typeIds.js delete mode 100644 migrations/20130208_user_customizations.js delete mode 100644 migrations/20130307_exp_overflow.js delete mode 100644 migrations/20130307_normalize_algo_values.js delete mode 100644 migrations/20130307_remove_duff_histories.js delete mode 100644 migrations/20130326_migrate_pets.js delete mode 100644 migrations/20130327_apply_tokens.js delete mode 100644 migrations/20130503_max_gear_achievement.js delete mode 100644 migrations/20130508_add_backer_pets.js delete mode 100644 migrations/20130602_survey_rewards.js delete mode 100644 migrations/20130612_survey_rewards_individual.js delete mode 100644 migrations/20130615_add_extra_indexes.js delete mode 100644 migrations/20130908_cleanup_corrupt_tags.js delete mode 100644 migrations/20131022_purchased_and_newStuff.js delete mode 100644 migrations/20131022_restore_ads.js delete mode 100644 migrations/20131102_restore_task_ids.js delete mode 100644 migrations/20131104_remove_invalid_dues.js delete mode 100644 migrations/20131105_remove_history_ids.js delete mode 100644 migrations/20131108_add_gems_for_contribs.js delete mode 100644 migrations/20131111_task_NaN.js delete mode 100644 migrations/20131114_migrate_websites_to_blurb.js delete mode 100644 migrations/20131115_update_gear_preferences.js delete mode 100644 migrations/20131117_fix_task_types.js delete mode 100644 migrations/20131117_remove_undefined_pets.js delete mode 100644 migrations/20131122_deleted_tags.js delete mode 100644 migrations/20131123_set_default_party_order.js delete mode 100644 migrations/20131126_clean_dayStart.js delete mode 100644 migrations/20131126_turkey_pet.js delete mode 100644 migrations/20131127_restore_dayStart.js delete mode 100644 migrations/20131221_restore_NaN_history.js delete mode 100644 migrations/20131225_restore_streaks.js delete mode 100644 migrations/20140119_task_creation_completion_dates.js delete mode 100644 migrations/20140130_birthdayEnd.js delete mode 100644 migrations/20140130_birthdayStart.js delete mode 100644 migrations/20140220_challenge_memberCount.js delete mode 100644 migrations/20140301_missing_mysteries.js delete mode 100644 migrations/20140610_missing_backer_mount.js delete mode 100644 migrations/20140712_wiped_quest_membership.js delete mode 100644 migrations/20140803_remove_undefined_notifications.js delete mode 100644 migrations/20140829_change_headAccessory_to_eyewear.js delete mode 100644 migrations/20140831_increase_gems_for_previous_contributions.js delete mode 100644 migrations/20140914_upgrade_admin_contrib_tiers.js delete mode 100644 migrations/20140922_free_candy.js delete mode 100644 migrations/20141006_jackolantern_pet.js delete mode 100644 migrations/20141126_turkey_mounts.js delete mode 100644 migrations/20141211_NaN_consecutives.js delete mode 100644 migrations/20141230_new_years_hats.js delete mode 100644 migrations/20150107_plan_dateUpdated_null.js delete mode 100644 migrations/20150124_mountmaster_fix.js delete mode 100644 migrations/20150130_birthday_goodies.js delete mode 100644 migrations/20150131_birthday_goodies_fix_remove_robe.js delete mode 100644 migrations/20150201_convert_creation_date_from_string_to_object.js delete mode 100644 migrations/20150201_recapture_emails_phase_update.js delete mode 100644 migrations/20150218_interactive_tour.js delete mode 100644 migrations/20150224_force_resting_in_inn.js delete mode 100644 migrations/20150310_survey_achievements.js delete mode 100644 migrations/20150604_ultimateGearSets.js delete mode 100644 migrations/20150731_purple_gryphon.js delete mode 100644 migrations/20150731_veteran_tiger.js delete mode 100644 migrations/20150731_veteran_wolf.js delete mode 100644 migrations/20150906_groups_fix_leaders.js delete mode 100644 migrations/20150906_groups_remove_deleted_users.js delete mode 100644 migrations/20150906_groups_remove_empty.js delete mode 100644 migrations/20150906_sync_groups_with_firebase.js delete mode 100644 migrations/20151013_jackolanterns.js delete mode 100644 migrations/20151021_usernames_emails_lowercase.js delete mode 100644 migrations/20151105_tutorial_flags.js delete mode 100644 migrations/20151116_costume_contest_award.js delete mode 100644 migrations/20151116_costume_contest_to_number.js delete mode 100644 migrations/20151125_turkey_ladder.js delete mode 100644 migrations/20151229_new_years_hats.js delete mode 100644 migrations/20160521_veteran_ladder.js delete mode 100644 migrations/20160527_fix_empty_checklist_id.js delete mode 100644 migrations/20160731_naming_day.js delete mode 100644 migrations/20160731_takeThis.js delete mode 100644 migrations/20160831_takeThis.js delete mode 100644 migrations/20161002_takeThis.js delete mode 100644 migrations/20161030-jackolanterns.js delete mode 100644 migrations/20161102_takeThis.js delete mode 100644 migrations/20161122_turkey_ladder.js delete mode 100644 migrations/20161230_nye_hats.js delete mode 100644 migrations/20170120_missing_incentive.js delete mode 100644 migrations/20170131_habit_birthday.js delete mode 100644 migrations/20170418_subscriber_jackalopes.js delete mode 100644 migrations/20170711_orcas.js delete mode 100644 migrations/20170731_naming_day.js delete mode 100644 migrations/20170928_redesign_guilds.js delete mode 100644 migrations/20171030_jackolanterns.js delete mode 100644 migrations/20171230_nye_hats.js delete mode 100644 migrations/api_v3/challenges.js delete mode 100644 migrations/api_v3/challengesMembers.js delete mode 100644 migrations/api_v3/coupons.js delete mode 100644 migrations/api_v3/emailUnsubscriptions.js delete mode 100644 migrations/api_v3/groups.js delete mode 100644 migrations/api_v3/users.js delete mode 100644 migrations/apology_gems.js create mode 100644 migrations/archive/2013/20130128_add_missing_crons.js create mode 100644 migrations/archive/2013/20130128_merge_completed_todo_ids.js create mode 100644 migrations/archive/2013/20130129_add_missing_preferences.js rename migrations/{ => archive/2013}/20130204_count_habits.js (55%) create mode 100644 migrations/archive/2013/20130204_user_public_private_paths.js create mode 100644 migrations/archive/2013/20130208_idLists_to_typeIds.js create mode 100644 migrations/archive/2013/20130208_user_customizations.js create mode 100644 migrations/archive/2013/20130307_exp_overflow.js create mode 100644 migrations/archive/2013/20130307_normalize_algo_values.js create mode 100644 migrations/archive/2013/20130307_remove_duff_histories.js create mode 100644 migrations/archive/2013/20130326_migrate_pets.js create mode 100644 migrations/archive/2013/20130327_apply_tokens.js create mode 100644 migrations/archive/2013/20130503_max_gear_achievement.js rename migrations/{ => archive/2013}/20130507_fix_broken_tags.js (58%) create mode 100644 migrations/archive/2013/20130508_add_backer_pets.js rename migrations/{ => archive/2013}/20130508_fix_duff_party_subscriptions.js (51%) rename migrations/{ => archive/2013}/20130518_setup_groups.js (54%) create mode 100644 migrations/archive/2013/20130602_survey_rewards.js create mode 100644 migrations/archive/2013/20130612_survey_rewards_individual.js create mode 100644 migrations/archive/2013/20130615_add_extra_indexes.js create mode 100644 migrations/archive/2013/20130908_cleanup_corrupt_tags.js rename migrations/{ => archive/2013}/20130908_cleanup_derby_corruption.js (61%) rename migrations/{ => archive/2013}/20130908_remove_staged_users.js (86%) create mode 100644 migrations/archive/2013/20131022_purchased_and_newStuff.js create mode 100644 migrations/archive/2013/20131022_restore_ads.js rename migrations/{ => archive/2013}/20131028_task_subdocs_tags_invites.js (51%) create mode 100644 migrations/archive/2013/20131102_restore_task_ids.js create mode 100644 migrations/archive/2013/20131104_remove_invalid_dues.js rename migrations/{ => archive/2013}/20131104_restore_lost_task_data.js (55%) create mode 100644 migrations/archive/2013/20131105_remove_history_ids.js rename migrations/{ => archive/2013}/20131107_from_backer_to_contributor.js (63%) create mode 100644 migrations/archive/2013/20131108_add_gems_for_contribs.js rename migrations/{ => archive/2013}/20131109_refactor_pets.js (61%) create mode 100644 migrations/archive/2013/20131111_task_NaN.js create mode 100644 migrations/archive/2013/20131114_migrate_websites_to_blurb.js create mode 100644 migrations/archive/2013/20131115_update_gear_preferences.js create mode 100644 migrations/archive/2013/20131117_fix_task_types.js create mode 100644 migrations/archive/2013/20131117_remove_undefined_pets.js create mode 100644 migrations/archive/2013/20131122_deleted_tags.js create mode 100644 migrations/archive/2013/20131123_set_default_party_order.js create mode 100644 migrations/archive/2013/20131126_clean_dayStart.js create mode 100644 migrations/archive/2013/20131126_turkey_pet.js create mode 100644 migrations/archive/2013/20131127_restore_dayStart.js rename migrations/{ => archive/2013}/20131214_classes.coffee (100%) rename migrations/{ => archive/2013}/20131217_unearned_backer_gear.js (51%) create mode 100644 migrations/archive/2013/20131221_restore_NaN_history.js create mode 100644 migrations/archive/2013/20131225_restore_streaks.js create mode 100644 migrations/archive/2014/20140119_task_creation_completion_dates.js create mode 100644 migrations/archive/2014/20140130_birthdayEnd.js create mode 100644 migrations/archive/2014/20140130_birthdayStart.js create mode 100644 migrations/archive/2014/20140220_challenge_memberCount.js create mode 100644 migrations/archive/2014/20140301_missing_mysteries.js create mode 100644 migrations/archive/2014/20140610_missing_backer_mount.js create mode 100644 migrations/archive/2014/20140712_wiped_quest_membership.js create mode 100644 migrations/archive/2014/20140803_remove_undefined_notifications.js rename migrations/{ => archive/2014}/20140823_remove_undefined_and_false_notifications.js (53%) create mode 100644 migrations/archive/2014/20140829_change_headAccessory_to_eyewear.js create mode 100644 migrations/archive/2014/20140831_increase_gems_for_previous_contributions.js create mode 100644 migrations/archive/2014/20140914_upgrade_admin_contrib_tiers.js create mode 100644 migrations/archive/2014/20140922_free_candy.js create mode 100644 migrations/archive/2014/20141006_jackolantern_pet.js rename migrations/{ => archive/2014}/20141117_consecutive_months.js (54%) create mode 100644 migrations/archive/2014/20141126_turkey_mounts.js create mode 100644 migrations/archive/2014/20141211_NaN_consecutives.js create mode 100644 migrations/archive/2014/20141230_new_years_hats.js create mode 100644 migrations/archive/2015/20150107_plan_dateUpdated_null.js create mode 100644 migrations/archive/2015/20150124_mountmaster_fix.js create mode 100644 migrations/archive/2015/20150130_birthday_goodies.js create mode 100644 migrations/archive/2015/20150131_birthday_goodies_fix_remove_robe.js create mode 100644 migrations/archive/2015/20150201_convert_creation_date_from_string_to_object.js create mode 100644 migrations/archive/2015/20150201_recapture_emails_phase_update.js create mode 100644 migrations/archive/2015/20150218_interactive_tour.js create mode 100644 migrations/archive/2015/20150224_force_resting_in_inn.js create mode 100644 migrations/archive/2015/20150310_survey_achievements.js rename migrations/{ => archive/2015}/20150325_egg_quest.js (56%) create mode 100644 migrations/archive/2015/20150604_ultimateGearSets.js rename migrations/{ => archive/2015}/20150706_orca_mounts.js (52%) create mode 100644 migrations/archive/2015/20150731_purple_gryphon.js create mode 100644 migrations/archive/2015/20150731_veteran_tiger.js create mode 100644 migrations/archive/2015/20150731_veteran_wolf.js create mode 100644 migrations/archive/2015/20150906_groups_fix_leaders.js create mode 100644 migrations/archive/2015/20150906_groups_remove_deleted_users.js create mode 100644 migrations/archive/2015/20150906_groups_remove_empty.js create mode 100644 migrations/archive/2015/20150906_sync_groups_with_firebase.js create mode 100644 migrations/archive/2015/20151013_jackolanterns.js create mode 100644 migrations/archive/2015/20151021_usernames_emails_lowercase.js create mode 100644 migrations/archive/2015/20151105_tutorial_flags.js create mode 100644 migrations/archive/2015/20151116_costume_contest_award.js create mode 100644 migrations/archive/2015/20151116_costume_contest_to_number.js create mode 100644 migrations/archive/2015/20151125_turkey_ladder.js create mode 100644 migrations/archive/2015/20151229_new_years_hats.js rename migrations/{ => archive/2016}/20160111_challenges_condense_same_day_history_entries.js (56%) rename migrations/{ => archive/2016}/20160129_habit_birthday.js (50%) create mode 100644 migrations/archive/2016/20160521_veteran_ladder.js create mode 100644 migrations/archive/2016/20160527_fix_empty_checklist_id.js rename migrations/{ => archive/2016}/20160529_fix_challenges.js (87%) rename migrations/{ => archive/2016}/20160530_fix_tasks_from_null_value_in_challenges_broken.js (88%) rename migrations/{ => archive/2016}/20160602_convert_quest_collection.js (87%) rename migrations/{ => archive/2016}/20160605_convert_quest_collection_again.js (86%) rename migrations/{ => archive/2016}/20160615_fix_bad_emails.js (82%) create mode 100644 migrations/archive/2016/20160731_naming_day.js create mode 100644 migrations/archive/2016/20160731_takeThis.js create mode 100644 migrations/archive/2016/20160831_takeThis.js rename migrations/{ => archive/2016}/20161002_add_missing_webhook_type.js (81%) create mode 100644 migrations/archive/2016/20161002_takeThis.js create mode 100644 migrations/archive/2016/20161030-jackolanterns.js create mode 100644 migrations/archive/2016/20161102_takeThis.js create mode 100644 migrations/archive/2016/20161122_turkey_ladder.js create mode 100644 migrations/archive/2016/20161230_nye_hats.js rename migrations/{ => archive/2017}/20170111_announce_collection_quest_change_in_parties.js (78%) create mode 100644 migrations/archive/2017/20170120_missing_incentive.js create mode 100644 migrations/archive/2017/20170131_habit_birthday.js create mode 100644 migrations/archive/2017/20170418_subscriber_jackalopes.js rename migrations/{ => archive/2017}/20170425_missing_incentives.js (70%) rename migrations/{ => archive/2017}/20170616_achievements.js (56%) create mode 100644 migrations/archive/2017/20170711_orcas.js create mode 100644 migrations/archive/2017/20170731_naming_day.js create mode 100644 migrations/archive/2017/20170928_redesign_guilds.js rename migrations/{ => archive/2017}/20170928_redesign_launch.js (50%) create mode 100644 migrations/archive/2017/20171030_jackolanterns.js rename migrations/{ => archive/2017}/20171117_turkey_ladder.js (57%) create mode 100644 migrations/archive/2017/20171230_nye_hats.js rename migrations/{ => archive/2018}/20180110_nextPaymentProcessing.js (50%) rename migrations/{ => archive/2018}/20180125_clean_new_notifications.js (100%) rename migrations/{ => archive/2018}/20180125_notifications.js (100%) rename migrations/{ => archive/2018}/20180130_habit_birthday.js (51%) create mode 100644 migrations/archive/README.md create mode 100644 migrations/archive/api_v3/challenges.js create mode 100644 migrations/archive/api_v3/challengesMembers.js create mode 100644 migrations/archive/api_v3/coupons.js create mode 100644 migrations/archive/api_v3/emailUnsubscriptions.js create mode 100644 migrations/archive/api_v3/groups.js rename migrations/{ => archive}/api_v3/indexes.js (100%) create mode 100644 migrations/archive/api_v3/users.js rename migrations/{ => archive}/manual_password_reset.js (54%) create mode 100644 migrations/archive/metrics.js create mode 100644 migrations/command-line/.eslintrc create mode 100644 migrations/command-line/apology_gems.js rename migrations/{ => command-line}/cancelSubscription.js (77%) create mode 100644 migrations/command-line/contribs_plan.js rename migrations/{ => command-line}/current_period_end.js (52%) rename migrations/{ => command-line}/duplicatedTasksFindAndRemove.js (75%) rename migrations/{ => command-line}/facebook_to_local.js (51%) rename migrations/{ => command-line}/find_unique_user.js (74%) rename migrations/{ => command-line}/freeMonth.js (65%) create mode 100644 migrations/command-line/habitica_day.js create mode 100644 migrations/command-line/missing_gems.js rename migrations/{ => command-line}/mystery_items.js (51%) delete mode 100644 migrations/contribs_plan.js delete mode 100644 migrations/habitica_day.js delete mode 100644 migrations/metrics.js delete mode 100644 migrations/missing_gems.js delete mode 100644 test/common/libs/taskClasses.test.js delete mode 100644 website/common/script/libs/taskClasses.js diff --git a/.eslintignore b/.eslintignore index 7b618cca64..d2db8508cf 100644 --- a/.eslintignore +++ b/.eslintignore @@ -10,12 +10,5 @@ apidoc_build/ content_cache/ node_modules/ -# 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 +# Old migrations, disabled +migrations/archive/* \ No newline at end of file diff --git a/migrations/.eslintrc b/migrations/.eslintrc new file mode 100644 index 0000000000..6509e9ee6b --- /dev/null +++ b/migrations/.eslintrc @@ -0,0 +1,7 @@ +{ + "root": false, + "rules": { + "no-console": 0, + "no-use-before-define": ["error", { "functions": false }] + } +} diff --git a/migrations/20130128_add_missing_crons.js b/migrations/20130128_add_missing_crons.js deleted file mode 100644 index 1a4c919cc2..0000000000 --- a/migrations/20130128_add_missing_crons.js +++ /dev/null @@ -1,5 +0,0 @@ -db.users.update( - { lastCron: { $exists: false} }, - { $set: { lastCron: +new Date } }, - { multi: true } -); \ No newline at end of file diff --git a/migrations/20130128_merge_completed_todo_ids.js b/migrations/20130128_merge_completed_todo_ids.js deleted file mode 100644 index d362a4b771..0000000000 --- a/migrations/20130128_merge_completed_todo_ids.js +++ /dev/null @@ -1,15 +0,0 @@ -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 } - } - ); -}); \ No newline at end of file diff --git a/migrations/20130129_add_missing_preferences.js b/migrations/20130129_add_missing_preferences.js deleted file mode 100644 index f4e7b55762..0000000000 --- a/migrations/20130129_add_missing_preferences.js +++ /dev/null @@ -1,5 +0,0 @@ -db.users.update( - {preferences:{$exists:false}}, - {$set:{preferences:{gender: 'm', armorSet: 'v1'}}}, - {multi:true} -) diff --git a/migrations/20130204_user_public_private_paths.js b/migrations/20130204_user_public_private_paths.js deleted file mode 100644 index 7f43148350..0000000000 --- a/migrations/20130204_user_public_private_paths.js +++ /dev/null @@ -1,102 +0,0 @@ -// %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); - } -}) \ No newline at end of file diff --git a/migrations/20130208_idLists_to_typeIds.js b/migrations/20130208_idLists_to_typeIds.js deleted file mode 100644 index f22dc25be7..0000000000 --- a/migrations/20130208_idLists_to_typeIds.js +++ /dev/null @@ -1,19 +0,0 @@ -// 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 - } - ) -}) \ No newline at end of file diff --git a/migrations/20130208_user_customizations.js b/migrations/20130208_user_customizations.js deleted file mode 100644 index 07b1dfb08f..0000000000 --- a/migrations/20130208_user_customizations.js +++ /dev/null @@ -1,20 +0,0 @@ -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}); - -}) \ No newline at end of file diff --git a/migrations/20130307_exp_overflow.js b/migrations/20130307_exp_overflow.js deleted file mode 100644 index 44fb2904b4..0000000000 --- a/migrations/20130307_exp_overflow.js +++ /dev/null @@ -1,39 +0,0 @@ -// 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); - } - - } - -}) \ No newline at end of file diff --git a/migrations/20130307_normalize_algo_values.js b/migrations/20130307_normalize_algo_values.js deleted file mode 100644 index e42faded43..0000000000 --- a/migrations/20130307_normalize_algo_values.js +++ /dev/null @@ -1,47 +0,0 @@ -// 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); - } -}) \ No newline at end of file diff --git a/migrations/20130307_remove_duff_histories.js b/migrations/20130307_remove_duff_histories.js deleted file mode 100644 index 6211693a1f..0000000000 --- a/migrations/20130307_remove_duff_histories.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * 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); - } -}) \ No newline at end of file diff --git a/migrations/20130326_migrate_pets.js b/migrations/20130326_migrate_pets.js deleted file mode 100644 index 2e0f0a395b..0000000000 --- a/migrations/20130326_migrate_pets.js +++ /dev/null @@ -1,98 +0,0 @@ -/** - * 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); - } -}) \ No newline at end of file diff --git a/migrations/20130327_apply_tokens.js b/migrations/20130327_apply_tokens.js deleted file mode 100644 index e277db38fd..0000000000 --- a/migrations/20130327_apply_tokens.js +++ /dev/null @@ -1,110 +0,0 @@ -/** - * 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); - } - } - }) - -}) \ No newline at end of file diff --git a/migrations/20130503_max_gear_achievement.js b/migrations/20130503_max_gear_achievement.js deleted file mode 100644 index d6be25558a..0000000000 --- a/migrations/20130503_max_gear_achievement.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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); - } - } -}) \ No newline at end of file diff --git a/migrations/20130508_add_backer_pets.js b/migrations/20130508_add_backer_pets.js deleted file mode 100644 index 2a9744af2a..0000000000 --- a/migrations/20130508_add_backer_pets.js +++ /dev/null @@ -1 +0,0 @@ -db.users.update({'backer.tier':{$gte:80}}, {$push:{'items.pets':'Wolf-Cerberus'}}, {multi:true}); \ No newline at end of file diff --git a/migrations/20130602_survey_rewards.js b/migrations/20130602_survey_rewards.js deleted file mode 100644 index 98ae8106c2..0000000000 --- a/migrations/20130602_survey_rewards.js +++ /dev/null @@ -1,31 +0,0 @@ -//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} -) \ No newline at end of file diff --git a/migrations/20130612_survey_rewards_individual.js b/migrations/20130612_survey_rewards_individual.js deleted file mode 100644 index 1d3fbf3317..0000000000 --- a/migrations/20130612_survey_rewards_individual.js +++ /dev/null @@ -1,9 +0,0 @@ -//mongo habitrpg migrations/20130612_survey_rewards_individual.js - -var query = {_id: ""}; - -db.users.update(query, - { - $set: { 'achievements.helpedHabit': true }, - $inc: { balance: 2.5 } - }) \ No newline at end of file diff --git a/migrations/20130615_add_extra_indexes.js b/migrations/20130615_add_extra_indexes.js deleted file mode 100644 index 2673568184..0000000000 --- a/migrations/20130615_add_extra_indexes.js +++ /dev/null @@ -1,4 +0,0 @@ -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} ) \ No newline at end of file diff --git a/migrations/20130908_cleanup_corrupt_tags.js b/migrations/20130908_cleanup_corrupt_tags.js deleted file mode 100644 index 2dce073bbc..0000000000 --- a/migrations/20130908_cleanup_corrupt_tags.js +++ /dev/null @@ -1,16 +0,0 @@ -//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); - } -}) diff --git a/migrations/20131022_purchased_and_newStuff.js b/migrations/20131022_purchased_and_newStuff.js deleted file mode 100644 index 862d9e107f..0000000000 --- a/migrations/20131022_purchased_and_newStuff.js +++ /dev/null @@ -1,5 +0,0 @@ -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}}); -}); \ No newline at end of file diff --git a/migrations/20131022_restore_ads.js b/migrations/20131022_restore_ads.js deleted file mode 100644 index 6261990664..0000000000 --- a/migrations/20131022_restore_ads.js +++ /dev/null @@ -1,12 +0,0 @@ -// 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!"); -}); \ No newline at end of file diff --git a/migrations/20131102_restore_task_ids.js b/migrations/20131102_restore_task_ids.js deleted file mode 100644 index 260675fbf5..0000000000 --- a/migrations/20131102_restore_task_ids.js +++ /dev/null @@ -1,25 +0,0 @@ -// 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); - } -}); - diff --git a/migrations/20131104_remove_invalid_dues.js b/migrations/20131104_remove_invalid_dues.js deleted file mode 100644 index 8ebf89afaf..0000000000 --- a/migrations/20131104_remove_invalid_dues.js +++ /dev/null @@ -1,7 +0,0 @@ -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}}); -}); \ No newline at end of file diff --git a/migrations/20131105_remove_history_ids.js b/migrations/20131105_remove_history_ids.js deleted file mode 100644 index 9ddbff620e..0000000000 --- a/migrations/20131105_remove_history_ids.js +++ /dev/null @@ -1,21 +0,0 @@ -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}}); -}); \ No newline at end of file diff --git a/migrations/20131108_add_gems_for_contribs.js b/migrations/20131108_add_gems_for_contribs.js deleted file mode 100644 index 03ff44e721..0000000000 --- a/migrations/20131108_add_gems_for_contribs.js +++ /dev/null @@ -1,4 +0,0 @@ -// 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)} }); -}); \ No newline at end of file diff --git a/migrations/20131111_task_NaN.js b/migrations/20131111_task_NaN.js deleted file mode 100644 index 16e6788003..0000000000 --- a/migrations/20131111_task_NaN.js +++ /dev/null @@ -1,15 +0,0 @@ -// 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}}); -}); \ No newline at end of file diff --git a/migrations/20131114_migrate_websites_to_blurb.js b/migrations/20131114_migrate_websites_to_blurb.js deleted file mode 100644 index e2bbdd9fcb..0000000000 --- a/migrations/20131114_migrate_websites_to_blurb.js +++ /dev/null @@ -1,14 +0,0 @@ -// 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} - }) -}) diff --git a/migrations/20131115_update_gear_preferences.js b/migrations/20131115_update_gear_preferences.js deleted file mode 100644 index 9ec1264b2f..0000000000 --- a/migrations/20131115_update_gear_preferences.js +++ /dev/null @@ -1,10 +0,0 @@ -//Add defaults to show gears in all users -db.users.update( - {}, - {$set:{ - 'preferences.showWeapon': true, - 'preferences.showShield': true, - 'preferences.showArmor': true, - }}, - {multi:true} -) diff --git a/migrations/20131117_fix_task_types.js b/migrations/20131117_fix_task_types.js deleted file mode 100644 index 14d86cb55f..0000000000 --- a/migrations/20131117_fix_task_types.js +++ /dev/null @@ -1,18 +0,0 @@ -// 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}}); -}); diff --git a/migrations/20131117_remove_undefined_pets.js b/migrations/20131117_remove_undefined_pets.js deleted file mode 100644 index f908621a75..0000000000 --- a/migrations/20131117_remove_undefined_pets.js +++ /dev/null @@ -1,12 +0,0 @@ -// 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} }); -}); diff --git a/migrations/20131122_deleted_tags.js b/migrations/20131122_deleted_tags.js deleted file mode 100644 index e915390041..0000000000 --- a/migrations/20131122_deleted_tags.js +++ /dev/null @@ -1,13 +0,0 @@ -// 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); -}); \ No newline at end of file diff --git a/migrations/20131123_set_default_party_order.js b/migrations/20131123_set_default_party_order.js deleted file mode 100644 index 970e37e05b..0000000000 --- a/migrations/20131123_set_default_party_order.js +++ /dev/null @@ -1,8 +0,0 @@ -//Add default to randomize party members list -db.users.update( - {}, - {$set:{ - 'party.order': 'random', - }}, - {multi:true} -) diff --git a/migrations/20131126_clean_dayStart.js b/migrations/20131126_clean_dayStart.js deleted file mode 100644 index b2001ec7c2..0000000000 --- a/migrations/20131126_clean_dayStart.js +++ /dev/null @@ -1,5 +0,0 @@ -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}}); -}); diff --git a/migrations/20131126_turkey_pet.js b/migrations/20131126_turkey_pet.js deleted file mode 100644 index 568e38b237..0000000000 --- a/migrations/20131126_turkey_pet.js +++ /dev/null @@ -1 +0,0 @@ -db.users.update({},{$set:{'items.pets.Turkey-Base':5, 'flags.newStuff':true}}, {multi:true}); \ No newline at end of file diff --git a/migrations/20131127_restore_dayStart.js b/migrations/20131127_restore_dayStart.js deleted file mode 100644 index fd9aba3618..0000000000 --- a/migrations/20131127_restore_dayStart.js +++ /dev/null @@ -1,38 +0,0 @@ -// 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!"); - }) - }); -}); \ No newline at end of file diff --git a/migrations/20131221_restore_NaN_history.js b/migrations/20131221_restore_NaN_history.js deleted file mode 100644 index f0d6d90093..0000000000 --- a/migrations/20131221_restore_NaN_history.js +++ /dev/null @@ -1,51 +0,0 @@ -// 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'); - }) -}); \ No newline at end of file diff --git a/migrations/20131225_restore_streaks.js b/migrations/20131225_restore_streaks.js deleted file mode 100644 index c07a3828f3..0000000000 --- a/migrations/20131225_restore_streaks.js +++ /dev/null @@ -1,38 +0,0 @@ -// 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'); - }) -}); \ No newline at end of file diff --git a/migrations/20140119_task_creation_completion_dates.js b/migrations/20140119_task_creation_completion_dates.js deleted file mode 100644 index 9176b744ac..0000000000 --- a/migrations/20140119_task_creation_completion_dates.js +++ /dev/null @@ -1,8 +0,0 @@ -db.users.find({},{todos:1,dailys:1,rewards:1,habits:1}).forEach(function(user){ - _.each(user.habits.concat(user.dailys).concat(user.todos).concat(user.rewards), function(t){ - t.dateCreated = t.created || new Date; - delete t.created; - if (t.type == 'todo' && t.completed) t.dateCompleted = new Date; - }) - db.users.update({_id:user._id}, {$set:{habits:user.habits,dailys:user.dailys,todos:user.todos,rewards:user.rewards}}); -}); diff --git a/migrations/20140130_birthdayEnd.js b/migrations/20140130_birthdayEnd.js deleted file mode 100644 index a9a8eb93bc..0000000000 --- a/migrations/20140130_birthdayEnd.js +++ /dev/null @@ -1 +0,0 @@ -db.users.update({},{$set:{'achievements.habitBirthday':true}},{multi:1}) diff --git a/migrations/20140130_birthdayStart.js b/migrations/20140130_birthdayStart.js deleted file mode 100644 index f022168bcd..0000000000 --- a/migrations/20140130_birthdayStart.js +++ /dev/null @@ -1,12 +0,0 @@ -db.users.update({},{$set:{ - 'items.food.Cake_Skeleton':1, - 'items.food.Cake_Base':1, - 'items.food.Cake_CottonCandyBlue':1, - 'items.food.Cake_CottonCandyPink':1, - 'items.food.Cake_Shade':1, - 'items.food.Cake_White':1, - 'items.food.Cake_Golden':1, - 'items.food.Cake_Zombie':1, - 'items.food.Cake_Desert':1, - 'items.food.Cake_Red':1 -}},{multi:1}) diff --git a/migrations/20140220_challenge_memberCount.js b/migrations/20140220_challenge_memberCount.js deleted file mode 100644 index 1b83408943..0000000000 --- a/migrations/20140220_challenge_memberCount.js +++ /dev/null @@ -1,3 +0,0 @@ -db.challenges.find({},{members:1}).forEach(function(chal){ - db.challenges.update({_id:chal._id}, {$set:{memberCount:chal.members.length}}); -}); diff --git a/migrations/20140301_missing_mysteries.js b/migrations/20140301_missing_mysteries.js deleted file mode 100644 index a244e3f3db..0000000000 --- a/migrations/20140301_missing_mysteries.js +++ /dev/null @@ -1,14 +0,0 @@ -db.users.update( - { - 'purchased.plan.dateCreated':{$gte:new Date('2014-02-22'),$lt:new Date('2014-02-29')}, - 'items.gear.owned.armor_mystery_201402':null, - 'items.gear.owned.head_mystery_201402': null, - 'items.gear.owned.back_mystery_201402': null, - 'purchased.plan.mysteryItems':{$nin:['armor_mystery_201402','head_mystery_201402','back_mystery_201402']} - }, - //{_id:1,'purchased.plan':1,'items.gear.owned':1} - {$push: {'purchased.plan.mysteryItems':{$each:['armor_mystery_201402','head_mystery_201402','back_mystery_201402']}}}, - {multi:true} -)/*.forEach(function(user){ - printjson(user); - });*/ diff --git a/migrations/20140610_missing_backer_mount.js b/migrations/20140610_missing_backer_mount.js deleted file mode 100644 index 5dd28af9da..0000000000 --- a/migrations/20140610_missing_backer_mount.js +++ /dev/null @@ -1 +0,0 @@ -db.users.update({'backer.tier':{$gt:69}},{$set:{'items.mounts.LionCub-Ethereal':true}},{multi:1}) \ No newline at end of file diff --git a/migrations/20140712_wiped_quest_membership.js b/migrations/20140712_wiped_quest_membership.js deleted file mode 100644 index f8985e7c77..0000000000 --- a/migrations/20140712_wiped_quest_membership.js +++ /dev/null @@ -1,11 +0,0 @@ -//mongo habitrpg node_modules/lodash/lodash.js ./migrations/20140712_wiped_quest_membership.js -db.groups.find({type:'party','quest.key':{$ne:null},'quest.active':true},{quest:1}).forEach(function(group){ - var activeMembers = _.reduce(group.quest.members, function(m,v,k){ - if (v===true) m.push(k); return m; - },[]); - db.users.update( - {_id:{$in: activeMembers}}, - {$set:{'party.quest.key':group.quest.key,'party.quest.completed':null}}, - {multi:true} - ); -}); diff --git a/migrations/20140803_remove_undefined_notifications.js b/migrations/20140803_remove_undefined_notifications.js deleted file mode 100644 index fe0d143a56..0000000000 --- a/migrations/20140803_remove_undefined_notifications.js +++ /dev/null @@ -1,13 +0,0 @@ -var _ = require('lodash'); - -db.users.find({}).forEach(function(user){ - var newNewMessages = {}; - - _.each(user.newMessages, function(val, key){ - if(key != "undefined"){ - newNewMessages[key] = val; - }; - }); - - db.users.update({_id: user._id}, {$set: {'newMessages': newNewMessages}}); -}); diff --git a/migrations/20140829_change_headAccessory_to_eyewear.js b/migrations/20140829_change_headAccessory_to_eyewear.js deleted file mode 100644 index 9940fb5013..0000000000 --- a/migrations/20140829_change_headAccessory_to_eyewear.js +++ /dev/null @@ -1,81 +0,0 @@ -// node .migrations/20140829_change_headAccessory_to_eyewear.js - -var migrationName = '20140829_change_headAccessory_to_eyewear'; -var authorName = 'Alys'; // in case script author needs to know when their ... -var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done - -// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. -// We've now upgraded to lodash v4 but the code used in this migration has not been -// adapted to work with it. Before this migration is used again any lodash method should -// be checked for compatibility against the v4 changelog and changed if necessary. -// https://github.com/lodash/lodash/wiki/Changelog#v400 - -/** - * https://github.com/HabitRPG/habitrpg/issues/3645 - */ -var mongo = require('mongoskin'); -var _ = require('lodash'); -var liveUsers = mongo.db('localhost:27017/habitrpg2?auto_reconnect').collection('users'); - -var fields = {'migration':1, - 'items.gear.costume.headAccessory':1, - 'items.gear.equipped.headAccessory':1, - 'items.gear.owned.headAccessory_special_wondercon_black':1, - 'items.gear.owned.headAccessory_special_wondercon_red':1, - 'items.gear.owned.headAccessory_special_summerRogue':1, - 'items.gear.owned.headAccessory_special_summerWarrior':1 - }; - -var progressCount = 1000; -var count = 0; -liveUsers.findEach({ $and: [ - { migration: {$ne:migrationName} }, - { $or: [ - {'items.gear.owned.headAccessory_special_summerRogue': {'$exists':true}}, - {'items.gear.owned.headAccessory_special_summerWarrior':{'$exists':true}}, - {'items.gear.owned.headAccessory_special_wondercon_red':{'$exists':true}}, - {'items.gear.owned.headAccessory_special_wondercon_black':{'$exists':true}} - ]} -]}, fields, {batchSize:250}, function(err, user){ - count++; - if (!user) err = '!user'; - if (err) {return console.error(err);} - - var set = {'migration': migrationName}; - var unset = {}; - - var oldToNew = { - 'headAccessory_special_summerRogue': 'eyewear_special_summerRogue', - 'headAccessory_special_summerWarrior': 'eyewear_special_summerWarrior', - 'headAccessory_special_wondercon_red': 'eyewear_special_wondercon_red', - 'headAccessory_special_wondercon_black':'eyewear_special_wondercon_black' - }; - - // items.gear.costume, items.gear.equipped: - _.each(['costume','equipped'],function(type){ - _.each(oldToNew,function(newName,oldName){ - if (user.items.gear[type].headAccessory === oldName) { - unset['items.gear.'+type+'.headAccessory'] = ""; - set['items.gear.'+type+'.eyewear'] = newName; - } - }); - }); - - // items.gear.owned: - _.each(oldToNew,function(newName,oldName){ - if (oldName in user.items.gear.owned) { - unset['items.gear.owned.'+oldName] = ""; - set['items.gear.owned.'+newName] = user.items.gear.owned[oldName]; - } - }); - - //console.log(JSON.stringify(user, null, " ")); - //console.log("set: " + JSON.stringify(set, null, " ")); - //console.log("unset: " + JSON.stringify(unset, null, " ")); - - liveUsers.update({_id:user._id}, {$set:set, $unset:unset, $inc:{_v:1}}); - - if (count%progressCount == 0) console.log(count + ' ' + user._id); - if (user._id == '9') console.log('lefnire processed'); - if (user._id == authorUuid) console.log(authorName + ' processed'); -}); diff --git a/migrations/20140831_increase_gems_for_previous_contributions.js b/migrations/20140831_increase_gems_for_previous_contributions.js deleted file mode 100644 index 164dda444b..0000000000 --- a/migrations/20140831_increase_gems_for_previous_contributions.js +++ /dev/null @@ -1,131 +0,0 @@ -// IMPORTANT: -// -// run like this to capture all output: -// -// node 20140831_increase_gems_for_previous_contributions.js > 20140831_increase_gems_for_previous_contributions_output.txt - -// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. -// We've now upgraded to lodash v4 but the code used in this migration has not been -// adapted to work with it. Before this migration is used again any lodash method should -// be checked for compatibility against the v4 changelog and changed if necessary. -// https://github.com/lodash/lodash/wiki/Changelog#v400 - -var migrationName = '20140831_increase_gems_for_previous_contributions'; - -/** - * https://github.com/HabitRPG/habitrpg/issues/3933 - * Increase Number of Gems for Contributors - * author: Alys (d904bd62-da08-416b-a816-ba797c9ee265) - * - * Increase everyone's gems per their contribution level. - * Originally they were given 2 gems per tier. - * Now they are given 3 gems per tier for tiers 1,2,3 - * and 4 gems per tier for tiers 4,5,6,7 - * So that means an EXTRA 1 for tier 1, - * 2 for tier 2, - * 3 for tier 3, - * 5 for tier 4, - * 7 for tier 5, - * 9 for tier 6, - * 11 for tier 7, - * 11 for tier 8 (moderators = tier 7 + admin privileges), - * none for tier 9 (staff) - */ - -var mongo = require('mongoskin'); -var _ = require('lodash'); - - -var dbUsers = mongo.db('localhost:27017/habitrpg?auto_reconnect').collection('users'); - - -var query = { - 'contributor.level': {$gt: 0, $lt: 9}, - 'migration': {$ne: migrationName} -}; - -var fields = { - 'migration':1, - 'contributor.level':1, - 'balance':1 -}; - -var userResults = {}; // each key is a UUID, each value is a string - // describing what changed for that user - -console.warn('Updating users...'); -var progressCount = 50; -var count = 0; -dbUsers.findEach(query, fields, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All users found. Fetching final balances...'); - return fetchFinalBalances(); - } - count++; - - var set = {'migration': migrationName}; - - var tier = user.contributor.level; - var extraGems = tier; // tiers 1,2,3 - if (tier > 3) { extraGems = 3 + (tier - 3) * 2; } - if (tier == 8) { extraGems = 11; } - var extraBalance = extraGems / 4; - set['balance'] = user.balance + extraBalance; - - // Capture current state of user: - userResults[user._id] = - user._id + ' ' + ':\n' + - ' contrib tier : ' + tier + '\n' + - ' balance before : ' + user.balance + '\n' + - ' balance (gems) added : ' + extraBalance + ' (' + - extraGems + ')' + '\n' + - ' expected balance after: ' + (user.balance + extraBalance) + '\n'; - - // Update user: - dbUsers.update({_id:user._id}, {$set:set, $inc:{_v:1}}); - if (count%progressCount == 0) console.warn(count + ' ' + user._id); -}); - - -function fetchFinalBalances() { - var query = {_id: {$in: Object.keys(userResults)}}; - var fields = { - 'balance':1, - }; - - var count1 = 0; - dbUsers.findEach(query, fields, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All final balances found.'); - return displayData(); - } - count1++; - userResults[user._id] = userResults[user._id] + - user._id + ' ' + ':\n' + - ' actual balance after : ' + user.balance + '\n'; - if (count1%progressCount == 0) console.warn(count1 + ' ' + user._id); - }); -} - - -function displayData() { - _.each(userResults, function(text, uuid) { - console.log(text); // text contains uuid - }); - console.log('\n' + count + - ' users processed (should be roughly 335 according to the Hall)\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} diff --git a/migrations/20140914_upgrade_admin_contrib_tiers.js b/migrations/20140914_upgrade_admin_contrib_tiers.js deleted file mode 100644 index 7962b7dfde..0000000000 --- a/migrations/20140914_upgrade_admin_contrib_tiers.js +++ /dev/null @@ -1,79 +0,0 @@ -var migrationName = '20140914_upgrade_admin_contrib_tiers'; -var authorName = 'Alys'; // in case script author needs to know when their ... -var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done - -/** - * https://github.com/HabitRPG/habitrpg/issues/3801 - * Convert Tier 8 contributors to Tier 9 (staff) (all current Tier 8s are admins). - * Convert Tier 7 contributors with admin flag to Tier 8 (moderators). - */ - -// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. -// We've now upgraded to lodash v4 but the code used in this migration has not been -// adapted to work with it. Before this migration is used again any lodash method should -// be checked for compatibility against the v4 changelog and changed if necessary. -// https://github.com/lodash/lodash/wiki/Changelog#v400 - -var mongo = require('mongoskin'); -var _ = require('lodash'); - -var dbUsers = mongo.db('localhost:27017/habitrpg?auto_reconnect').collection('users'); - -var query = - { 'contributor.level':{$gte:7}, 'contributor.admin':true, 'migration': {$ne: migrationName} }; - -var fields = {'migration':1, - 'contributor.admin':1, - 'contributor.level':1, - 'auth.local.username':1, - 'profile.name':1, - }; - -var userResults = {}; // each key is a UUID, each value is a username; - // contains only the users changed - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - return displayData(); - } - count++; - - var set = {'migration': migrationName}; - var inc = {'contributor.level':1, _v:1}; - - userResults[user._id] = user.profile.name; - - dbUsers.update({_id:user._id}, {$set:set, $inc:inc}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); - if (user._id == '9' ) console.warn('lefnire' + ' processed'); -}); - - -function displayData() { - console.log('users modified:'); - _.each(userResults, function(name, uuid) { - console.log(name); - }); - console.warn('\n' + count + - ' users processed (should be 11 according to the Hall)\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - diff --git a/migrations/20140922_free_candy.js b/migrations/20140922_free_candy.js deleted file mode 100644 index 77cc221462..0000000000 --- a/migrations/20140922_free_candy.js +++ /dev/null @@ -1,18 +0,0 @@ -db.users.update( - {}, - { - $inc: { - 'items.food.Candy_Base':1, - 'items.food.Candy_CottonCandyBlue':1, - 'items.food.Candy_CottonCandyPink':1, - 'items.food.Candy_Desert':1, - 'items.food.Candy_Golden':1, - 'items.food.Candy_Red':1, - 'items.food.Candy_Shade':1, - 'items.food.Candy_Skeleton':1, - 'items.food.Candy_White':1, - 'items.food.Candy_Zombie':1 - } - }, - {multi:1} -); \ No newline at end of file diff --git a/migrations/20141006_jackolantern_pet.js b/migrations/20141006_jackolantern_pet.js deleted file mode 100644 index f09c38d9f3..0000000000 --- a/migrations/20141006_jackolantern_pet.js +++ /dev/null @@ -1 +0,0 @@ -db.users.update({_id:'9'},{$set:{'items.pets.JackOLantern-Base':5, 'flags.newStuff':true}}, {multi:true}); \ No newline at end of file diff --git a/migrations/20141126_turkey_mounts.js b/migrations/20141126_turkey_mounts.js deleted file mode 100644 index 1270390c06..0000000000 --- a/migrations/20141126_turkey_mounts.js +++ /dev/null @@ -1,11 +0,0 @@ -db.users.update( - {'items.pets.Turkey-Base':{$ne:null}}, - {$set:{'items.mounts.Turkey-Base':true}}, - {multi:1} -) - -db.users.update( - {'items.pets.Turkey-Base':null}, - {$set:{'items.pets.Turkey-Base':5}}, - {multi:1} -) \ No newline at end of file diff --git a/migrations/20141211_NaN_consecutives.js b/migrations/20141211_NaN_consecutives.js deleted file mode 100644 index 2822df0267..0000000000 --- a/migrations/20141211_NaN_consecutives.js +++ /dev/null @@ -1,4 +0,0 @@ -db.users.update({'purchased.plan.consecutive.count':NaN}, {$set:{'purchased.plan.consecutive.count':0}}, {multi:1}); -db.users.update({'purchased.plan.consecutive.offset':NaN}, {$set:{'purchased.plan.consecutive.offset':0}}, {multi:1}); -db.users.update({'purchased.plan.consecutive.gemCapExtra':NaN}, {$set:{'purchased.plan.consecutive.gemCapExtra':0}}, {multi:1}); -db.users.update({'purchased.plan.consecutive.trinkets':NaN}, {$set:{'purchased.plan.consecutive.trinkets':0}}, {multi:1}); \ No newline at end of file diff --git a/migrations/20141230_new_years_hats.js b/migrations/20141230_new_years_hats.js deleted file mode 100644 index 595027be0a..0000000000 --- a/migrations/20141230_new_years_hats.js +++ /dev/null @@ -1,11 +0,0 @@ -db.users.update( - {'items.gear.owned.head_special_nye':{$ne:null}}, - {$set:{'items.gear.owned.head_special_nye2014':false}}, - {multi:1} -) - -db.users.update( - {'items.gear.owned.head_special_nye':null}, - {$set:{'items.gear.owned.head_special_nye':false}}, - {multi:1} -) diff --git a/migrations/20150107_plan_dateUpdated_null.js b/migrations/20150107_plan_dateUpdated_null.js deleted file mode 100644 index 6e03b41a55..0000000000 --- a/migrations/20150107_plan_dateUpdated_null.js +++ /dev/null @@ -1,8 +0,0 @@ -db.users.update( - {'purchased.plan.customerId':{$ne:null}, 'purchased.plan.dateUpdated':null}, - { - $set: {'purchased.plan.dateUpdated': new Date('12/01/2014')}, - $unset: {'purchased.plan.datedUpdated':''} - }, - {multi:true} -); diff --git a/migrations/20150124_mountmaster_fix.js b/migrations/20150124_mountmaster_fix.js deleted file mode 100644 index 874faea22c..0000000000 --- a/migrations/20150124_mountmaster_fix.js +++ /dev/null @@ -1,88 +0,0 @@ -var migrationName = '20150124_mountmaster_fix.js'; -var authorName = 'Alys'; // in case script author needs to know when their ... -var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done - -/** - * https://github.com/HabitRPG/habitrpg/pull/4374#issuecomment-71038795 - * Convert false to null for mounts that used to be owned. - */ - -var dbserver = 'localhost:27017' // CHANGE THIS FOR PRODUCTION DATABASE - -// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. -// We've now upgraded to lodash v4 but the code used in this migration has not been -// adapted to work with it. Before this migration is used again any lodash method should -// be checked for compatibility against the v4 changelog and changed if necessary. -// https://github.com/lodash/lodash/wiki/Changelog#v400 - -var mongo = require('mongoskin'); -var _ = require('lodash'); - -var dbUsers = mongo.db(dbserver + '/habitrpg?auto_reconnect').collection('users'); - -var query = { - 'items.mounts':{$exists:true} - }; - -var fields = { - 'items.mounts':1 - }; - -var animals = [ "Wolf-Base", "Wolf-White", "Wolf-Desert", "Wolf-Red", "Wolf-Shade", "Wolf-Skeleton", "Wolf-Zombie", "Wolf-CottonCandyPink", "Wolf-CottonCandyBlue", "Wolf-Golden", "TigerCub-Base", "TigerCub-White", "TigerCub-Desert", "TigerCub-Red", "TigerCub-Shade", "TigerCub-Skeleton", "TigerCub-Zombie", "TigerCub-CottonCandyPink", "TigerCub-CottonCandyBlue", "TigerCub-Golden", "PandaCub-Base", "PandaCub-White", "PandaCub-Desert", "PandaCub-Red", "PandaCub-Shade", "PandaCub-Skeleton", "PandaCub-Zombie", "PandaCub-CottonCandyPink", "PandaCub-CottonCandyBlue", "PandaCub-Golden", "LionCub-Base", "LionCub-White", "LionCub-Desert", "LionCub-Red", "LionCub-Shade", "LionCub-Skeleton", "LionCub-Zombie", "LionCub-CottonCandyPink", "LionCub-CottonCandyBlue", "LionCub-Golden", "Fox-Base", "Fox-White", "Fox-Desert", "Fox-Red", "Fox-Shade", "Fox-Skeleton", "Fox-Zombie", "Fox-CottonCandyPink", "Fox-CottonCandyBlue", "Fox-Golden", "FlyingPig-Base", "FlyingPig-White", "FlyingPig-Desert", "FlyingPig-Red", "FlyingPig-Shade", "FlyingPig-Skeleton", "FlyingPig-Zombie", "FlyingPig-CottonCandyPink", "FlyingPig-CottonCandyBlue", "FlyingPig-Golden", "Dragon-Base", "Dragon-White", "Dragon-Desert", "Dragon-Red", "Dragon-Shade", "Dragon-Skeleton", "Dragon-Zombie", "Dragon-CottonCandyPink", "Dragon-CottonCandyBlue", "Dragon-Golden", "Cactus-Base", "Cactus-White", "Cactus-Desert", "Cactus-Red", "Cactus-Shade", "Cactus-Skeleton", "Cactus-Zombie", "Cactus-CottonCandyPink", "Cactus-CottonCandyBlue", "Cactus-Golden", "BearCub-Base", "BearCub-White", "BearCub-Desert", "BearCub-Red", "BearCub-Shade", "BearCub-Skeleton", "BearCub-Zombie", "BearCub-CottonCandyPink", "BearCub-CottonCandyBlue", "BearCub-Golden" ]; // all Gen1 mounts - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - return displayData(); - } - count++; - - var mounts = user.items.mounts; - var changed = false; - for(var a in animals) { - if(mounts[animals[a]] == false) { - mounts[animals[a]] = null; - changed = true; - } - } - - if (changed) { - dbUsers.update( - { _id: user._id}, - { - $set: { "migration": migrationName, - "items.mounts" : mounts - } - } - ); - } - - // var set = {'migration': migrationName}; - // var inc = {'xyz':1, _v:1}; - // dbUsers.update({_id:user._id}, {$set:set, $inc:inc}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); - if (user._id == '9' ) console.warn('lefnire' + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} diff --git a/migrations/20150130_birthday_goodies.js b/migrations/20150130_birthday_goodies.js deleted file mode 100644 index 6c7c2e08c5..0000000000 --- a/migrations/20150130_birthday_goodies.js +++ /dev/null @@ -1,36 +0,0 @@ -db.users.update( - {'items.gear.owned.armor_special_birthday':{$ne:null}}, - {$set:{'items.gear.owned.armor_special_birthday2015':false}}, - {multi:1} -) - -db.users.update( - {'items.gear.owned.armor_special_birthday':null}, - {$set:{'items.gear.owned.armor_special_birthday':false}}, - {multi:1} -) - -db.users.update({},{$inc:{ - 'items.food.Cake_Skeleton':1, - 'items.food.Cake_Base':1, - 'items.food.Cake_CottonCandyBlue':1, - 'items.food.Cake_CottonCandyPink':1, - 'items.food.Cake_Shade':1, - 'items.food.Cake_White':1, - 'items.food.Cake_Golden':1, - 'items.food.Cake_Zombie':1, - 'items.food.Cake_Desert':1, - 'items.food.Cake_Red':1 -}},{multi:1}) - -db.users.update( - {'achievements.habitBirthday':true}, - {$set:{'achievements.habitBirthdays':1}}, - {multi:1} -) - -db.users.update( - {}, - {$inc:{'achievements.habitBirthdays':1}}, - {multi:1} -) \ No newline at end of file diff --git a/migrations/20150131_birthday_goodies_fix_remove_robe.js b/migrations/20150131_birthday_goodies_fix_remove_robe.js deleted file mode 100644 index 54b39030ba..0000000000 --- a/migrations/20150131_birthday_goodies_fix_remove_robe.js +++ /dev/null @@ -1,78 +0,0 @@ -var migrationName = '20150131_birthday_goodies_fix__one_birthday__1'; -var authorName = 'Alys'; // in case script author needs to know when their ... -var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done - -/* - * remove new birthday robes and second achievement from people who shouldn't have them - */ - -var dbserver = 'localhost:27017' // CHANGE THIS FOR PRODUCTION DATABASE - -// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. -// We've now upgraded to lodash v4 but the code used in this migration has not been -// adapted to work with it. Before this migration is used again any lodash method should -// be checked for compatibility against the v4 changelog and changed if necessary. -// https://github.com/lodash/lodash/wiki/Changelog#v400 - -var mongo = require('mongoskin'); -var _ = require('lodash'); - -var dbUsers = mongo.db(dbserver + '/habitrpg?auto_reconnect').collection('users'); - - // 'auth.timestamps.created':{$gt:new Date('2014-02-01')}, -var query = { - 'achievements.habitBirthdays':1, - 'auth.timestamps.loggedin':{$gt:new Date('2014-12-20')} - }; - - // '_id': 'c03e41bd-501f-438c-9553-a7afdf52a08c', - // 'achievements.habitBirthday':{$exists:false}, - // 'items.gear.owned.armor_special_birthday2015':1 - -var fields = { - // 'auth.timestamps.created':1, - // 'achievements.habitBirthday':1, - // 'achievements.habitBirthdays':1, - 'items.gear.owned.armor_special_birthday2015':1, - // 'items.gear.owned.armor_special':1 - }; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - return displayData(); - } - count++; - - var unset = {'items.gear.owned.armor_special_birthday2015': 1}; - // var set = {'migration':migrationName, 'achievements.habitBirthdays':1 }; - // var inc = {'xyz':1, _v:1}; - dbUsers.update({_id:user._id}, {$unset:unset}); // , $inc:inc}); - // dbUsers.update({_id:user._id}, {$unset:unset, $set:set}); - // console.warn(user.auth.timestamps.created); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); - if (user._id == '9' ) console.warn('lefnire' + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} diff --git a/migrations/20150201_convert_creation_date_from_string_to_object.js b/migrations/20150201_convert_creation_date_from_string_to_object.js deleted file mode 100644 index 72a31e8020..0000000000 --- a/migrations/20150201_convert_creation_date_from_string_to_object.js +++ /dev/null @@ -1,112 +0,0 @@ -var migrationName = '20150201_convert_creation_date_from_string_to_object__no_date_recent_signup'; -//// var migrationName = '20150201_convert_creation_date_from_string_to_object'; - -var authorName = 'Alys'; // in case script author needs to know when their ... -var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done - -/* - * For users that have no value for auth.timestamps.created, assign them - * a recent value. - * - * NOTE: - * Before this script was used as described above, it was first used to - * find all users that have a auth.timestamps.created field that is a string - * rather than a date object and set it to be a date object. The code used - * for this has been commented out with four slashes: //// - * - * https://github.com/HabitRPG/habitrpg/issues/4601#issuecomment-72339846 - */ - -var dbserver = 'localhost:27017' // CHANGE THIS FOR PRODUCTION DATABASE - -// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. -// We've now upgraded to lodash v4 but the code used in this migration has not been -// adapted to work with it. Before this migration is used again any lodash method should -// be checked for compatibility against the v4 changelog and changed if necessary. -// https://github.com/lodash/lodash/wiki/Changelog#v400 - -var mongo = require('mongoskin'); -var _ = require('lodash'); -var moment = require('moment'); - -var dbUsers = mongo.db(dbserver + '/habitrpg?auto_reconnect').collection('users'); - -var uuidArrayRecent=[ // recent users with no creation dates -'1a0d4b75-73ed-4937-974d-d504d6398884', -'1c7ebe27-1250-4f95-ba10-965580adbfd7', -'5f972121-4a6d-411c-95e9-7093d3e89b66', -'ae85818a-e336-4ccd-945e-c15cef975102', -'ba273976-d9fc-466c-975f-38559d34a824', -]; - -var query = { - '_id':{$in: uuidArrayRecent} - //// 'auth':{$exists:true}, - //// 'auth.timestamps':{$exists:true}, - //// 'auth.timestamps.created':{$not: {$lt:new Date('2018-01-01')}} - }; - -var fields = { - '_id':1, - 'auth.timestamps.created':1 - }; - // 'achievements.habitBirthdays':1 - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - return displayData(); - } - count++; - - //// var oldDate = user.auth.timestamps.created; - //// var newDate = moment(oldDate).toDate(); - var oldDate = 'none'; - var newDate = moment('2015-01-11').toDate(); - console.warn(user._id + ' == ' + oldDate + ' == ' + newDate); - - //// var set = { 'migration': migrationName, - //// 'auth.timestamps.created': newDate, - //// 'achievements.habitBirthdays': 2, - //// 'items.gear.owned.head_special_nye':true, - //// 'items.gear.owned.head_special_nye2014':true, - //// 'items.gear.owned.armor_special_birthday':true, - //// 'items.gear.owned.armor_special_birthday2015':true, - //// }; - - var set = { 'migration': migrationName, - 'auth.timestamps.created': newDate, - 'achievements.habitBirthdays': 1, - 'items.gear.owned.armor_special_birthday':true, - }; - - // var unset = {'items.gear.owned.armor_special_birthday2015': 1}; - // var inc = {'xyz':1, _v:1}; - dbUsers.update({_id:user._id}, {$set:set}); - // dbUsers.update({_id:user._id}, {$unset:unset, $set:set, $inc:inc}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); - if (user._id == '9' ) console.warn('lefnire' + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} diff --git a/migrations/20150201_recapture_emails_phase_update.js b/migrations/20150201_recapture_emails_phase_update.js deleted file mode 100644 index 51fd73a6fa..0000000000 --- a/migrations/20150201_recapture_emails_phase_update.js +++ /dev/null @@ -1,7 +0,0 @@ -db.users.update({ - 'flags.recaptureEmailsPhase': { - $gt: 0 - } -},{$inc:{ - 'flags.recaptureEmailsPhase':1 -}},{multi:1}) \ No newline at end of file diff --git a/migrations/20150218_interactive_tour.js b/migrations/20150218_interactive_tour.js deleted file mode 100644 index 91769742ed..0000000000 --- a/migrations/20150218_interactive_tour.js +++ /dev/null @@ -1,10 +0,0 @@ -db.users.update({},{$set:{ - 'flags.tour.intro':-2, - //'flags.tour.classes':-2, - 'flags.tour.stats':-2, - 'flags.tour.tavern':-2, - 'flags.tour.party':-2, - 'flags.tour.guilds':-2, - 'flags.tour.challenges':-2, - 'flags.tour.market':-2 -}},{multi:1}) \ No newline at end of file diff --git a/migrations/20150224_force_resting_in_inn.js b/migrations/20150224_force_resting_in_inn.js deleted file mode 100644 index 1b704fb411..0000000000 --- a/migrations/20150224_force_resting_in_inn.js +++ /dev/null @@ -1,64 +0,0 @@ -var migrationName = '20150224_force_resting_in_inn'; -var authorName = 'Alys'; // in case script author needs to know when their ... -var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done - -/* - * force all active players to rest in the inn due to massive server fail - */ - -// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. -// We've now upgraded to lodash v4 but the code used in this migration has not been -// adapted to work with it. Before this migration is used again any lodash method should -// be checked for compatibility against the v4 changelog and changed if necessary. -// https://github.com/lodash/lodash/wiki/Changelog#v400 - -var dbserver = 'localhost:27017' // CHANGE THIS FOR PRODUCTION DATABASE - -var mongo = require('mongoskin'); -var _ = require('lodash'); - -var dbUsers = mongo.db(dbserver + '/habitrpg?auto_reconnect').collection('users'); - -var query = { - 'auth.timestamps.loggedin':{$gt:new Date('2015-02-22')} - }; - -var fields = { - 'preferences.sleep':1, - }; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - return displayData(); - } - count++; - - var set = {'migration':migrationName, 'preferences.sleep':1 }; - dbUsers.update({_id:user._id}, {$set:set}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); - if (user._id == '9' ) console.warn('lefnire' + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} diff --git a/migrations/20150310_survey_achievements.js b/migrations/20150310_survey_achievements.js deleted file mode 100644 index 9678b6c3c4..0000000000 --- a/migrations/20150310_survey_achievements.js +++ /dev/null @@ -1,5 +0,0 @@ -db.users.update( - {'achievements.helpedHabit':true}, - {$set:{'achievements.habitSurveys':1}}, - {multi:1} -) \ No newline at end of file diff --git a/migrations/20150604_ultimateGearSets.js b/migrations/20150604_ultimateGearSets.js deleted file mode 100644 index 64ff0142d3..0000000000 --- a/migrations/20150604_ultimateGearSets.js +++ /dev/null @@ -1,138 +0,0 @@ -// var migrationName = '20150604_ultimateGearSets'; -// var authorName = 'Sabe'; // in case script author needs to know when their ... -// var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -var migrationName = '20150620_ultimateGearSets'; -var authorName = 'Alys'; // in case script author needs to know when their ... -var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done - -/* - * grant the new ultimateGearSets achievement for existing users' collected equipment - * - * - * Changed by Alys on 20150620 to assign false values to - * 'achievements.ultimateGearSets' when true values are not appropriate, - * because of https://github.com/HabitRPG/habitrpg/issues/5427 - * - * Minimal changes were made so the code isn't as efficient or clean - * as it could be, but it's (hopefully) one-use-only and minimal changes - * means minimal new testing. - */ - -// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. -// We've now upgraded to lodash v4 but the code used in this migration has not been -// adapted to work with it. Before this migration is used again any lodash method should -// be checked for compatibility against the v4 changelog and changed if necessary. -// https://github.com/lodash/lodash/wiki/Changelog#v400 - -var dbserver = 'localhost:27017' // FOR TEST DATABASE -// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379' // FOR PRODUCTION DATABASE -var dbname = 'habitrpg'; - -var mongo = require('mongoskin'); -var _ = require('lodash'); - -var dbUsers = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('users'); - -var fields = { - 'achievements.ultimateGearSets':1, - 'items.gear.owned':1 -}; - - -// Changes 20150620: All users have to be processed now (non-achievers need -// false values). -var query = { -}; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - return displayData(); - } - count++; - - var achievements = {}; - var changeUser = false; - // Changes 20150620: 'changeUser' now indicates that the user must have the - // Enchanted Armoire unlocked. - if ( (typeof user.items.gear.owned.weapon_wizard_6 !== 'undefined') - && (typeof user.items.gear.owned.armor_wizard_5 !== 'undefined') - && (typeof user.items.gear.owned.head_wizard_5 !== 'undefined') - ) { - achievements['wizard'] = true; - changeUser = true; - } - else { - // Changes 20150620: false added for all classes (here and below) - achievements['wizard'] = false; - } - - if ( (typeof user.items.gear.owned.weapon_warrior_6 !== 'undefined') - && (typeof user.items.gear.owned.armor_warrior_5 !== 'undefined') - && (typeof user.items.gear.owned.head_warrior_5 !== 'undefined') - && (typeof user.items.gear.owned.shield_warrior_5 !== 'undefined') - ) { - achievements['warrior'] = true; - changeUser = true; - } - else { - achievements['warrior'] = false; - } - - if ( (typeof user.items.gear.owned.weapon_healer_6 !== 'undefined') - && (typeof user.items.gear.owned.armor_healer_5 !== 'undefined') - && (typeof user.items.gear.owned.head_healer_5 !== 'undefined') - && (typeof user.items.gear.owned.shield_healer_5 !== 'undefined') - ) { - achievements['healer'] = true; - changeUser = true; - } - else { - achievements['healer'] = false; - } - - if ( (typeof user.items.gear.owned.weapon_rogue_6 !== 'undefined') - && (typeof user.items.gear.owned.armor_rogue_5 !== 'undefined') - && (typeof user.items.gear.owned.head_rogue_5 !== 'undefined') - && (typeof user.items.gear.owned.shield_rogue_6 !== 'undefined') - ) { - achievements['rogue'] = true; - changeUser = true; - } - else { - achievements['rogue'] = false; - } - - // Changes 20150620: $set is now run for all users. - var set = {'migration':migrationName, 'achievements.ultimateGearSets':achievements}; - if (changeUser) { // user has at least one Ultimate Gear achievement - set['flags.armoireEnabled'] = true; - } - dbUsers.update({_id:user._id}, {$set:set}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); - if (user._id == '9' ) console.warn('lefnire' + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} diff --git a/migrations/20150731_purple_gryphon.js b/migrations/20150731_purple_gryphon.js deleted file mode 100644 index 8e44361ae0..0000000000 --- a/migrations/20150731_purple_gryphon.js +++ /dev/null @@ -1,5 +0,0 @@ -db.users.update( - {}, - {$set:{'items.mounts.Gryphon-RoyalPurple':true}}, - {multi:true} -); diff --git a/migrations/20150731_veteran_tiger.js b/migrations/20150731_veteran_tiger.js deleted file mode 100644 index 55e46fb1af..0000000000 --- a/migrations/20150731_veteran_tiger.js +++ /dev/null @@ -1,5 +0,0 @@ -db.users.update( - {'items.pets.Wolf-Veteran':{$ne:null}}, - {$set:{'items.pets.Tiger-Veteran':5}}, - {multi:true} -); diff --git a/migrations/20150731_veteran_wolf.js b/migrations/20150731_veteran_wolf.js deleted file mode 100644 index 9d31d4591d..0000000000 --- a/migrations/20150731_veteran_wolf.js +++ /dev/null @@ -1,7 +0,0 @@ -// Run after the Veteran Tiger script, not before! - -db.users.update( - {'items.pets.Wolf-Veteran':{$exists:false}}, - {$set:{'items.pets.Wolf-Veteran':5}}, - {multi:true} -); diff --git a/migrations/20150906_groups_fix_leaders.js b/migrations/20150906_groups_fix_leaders.js deleted file mode 100644 index 0a1a226c91..0000000000 --- a/migrations/20150906_groups_fix_leaders.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Make sure leaders are existing users - */ - -var mongo = require('mongoskin'); -var async = require('async'); - -var dbserver = 'url'; -var dbname = 'dbname'; -var countGroups = 0; -var countUsers = 0; - -var db = mongo.db(dbserver + '/' + dbname + '?auto_reconnect'); -var dbUsers = db.collection('users'); -var dbGroups = db.collection('groups'); - -console.log('Begins work on db'); - -function findGroups(gt){ - var query = {}; - if(gt) query._id = {$gt: gt}; - - console.log(query) - - dbGroups.find(query, { - fields: {_id: 1, members: 1, leader: 1}, - limit: 10000, - sort: { - _id: 1 - } - }).toArray(function(err, groups){ - if(err) throw err; - - var lastGroup = null; - if(groups.length === 10000){ - lastGroup = groups[groups.length - 1]; - } - - async.eachLimit(groups, 30, function(group, cb1){ - countGroups++; - console.log('Group: ', countGroups, group._id); - - var members = group.members; - - dbUsers.findOne({_id: group.leader}, {fields: {_id: 1}}, function(err, user){ - if(err) return cb1(err); - - // If leader has deleted account - if(!user && (group._id !== 'habitrpg') && members && members[0]) { - dbGroups.update({ - _id: group._id - }, { - $set: { - // Set first user as new leader - leader: members[0] - } - }, { - multi: false - }, function(err, res){ - if(err) return cb1(err); - - console.log('Updated: ', res); - return cb1(); - }); - }else{ - return cb1(); - } - }); - }, function(err){ - if(err) throw err; - - if(lastGroup && lastGroup._id){ - findGroups(lastGroup._id); - } - }); - }); -}; - -findGroups(); \ No newline at end of file diff --git a/migrations/20150906_groups_remove_deleted_users.js b/migrations/20150906_groups_remove_deleted_users.js deleted file mode 100644 index 96146a8c0e..0000000000 --- a/migrations/20150906_groups_remove_deleted_users.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Remove deleted accounts from groups - */ - -var mongo = require('mongoskin'); -var async = require('async'); - -var dbserver = 'url'; -var dbname = 'dbname'; -var countGroups = 0; -var countUsers = 0; - -var db = mongo.db(dbserver + '/' + dbname + '?auto_reconnect'); -var dbUsers = db.collection('users'); -var dbGroups = db.collection('groups'); - -console.log('Begins work on db'); - -function findGroups(gt){ - var query = {}; - if(gt) query._id = {$gt: gt}; - - console.log(query) - - dbGroups.find(query, { - fields: {_id: 1, members: 1}, - limit: 10000, - sort: { - _id: 1 - } - }).toArray(function(err, groups){ - if(err) throw err; - - var lastGroup = null; - if(groups.length === 10000){ - lastGroup = groups[groups.length - 1]; - } - - async.eachLimit(groups, 3, function(group, cb1){ - countGroups++; - console.log('Group: ', countGroups, group._id); - - var members = group.members; - - // Remove users who deleted their account - async.eachLimit(members, 15, function(member, cb2){ - dbUsers.findOne({_id: member}, {fields: {_id: 1}}, function(err, user){ - if(err) return cb2(err); - - if(!user){ - countUsers++; - console.log('User removed n. ', countUsers, 'user id ', member, 'group id ', group._id); - - dbGroups.update({ - _id: group._id - }, { - $pull: {members: member}, - $inc: {memberCount: -1} - }, { - multi: false - }, function(err, res){ - if(err) return cb2(err); - - console.log('Updated: ', res); - return cb2(); - }); - }else{ - cb2(); - } - }); - }, function(err){ - if(err) return cb1(err); - - cb1(); - }); - - }, function(err){ - if(err) throw err; - - if(lastGroup && lastGroup._id){ - findGroups(lastGroup._id); - } - }); - }); -}; - -findGroups(); \ No newline at end of file diff --git a/migrations/20150906_groups_remove_empty.js b/migrations/20150906_groups_remove_empty.js deleted file mode 100644 index 44da0b600c..0000000000 --- a/migrations/20150906_groups_remove_empty.js +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Remove empty private groups - */ - -var mongo = require('mongoskin'); - -var dbserver = 'url'; -var dbname = 'name'; - -var db = mongo.db(dbserver + '/' + dbname + '?auto_reconnect'); -var dbGroups = db.collection('groups'); - -console.log('Begins work on db'); - -dbGroups.findEach({ - memberCount: 0, -}, {_id: 1}, function(err, res){ - if(err) throw err; - - console.log(res); -}); \ No newline at end of file diff --git a/migrations/20150906_sync_groups_with_firebase.js b/migrations/20150906_sync_groups_with_firebase.js deleted file mode 100644 index d7b4f12174..0000000000 --- a/migrations/20150906_sync_groups_with_firebase.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Sync groups with Firebase - */ - -var mongo = require('mongoskin'); -var Firebase = require('Firebase'); - -var dbserver = 'mongodb://url'; -var dbname = 'db'; - -var db = mongo.db(dbserver + '/' + dbname + '?auto_reconnect'); -var dbGroups = db.collection('groups'); -var countGroups = 0; - -var firebaseRef = new Firebase('https://' + 'firebase-app' + '.firebaseio.com'); - -// TODO handle sync errors with firebase? -firebaseRef.authWithCustomToken('firebase-secret', function(err, authData){ - if(err) throw new Error('Impossible to authenticate Firebase'); - - console.log('Firebase connected, begins work on db'); - - dbGroups.findEach({}, {_id: 1, members: 1}, {batchSize: 100}, function(err, group){ - if(err) throw err; - if(group._id !== 'habitrpg') return; - - countGroups++; - console.log('Group: ', countGroups); - - firebaseRef.child('rooms/' + group._id) - .set({ - name: group.name - }); - - group.members.forEach(function(member){ - firebaseRef.child('members/' + group._id + '/' + userId) - .set(true); - - firebaseRef.child('users/' + member + '/rooms/' + group._id) - .set(true); - }); - }); -}); \ No newline at end of file diff --git a/migrations/20151013_jackolanterns.js b/migrations/20151013_jackolanterns.js deleted file mode 100644 index 67ff0ac9a2..0000000000 --- a/migrations/20151013_jackolanterns.js +++ /dev/null @@ -1,67 +0,0 @@ -var migrationName = '20151013_jackolanterns.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award Jack-O'-Lantern mounts to users who already have the pet version, award pet if they don't - */ - -var dbserver = 'localhost:27017'; // FOR TEST DATABASE -// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE -var dbname = 'habitrpg'; - -var mongo = require('mongoskin'); -var _ = require('lodash'); - -var dbUsers = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('users'); - -// specify a query to limit the affected users (empty for all users): -var query = { -}; - -// specify fields we are interested in to limit retrieved data (empty if we're not reading data): -var fields = { - 'items.pets.JackOLantern-Base':1 -}; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - return displayData(); - } - count++; - - // specify user data to change: - var set = {}; - if (user.items.pets['JackOLantern-Base']) { - set = {'migration':migrationName, 'items.mounts.JackOLantern-Base':true}; - } else { - set = {'migration':migrationName, 'items.pets.JackOLantern-Base':5}; - } - - dbUsers.update({_id:user._id}, {$set:set}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} diff --git a/migrations/20151021_usernames_emails_lowercase.js b/migrations/20151021_usernames_emails_lowercase.js deleted file mode 100644 index b40aaad9d7..0000000000 --- a/migrations/20151021_usernames_emails_lowercase.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Migrate email to lowerCase version and add auth.local.lowerCaseUsername email - */ - -var mongo = require('mongoskin'); -var async = require('async'); - -var dbserver = 'url'; -var dbname = 'dbname'; -var countUsers = 0; - -var db = mongo.db(dbserver + '/' + dbname + '?auto_reconnect'); -var dbUsers = db.collection('users'); - -console.log('Begins work on db'); - -function findUsers(gt){ - var query = {}; - if(gt) query._id = {$gt: gt}; - - console.log(query) - - dbUsers.find(query, { - fields: {_id: 1, auth: 1}, - limit: 10000, - sort: { - _id: 1 - } - }).toArray(function(err, users){ - if(err) throw err; - - var lastUser = null; - if(users.length === 10000){ - lastUser = users[users.length - 1]; - } - - async.eachLimit(users, 20, function(user, cb){ - countUsers++; - console.log('User: ', countUsers, user._id); - - var update = { - $set: {} - }; - - if(user.auth && user.auth.local) { - if(user.auth.local.username) update['$set']['auth.local.lowerCaseUsername'] = user.auth.local.username.toLowerCase(); - if(user.auth.local.email) update['$set']['auth.local.email'] = user.auth.local.email.toLowerCase(); - } - - dbUsers.update({ - _id: user._id - }, update, cb); - }, function(err){ - if(err) throw err; - - if(lastUser && lastUser._id){ - findUsers(lastUser._id); - } - }); - }); -}; - -findUsers(); diff --git a/migrations/20151105_tutorial_flags.js b/migrations/20151105_tutorial_flags.js deleted file mode 100644 index 6af069965c..0000000000 --- a/migrations/20151105_tutorial_flags.js +++ /dev/null @@ -1,63 +0,0 @@ -var migrationName = '20151105_tutorial_flags_v1'; -var authorName = 'Alys'; // in case script author needs to know when their ... -var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done - -/* - * set flags.tutorial.ios and flags.tutorial.main flags to true in preparation - * for the release of a new iOS tutorial - * - */ - -// var dbserver = 'localhost:27017' // FOR TEST DATABASE -var dbserver = 'alys:@ds031379-a0.mongolab.com:31379' // FOR PRODUCTION DATABASE -var dbname = 'habitrpg'; - -var mongo = require('mongoskin'); -var _ = require('lodash'); - -var dbUsers = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('users'); - -var fields = { -}; - - -var query = { - 'auth.timestamps.loggedin':{$gt:new Date('2015-10-20')} -}; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - return displayData(); - } - count++; - - // var set = {'migration':migrationName, 'flags.tutorial.ios':true, 'flags.tutorial.main':true }; - var set = {'migration':migrationName, 'flags.tutorial.ios':{} }; - - dbUsers.update({_id:user._id}, {$set:set}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} diff --git a/migrations/20151116_costume_contest_award.js b/migrations/20151116_costume_contest_award.js deleted file mode 100644 index 25891f9215..0000000000 --- a/migrations/20151116_costume_contest_award.js +++ /dev/null @@ -1,102 +0,0 @@ -var migrationName = '20151116_costume_contest.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award Costume Contest achievement to 2015 winners - */ - -var dbserver = 'localhost:27017'; // FOR TEST DATABASE -// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE -var dbname = 'habitrpg'; - -var mongo = require('mongoskin'); -var _ = require('lodash'); - -var dbUsers = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('users'); - -// specify a query to limit the affected users (empty for all users): -var query = { - _id: { - $in: [ - 'e411dab3-a4ca-414d-bdbd-b6940b3bdeb3', - '35ced5cc-c33a-45c8-93dc-16000ee66fde', - 'ab3f0549-7247-4fd5-975b-efcff98c79c3', - 'b1261fd2-eb25-46b4-97a9-ae7a0dc8a131', - '1f27893f-3808-4724-9725-f46dab93faca', - '216a0c23-6afd-4a5e-b434-d386a10862a2', - '2d6ef231-50b4-4a22-90e7-45eb97147a2c', - '98b8cf4f-89bd-4b0a-988d-02629a217232', - 'c5183dfa-c741-43ce-935e-c6d89b41a030', - '262a7afb-6b57-4d81-88e0-80d2e9f6cbdc', - '33991e0a-de55-4986-ac81-af78491a84de', - '7adf6ada-3c05-4054-b5df-fa7d49d3b9eb', - '235a1cbd-48c5-41b1-afb4-59d2f8645c57', - 'b7617a61-188b-4332-bf4d-32268fa77f2b', - '672c1ce0-9f47-44f0-a3f3-8cc3c6c5a9cb', - 'd0a3217a-7b92-48d6-b39a-b1b1be96702e', - '5ef910dc-1d22-47d9-aa38-a60132c60679', - '370a44c8-e94a-4a2c-91f2-33166926db1f', - '1b0b3ef3-28bd-4046-a49b-e1c83e281baf', - '75b93321-66b9-49bd-9076-052499c1d2bf', - 'd97516e4-81d0-4f60-bf03-95f7330925ab', - '3e13cc79-de38-420d-822e-9e9da309ce6b', - '0e471dc1-ecb0-4388-a891-b873a237d2cf', - 'ca3da398-4f73-4304-b838-af3669ed4cbb', - '44cdf105-8bda-4197-9d1a-1bcb83b4dc84', - '5419830c-b837-4573-ae82-4718ab95b7f1', - 'ac6fbe37-b0dc-40d8-ba14-77dde66fbfa8', - '8789ba18-a498-46b9-b367-3b929a0acb94', - '52fce1a9-9b0a-4e26-95dc-adc12f52e752', - '21bf71ac-399c-470b-abe0-cc49a03b6a8b', - 'f1618ce2-552e-4f23-bc76-e73d63ebedd0', - '4cc0c749-d943-4090-b529-42bc665b7244', - 'e259682e-cb5c-4d94-b472-ceedc66d7484', - 'fa197a4b-e065-4551-803a-c8a5b9970f9d' - ] - } -}; - -// specify fields we are interested in to limit retrieved data (empty if we're not reading data): -var fields = { -}; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - return displayData(); - } - count++; - - // specify user data to change: - var set = {'migration':migrationName}; - var inc = {'achievements.costumeContests':1}; - - dbUsers.update({_id:user._id}, {$set:set}); - dbUsers.update({_id:user._id}, {$inc:inc}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - diff --git a/migrations/20151116_costume_contest_to_number.js b/migrations/20151116_costume_contest_to_number.js deleted file mode 100644 index 7121e9bc31..0000000000 --- a/migrations/20151116_costume_contest_to_number.js +++ /dev/null @@ -1,64 +0,0 @@ -var migrationName = '20151116_costume_contest_to_number.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Change Costume Contest achievement from Boolean to Number, so people can win repeatedly - */ - -var dbserver = 'localhost:27017'; // FOR TEST DATABASE -// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE -var dbname = 'habitrpg'; - -var mongo = require('mongoskin'); -var _ = require('lodash'); - -var dbUsers = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('users'); - -// specify a query to limit the affected users (empty for all users): -var query = { - 'achievements.costumeContest':true -}; - -// specify fields we are interested in to limit retrieved data (empty if we're not reading data): -var fields = { - 'achievements.costumeContest':1 -}; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - return displayData(); - } - count++; - - // specify user data to change: - var set = {'achievements.costumeContests':1}; - - dbUsers.update({_id:user._id}, {$set:set}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - diff --git a/migrations/20151125_turkey_ladder.js b/migrations/20151125_turkey_ladder.js deleted file mode 100644 index e028b4f882..0000000000 --- a/migrations/20151125_turkey_ladder.js +++ /dev/null @@ -1,71 +0,0 @@ -var migrationName = '20151125_turkey_ladder.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award Gilded Turkey pet to Turkey mount owners, Turkey Mount if they only have Turkey Pet, - * and Turkey Pet otherwise - */ - -var dbserver = 'localhost:27017'; // FOR TEST DATABASE -// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE -var dbname = 'habitrpg'; - -var mongo = require('mongoskin'); -var _ = require('lodash'); - -var dbUsers = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('users'); - -// specify a query to limit the affected users (empty for all users): -var query = { -}; - -// specify fields we are interested in to limit retrieved data (empty if we're not reading data): -var fields = { - 'items.pets.Turkey-Base': 1, - 'items.mounts.Turkey-Base': 1 -}; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - return displayData(); - } - count++; - - // specify user data to change: - var set = {}; - if (user.items.mounts['Turkey-Base']) { - set = {'migration':migrationName, 'items.pets.Turkey-Gilded':5}; - } else if (user.items.pets['Turkey-Base']) { - set = {'migration':migrationName, 'items.mounts.Turkey-Base':true}; - } else { - set = {'migration':migrationName, 'items.pets.Turkey-Base':5}; - } - - dbUsers.update({_id:user._id}, {$set:set}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} diff --git a/migrations/20151229_new_years_hats.js b/migrations/20151229_new_years_hats.js deleted file mode 100644 index 895e545dd0..0000000000 --- a/migrations/20151229_new_years_hats.js +++ /dev/null @@ -1,70 +0,0 @@ -var migrationName = '20151229_new_years_hats.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award 2015 party hat if user has 2014 hat, 2014 hat if they have the 2013 hat, - * and 2013 hat otherwise - */ - -var dbserver = 'localhost:27017'; // FOR TEST DATABASE -// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE -var dbname = 'habitrpg'; - -var mongo = require('mongoskin'); -var _ = require('lodash'); - -var dbUsers = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('users'); - -// specify a query to limit the affected users (empty for all users): -var query = { -}; - -// specify fields we are interested in to limit retrieved data (empty if we're not reading data): -var fields = { - 'items.gear.owned': 1, -}; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - return displayData(); - } - count++; - - // specify user data to change: - var set = {}; - if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('head_special_nye2014')) { - set = {'migration':migrationName, 'items.gear.owned.head_special_nye2015':false}; - } else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('head_special_nye')) { - set = {'migration':migrationName, 'items.gear.owned.head_special_nye2014':false}; - } else { - set = {'migration':migrationName, 'items.gear.owned.head_special_nye':false}; - } - - dbUsers.update({_id:user._id}, {$set:set}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} diff --git a/migrations/20160521_veteran_ladder.js b/migrations/20160521_veteran_ladder.js deleted file mode 100644 index 0cc19e4514..0000000000 --- a/migrations/20160521_veteran_ladder.js +++ /dev/null @@ -1,82 +0,0 @@ -var migrationName = '20160521_veteran_ladder.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award Gilded Turkey pet to Turkey mount owners, Turkey Mount if they only have Turkey Pet, - * and Turkey Pet otherwise - */ - -var dbserver = 'localhost:27017'; // FOR TEST DATABASE -// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE -var dbname = 'habitrpg'; - -var mongo = require('mongoskin'); -var _ = require('lodash'); - -// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. -// We've now upgraded to lodash v4 but the code used in this migration has not been -// adapted to work with it. Before this migration is used again any lodash method should -// be checked for compatibility against the v4 changelog and changed if necessary. -// https://github.com/lodash/lodash/wiki/Changelog#v400 - -var dbUsers = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('users'); - -// specify a query to limit the affected users (empty for all users): -var query = { - 'auth.timestamps.loggedin':{$gt:new Date('2016-05-01')} // remove when running migration a second time -}; - -// specify fields we are interested in to limit retrieved data (empty if we're not reading data): -var fields = { - 'migration': 1, - 'items.pets.Wolf-Veteran': 1, - 'items.pets.Tiger-Veteran': 1 -}; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - return displayData(); - } - count++; - - // specify user data to change: - var set = {}; - if (user.migration !== migrationName) { - if (user.items.pets['Tiger-Veteran']) { - set = {'migration':migrationName, 'items.pets.Lion-Veteran':5}; - } else if (user.items.pets['Wolf-Veteran']) { - set = {'migration':migrationName, 'items.pets.Tiger-Veteran':5}; - } else { - set = {'migration':migrationName, 'items.pets.Wolf-Veteran':5}; - } - } - - dbUsers.update({_id:user._id}, {$set:set}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - diff --git a/migrations/20160527_fix_empty_checklist_id.js b/migrations/20160527_fix_empty_checklist_id.js deleted file mode 100644 index 0276d3241e..0000000000 --- a/migrations/20160527_fix_empty_checklist_id.js +++ /dev/null @@ -1,84 +0,0 @@ -var uuid = require('uuid').v4; -var mongo = require('mongodb').MongoClient; -var _ = require('lodash'); - -// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. -// We've now upgraded to lodash v4 but the code used in this migration has not been -// adapted to work with it. Before this migration is used again any lodash method should -// be checked for compatibility against the v4 changelog and changed if necessary. -// https://github.com/lodash/lodash/wiki/Changelog#v400 - -var taskIds = require('checklists-no-id.json').map(function (obj) { - return obj._id; -}); - -// Fix empty task.checklistt.id - -var progressCount = 100; -var count = 0; - -function displayData() { - console.warn('\n' + count + ' tasks processed\n'); - return exiting(0); -} - -function exiting(code, msg) { - code = code || 0; // 0 = success - - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } -} - -mongo.connect('db url') -.then(function (db) { - var dbTasks = db.collection('tasks'); - - // specify a query to limit the affected tasks (empty for all tasks): - var query = { - '_id':{ $in: taskIds }, - }; - - // specify fields we are interested in to limit retrieved data (empty if we're not reading data): - var fields = { - 'checklist': 1, - }; - - console.warn('Updating tasks...'); - - dbTasks.find(query, fields, {batchSize: 250}).toArray(function(err, tasks) { - if (err) { return exiting(1, 'ERROR! ' + err); } - - tasks.forEach(function (task) { - var checklist = task.checklist || []; - checklist.forEach(function (item) { - if (!item.id || item.id === "") { - item.id = uuid(); - } - }); - - // specify user data to change: - var set = { - checklist: checklist, - }; - //console.log(set); - - dbTasks.update({_id: task._id}, {$set: set}, function (err, res) { - if (err) console.error('Error while updating', err); - }); - - count++; - if (count % progressCount == 0) console.warn(count + ' ' + task._id); - }); - - if (count === tasks.length) { - console.warn('All appropriate tasks found and modified.'); - return displayData(); - } - }); -}) -.catch(function (err) { - throw err; -}); \ No newline at end of file diff --git a/migrations/20160731_naming_day.js b/migrations/20160731_naming_day.js deleted file mode 100644 index 601798d14c..0000000000 --- a/migrations/20160731_naming_day.js +++ /dev/null @@ -1,82 +0,0 @@ -var migrationName = '20160731_naming_day.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award Royal Purple Gryphon pet to Royal Purple Gryphon mount owners, mount to everyone else - */ - -var mongo = require('mongoskin'); - -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE - -var dbUsers = mongo.db(connectionString).collection('users'); - -// specify a query to limit the affected users (empty for all users): -var query = { - 'migration':{$ne:migrationName}, - 'auth.timestamps.loggedin':{$gt:new Date('2016-07-30')} // Extend timeframe each run of migration -}; - -// specify fields we are interested in to limit retrieved data (empty if we're not reading data): -var fields = { - 'items.mounts': 1 -}; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - setTimeout(displayData, 300000); - return; - } - count++; - - // specify user data to change: - var set = {}; - var inc = {}; - inc = { - 'achievements.habiticaDays': 1, - 'items.food.Cake_Skeleton': 1, - 'items.food.Cake_Base': 1, - 'items.food.Cake_CottonCandyBlue': 1, - 'items.food.Cake_CottonCandyPink': 1, - 'items.food.Cake_Shade': 1, - 'items.food.Cake_White': 1, - 'items.food.Cake_Golden': 1, - 'items.food.Cake_Zombie': 1, - 'items.food.Cake_Desert': 1, - 'items.food.Cake_Red': 1 - }; - if (user.items.mounts['Gryphon-RoyalPurple']) { - set = {'migration':migrationName, 'items.pets.Gryphon-RoyalPurple':5}; - } else { - set = {'migration':migrationName, 'items.mounts.Gryphon-RoyalPurple':true}; - } - - dbUsers.update({_id:user._id}, {$set:set, $inc:inc}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - diff --git a/migrations/20160731_takeThis.js b/migrations/20160731_takeThis.js deleted file mode 100644 index b08700a4bd..0000000000 --- a/migrations/20160731_takeThis.js +++ /dev/null @@ -1,71 +0,0 @@ -var migrationName = '20160731_takeThis.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award Take This Sword to Take This challenge participants who already own the Shield - * and Take This Shield to the rest of the list - */ - -var mongo = require('mongoskin'); - -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE - -var dbUsers = mongo.db(connectionString).collection('users'); - -// specify a query to limit the affected users (empty for all users): -var query = { - 'migration':{$ne:migrationName}, - 'auth.timestamps.loggedin':{$gt:new Date('2016-07-30')}, // Extend timeframe each run of migration - 'challenges':{$in:['da8859b2-5c6e-4aa5-b8b2-8db93d5de9fc']} -}; - -// specify fields we are interested in to limit retrieved data (empty if we're not reading data): -var fields = { - 'items.gear.owned': 1 -}; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - setTimeout(displayData, 300000); - return; - } - count++; - - // specify user data to change: - var set = {}; - - if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false}; - } else { - set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false}; - } - - dbUsers.update({_id:user._id}, {$set:set}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - diff --git a/migrations/20160831_takeThis.js b/migrations/20160831_takeThis.js deleted file mode 100644 index 853ade4044..0000000000 --- a/migrations/20160831_takeThis.js +++ /dev/null @@ -1,72 +0,0 @@ -var migrationName = '20160831_takeThis.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award Take This Sword to Take This challenge participants who already own the Shield - * and Take This Shield to the rest of the list - */ - -var mongo = require('mongoskin'); - -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE - -var dbUsers = mongo.db(connectionString).collection('users'); - -// specify a query to limit the affected users (empty for all users): -var query = { - 'migration':{$ne:migrationName}, - 'challenges':{$in:['ee2b3c87-13f0-422a-af3c-309102d4f7e6']} -}; - -// specify fields we are interested in to limit retrieved data (empty if we're not reading data): -var fields = { - 'items.gear.owned': 1 -}; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - setTimeout(displayData, 300000); - return; - } - count++; - - // specify user data to change: - var set = {}; - - if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false}; - } else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false}; - } else { - set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false}; - } - - dbUsers.update({_id:user._id}, {$set:set}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - diff --git a/migrations/20161002_takeThis.js b/migrations/20161002_takeThis.js deleted file mode 100644 index a3dafa3042..0000000000 --- a/migrations/20161002_takeThis.js +++ /dev/null @@ -1,73 +0,0 @@ -var migrationName = '20161002_takeThis.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award Take This ladder items to participants in this month's challenge - */ - -var mongo = require('mongoskin'); - -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE - -var dbUsers = mongo.db(connectionString).collection('users'); - -// specify a query to limit the affected users (empty for all users): -var query = { - 'migration':{$ne:migrationName}, - 'challenges':{$in:['4bbf63b5-10bc-49f9-8e95-5bd2ac99cd1c']} -}; - -// specify fields we are interested in to limit retrieved data (empty if we're not reading data): -var fields = { - 'items.gear.owned': 1 -}; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - setTimeout(displayData, 300000); - return; - } - count++; - - // specify user data to change: - var set = {}; - - if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false}; - } else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false}; - } else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false}; - } else { - set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false}; - } - - dbUsers.update({_id:user._id}, {$set:set}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - diff --git a/migrations/20161030-jackolanterns.js b/migrations/20161030-jackolanterns.js deleted file mode 100644 index bbea4d3455..0000000000 --- a/migrations/20161030-jackolanterns.js +++ /dev/null @@ -1,86 +0,0 @@ -var migrationName = '20161030-jackolanterns.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * set the newStuff flag in all user accounts so they see a Bailey message - */ - -var mongo = require('mongoskin'); - -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE - -var dbUsers = mongo.db(connectionString).collection('users'); - -// specify a query to limit the affected users (empty for all users): -var query = { - 'auth.timestamps.loggedin':{$gt:new Date('2016-10-01')} // remove when running migration a second time -}; - -// specify fields we are interested in to limit retrieved data (empty if we're not reading data): -var fields = { - 'migration': 1, - 'items.pets.JackOLantern-Base': 1, - 'items.mounts.JackOLantern-Base': 1, -}; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - setTimeout(displayData, 300000); - return; - } - count++; - - // specify user data to change: - var set = {}; - var inc = {}; - if (user.migration !== migrationName) { - if (user.items.mounts['JackOLantern-Base']) { - set = {'migration':migrationName, 'items.pets.JackOLantern-Ghost':5}; - } else if (user.items.pets['JackOLantern-Base']) { - set = {'migration':migrationName, 'items.mounts.JackOLantern-Base':true}; - } else { - set = {'migration':migrationName, 'items.pets.JackOLantern-Base':5}; - } - inc = { - 'items.food.Candy_Base': 1, - 'items.food.Candy_CottonCandyBlue': 1, - 'items.food.Candy_CottonCandyPink': 1, - 'items.food.Candy_Desert': 1, - 'items.food.Candy_Golden': 1, - 'items.food.Candy_Red': 1, - 'items.food.Candy_Shade': 1, - 'items.food.Candy_Skeleton': 1, - 'items.food.Candy_White': 1, - 'items.food.Candy_Zombie': 1, - } - } - - dbUsers.update({_id:user._id}, {$set:set, $inc:inc}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - diff --git a/migrations/20161102_takeThis.js b/migrations/20161102_takeThis.js deleted file mode 100644 index d13d180ccf..0000000000 --- a/migrations/20161102_takeThis.js +++ /dev/null @@ -1,75 +0,0 @@ -var migrationName = '20161102_takeThis.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award Take This ladder items to participants in this month's challenge - */ - -var mongo = require('mongoskin'); - -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE - -var dbUsers = mongo.db(connectionString).collection('users'); - -// specify a query to limit the affected users (empty for all users): -var query = { - 'migration':{$ne:migrationName}, - 'challenges':{$in:['d1be0965-e909-4d30-82fa-9a0011f885b2']} -}; - -// specify fields we are interested in to limit retrieved data (empty if we're not reading data): -var fields = { - 'items.gear.owned': 1 -}; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - setTimeout(displayData, 300000); - return; - } - count++; - - // specify user data to change: - var set = {}; - - if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.body_special_takeThis':false}; - } else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false}; - } else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false}; - } else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false}; - } else { - set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false}; - } - - dbUsers.update({_id:user._id}, {$set:set}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - diff --git a/migrations/20161122_turkey_ladder.js b/migrations/20161122_turkey_ladder.js deleted file mode 100644 index 4992ed272f..0000000000 --- a/migrations/20161122_turkey_ladder.js +++ /dev/null @@ -1,74 +0,0 @@ -var migrationName = '20161122_turkey_ladder.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Yearly Turkey Day award. Turkey pet, Turkey mount, Gilded Turkey pet, Gilded Turkey mount - */ - -var mongo = require('mongoskin'); - -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE - -var dbUsers = mongo.db(connectionString).collection('users'); - -// specify a query to limit the affected users (empty for all users): -var query = { - 'migration':{$ne:migrationName}, - 'auth.timestamps.loggedin':{$gt:new Date('2016-10-31')} // Extend timeframe each run of migration -}; - -// specify fields we are interested in to limit retrieved data (empty if we're not reading data): -var fields = { - 'migration': 1, - 'items.mounts': 1, - 'items.pets': 1, -}; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - setTimeout(displayData, 300000); - return; - } - count++; - - // specify user data to change: - var set = {}; - - if (user.items.pets['Turkey-Gilded']) { - set = {'migration':migrationName, 'items.mounts.Turkey-Gilded':true}; - } else if (user.items.mounts['Turkey-Base']) { - set = {'migration':migrationName, 'items.pets.Turkey-Gilded':5}; - } else if (user.items.pets['Turkey-Base']) { - set = {'migration':migrationName, 'items.mounts.Turkey-Base':true}; - } else { - set = {'migration':migrationName, 'items.pets.Turkey-Base':5}; - } - - dbUsers.update({_id:user._id}, {$set:set}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} diff --git a/migrations/20161230_nye_hats.js b/migrations/20161230_nye_hats.js deleted file mode 100644 index 7eeb8dd7da..0000000000 --- a/migrations/20161230_nye_hats.js +++ /dev/null @@ -1,73 +0,0 @@ -var migrationName = '20161230_nye_hats.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Yearly New Year's party hat award - */ - -var mongo = require('mongoskin'); - -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE - -var dbUsers = mongo.db(connectionString).collection('users'); - -// specify a query to limit the affected users (empty for all users): -var query = { - 'migration':{$ne:migrationName}, - 'auth.timestamps.loggedin':{$gt:new Date('2016-11-30')} // Remove after first run -}; - -// specify fields we are interested in to limit retrieved data (empty if we're not reading data): -var fields = { - 'items.gear.owned': 1, -}; - -console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } - if (!user) { - console.warn('All appropriate users found and modified.'); - setTimeout(displayData, 300000); - return; - } - count++; - - // specify user data to change: - var set = {}; - - if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.head_special_nye2016':false}; - } else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.head_special_nye2015':false}; - } else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.head_special_nye2014':false}; - } else { - set = {'migration':migrationName, 'items.gear.owned.head_special_nye':false}; - } - - dbUsers.update({_id:user._id}, {$set:set}); - - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -}); - - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - diff --git a/migrations/20170120_missing_incentive.js b/migrations/20170120_missing_incentive.js deleted file mode 100644 index 8ee3c82c1b..0000000000 --- a/migrations/20170120_missing_incentive.js +++ /dev/null @@ -1,113 +0,0 @@ -var migrationName = '20170120_missing_incentive.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award missing Royal Purple Hatching Potion to users with 55+ check-ins - * Reduce users with impossible check-in counts to a reasonable number - */ - -import monk from 'monk'; -import common from '../website/common'; - -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbUsers = monk(connectionString).get('users', { castIds: false }); - -function processUsers(lastId) { - // specify a query to limit the affected users (empty for all users): - var query = { - 'loginIncentives': {$gt:54}, - 'migration': {$ne: migrationName}, - }; - - if (lastId) { - query._id = { - $gt: lastId - } - } - - dbUsers.find(query, { - sort: {_id: 1}, - limit: 250, - fields: [] // specify fields we are interested in to limit retrieved data (empty if we're not reading data): - }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); -} - -var progressCount = 1000; -var count = 0; - -function updateUsers (users) { - if (!users || users.length === 0) { - console.warn('All appropriate users found and modified.'); - displayData(); - return; - } - - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; - - return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); -} - -function updateUser (user) { - count++; - - var language = user.preferences.language || 'en'; - var set = {'migration': migrationName}; - var inc = {'items.hatchingPotions.RoyalPurple': 1}; - if (user.loginIncentives > 58) { - set = {'migration': migrationName, 'loginIncentives': 58}; - } - var push = { - 'notifications': { - 'type': 'LOGIN_INCENTIVE', - 'data': { - 'nextRewardAt': 60, - 'rewardKey': [ - 'Pet_HatchingPotion_Purple', - ], - 'rewardText': common.i18n.t('potion', {potionType: common.i18n.t('hatchingPotionRoyalPurple', language)}, language), - 'reward': [ - { - 'premium': true, - 'key': 'RoyalPurple', - 'limited': true, - 'value': 2, - } - ], - 'message': common.i18n.t('unlockedCheckInReward', language), - }, - 'id': common.uuid(), - } - }; - - dbUsers.update({_id: user._id}, {$set:set, $push:push, $inc:inc}); - - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -} - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - -module.exports = processUsers; diff --git a/migrations/20170131_habit_birthday.js b/migrations/20170131_habit_birthday.js deleted file mode 100644 index bb1571c71b..0000000000 --- a/migrations/20170131_habit_birthday.js +++ /dev/null @@ -1,109 +0,0 @@ -var migrationName = '20170131_habit_birthday.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award 2017 party robes if user has 2016 robes, 2016 robes if they have the 2015 robes, - * 2015 robes if they have the 2014 robes, and 2014 robes otherwise. Also cake! - */ - -var monk = require('monk'); -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbUsers = monk(connectionString).get('users', { castIds: false }); - -function processUsers(lastId) { - // specify a query to limit the affected users (empty for all users): - var query = { - 'migration':{$ne:migrationName}, - 'auth.timestamps.loggedin':{$gt:new Date('2017-01-24')}, // remove after first run to cover remaining users - }; - - if (lastId) { - query._id = { - $gt: lastId - } - } - - dbUsers.find(query, { - sort: {_id: 1}, - limit: 250, - fields: [ // specify fields we are interested in to limit retrieved data (empty if we're not reading data) - 'items.gear.owned' - ], - }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); -} - -var progressCount = 1000; -var count = 0; - -function updateUsers (users) { - if (!users || users.length === 0) { - console.warn('All appropriate users found and modified.'); - displayData(); - return; - } - - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; - - return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); -} - -function updateUser (user) { - count++; - - var set = {'migration':migrationName}; - if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2016')) { - set['items.gear.owned.armor_special_birthday2017'] = false; - } else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2015')) { - set['items.gear.owned.armor_special_birthday2016'] = false; - } else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday')) { - set['items.gear.owned.armor_special_birthday2015'] = false; - } else { - set['items.gear.owned.armor_special_birthday'] = false; - } - - var inc = { - 'items.food.Cake_Skeleton':1, - 'items.food.Cake_Base':1, - 'items.food.Cake_CottonCandyBlue':1, - 'items.food.Cake_CottonCandyPink':1, - 'items.food.Cake_Shade':1, - 'items.food.Cake_White':1, - 'items.food.Cake_Golden':1, - 'items.food.Cake_Zombie':1, - 'items.food.Cake_Desert':1, - 'items.food.Cake_Red':1, - 'achievements.habitBirthdays':1 - }; - - dbUsers.update({_id: user._id}, {$set:set, $inc:inc}); - - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -} - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - -module.exports = processUsers; diff --git a/migrations/20170418_subscriber_jackalopes.js b/migrations/20170418_subscriber_jackalopes.js deleted file mode 100644 index d32867de98..0000000000 --- a/migrations/20170418_subscriber_jackalopes.js +++ /dev/null @@ -1,88 +0,0 @@ -var migrationName = '20170418_subscriber_jackalopes.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award Royal Purple Jackalope pet to all current subscribers - */ - -var monk = require('monk'); -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbUsers = monk(connectionString).get('users', { castIds: false }); -var now = new Date(); - -function processUsers(lastId) { - // specify a query to limit the affected users (empty for all users): - var query = { - 'purchased.plan.customerId': {$type: 2}, - $or: [ - {'purchased.plan.dateTerminated': null}, - {'purchased.plan.dateTerminated': {$exists: false}}, - {'purchased.plan.dateTerminated': {$gt: now}}, - ] - }; - - if (lastId) { - query._id = { - $gt: lastId - } - } - - dbUsers.find(query, { - sort: {_id: 1}, - limit: 250, - fields: [] // specify fields we are interested in to limit retrieved data (empty if we're not reading data): - }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); -} - -var progressCount = 1000; -var count = 0; - -function updateUsers (users) { - if (!users || users.length === 0) { - console.warn('All appropriate users found and modified.'); - displayData(); - return; - } - - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; - - return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); -} - -function updateUser (user) { - count++; - - var set = {'items.pets.Jackalope-RoyalPurple': 5}; - - dbUsers.update({_id: user._id}, {$set:set}); - - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -} - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - -module.exports = processUsers; diff --git a/migrations/20170711_orcas.js b/migrations/20170711_orcas.js deleted file mode 100644 index fbecd0cecd..0000000000 --- a/migrations/20170711_orcas.js +++ /dev/null @@ -1,90 +0,0 @@ -var migrationName = '20170711_orcas.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award Orca pets to owners of Orca mount, and Orca mount to everyone else - */ - -var monk = require('monk'); -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbUsers = monk(connectionString).get('users', { castIds: false }); - -function processUsers(lastId) { - // specify a query to limit the affected users (empty for all users): - var query = { - 'migration':{$ne:migrationName}, - }; - - if (lastId) { - query._id = { - $gt: lastId - } - } - - dbUsers.find(query, { - sort: {_id: 1}, - limit: 250, - fields: [ - 'items.mounts', - ] // specify fields we are interested in to limit retrieved data (empty if we're not reading data): - }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); -} - -var progressCount = 1000; -var count = 0; - -function updateUsers (users) { - if (!users || users.length === 0) { - console.warn('All appropriate users found and modified.'); - displayData(); - return; - } - - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; - - return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); -} - -function updateUser (user) { - count++; - - var set = {}; - - if (user.items.mounts['Orca-Base']) { - set = {'migration':migrationName, 'items.pets.Orca-Base': 5}; - } else { - set = {'migration':migrationName, 'items.mounts.Orca-Base': true}; - } - - dbUsers.update({_id: user._id}, {$set:set}); - - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -} - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - -module.exports = processUsers; diff --git a/migrations/20170731_naming_day.js b/migrations/20170731_naming_day.js deleted file mode 100644 index a87ff6cb5a..0000000000 --- a/migrations/20170731_naming_day.js +++ /dev/null @@ -1,109 +0,0 @@ -var migrationName = '20170731_naming_day.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award Royal Purple Gryphon Helm to Royal Purple Gryphon pet owners, - * award Royal Purple Gryphon pet to Royal Purple Gryphon mount owners, - * award Royal Purple Gryphon mount to everyone else - */ - -var monk = require('monk'); -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbUsers = monk(connectionString).get('users', { castIds: false }); - -function processUsers(lastId) { - // specify a query to limit the affected users (empty for all users): - var query = { - 'migration':{$ne:migrationName}, - 'auth.timestamps.loggedin': {$gt: new Date('2017-01-01')}, - }; - - if (lastId) { - query._id = { - $gt: lastId - } - } - - dbUsers.find(query, { - sort: {_id: 1}, - limit: 250, - fields: [ - 'items.mounts', - 'items.pets', - ] // specify fields we are interested in to limit retrieved data (empty if we're not reading data): - }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); -} - -var progressCount = 1000; -var count = 0; - -function updateUsers (users) { - if (!users || users.length === 0) { - console.warn('All appropriate users found and modified.'); - displayData(); - return; - } - - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; - - return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); -} - -function updateUser (user) { - count++; - - var set = {}; - var inc = { - 'achievements.habiticaDays': 1, - 'items.food.Cake_Skeleton': 1, - 'items.food.Cake_Base': 1, - 'items.food.Cake_CottonCandyBlue': 1, - 'items.food.Cake_CottonCandyPink': 1, - 'items.food.Cake_Shade': 1, - 'items.food.Cake_White': 1, - 'items.food.Cake_Golden': 1, - 'items.food.Cake_Zombie': 1, - 'items.food.Cake_Desert': 1, - 'items.food.Cake_Red': 1 - }; - - if (user.items.pets['Gryphon-RoyalPurple']) { - set = {'migration':migrationName, 'items.gear.owned.head_special_namingDay2017': false}; - } else if (user.items.mounts['Gryphon-RoyalPurple']) { - set = {'migration':migrationName, 'items.pets.Gryphon-RoyalPurple': 5}; - } else { - set = {'migration':migrationName, 'items.mounts.Gryphon-RoyalPurple': true}; - } - - dbUsers.update({_id: user._id}, {$set: set, $inc: inc}); - - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -} - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - -module.exports = processUsers; diff --git a/migrations/20170928_redesign_guilds.js b/migrations/20170928_redesign_guilds.js deleted file mode 100644 index 62d68d4ac4..0000000000 --- a/migrations/20170928_redesign_guilds.js +++ /dev/null @@ -1,97 +0,0 @@ -var migrationName = '20170928_redesign_guilds.js'; - -/* - * Copy Guild Leader messages to end of Guild descriptions - * Copy Guild logos to beginning of Guild descriptions - */ - -var monk = require('monk'); -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbGroups = monk(connectionString).get('groups', { castIds: false }); - -function processGroups(lastId) { - // specify a query to limit the affected groups (empty for all groups): - var query = { - }; - - var fields = { - 'description': 1, - 'logo': 1, - 'leaderMessage': 1, - } - - if (lastId) { - query._id = { - $gt: lastId - } - } - - return dbGroups.find(query, { - fields: fields, - sort: {_id: 1}, - limit: 250, - }) - .then(updateGroups) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); -} - -var progressCount = 1000; -var count = 0; - -function updateGroups (groups) { - if (!groups || groups.length === 0) { - console.warn('All appropriate groups found and modified.'); - displayData(); - return; - } - - var groupPromises = groups.map(updateGroup); - var lastGroup = groups[groups.length - 1]; - - return Promise.all(groupPromises) - .then(function () { - processGroups(lastGroup._id); - }); -} - -function updateGroup (group) { - count++; - - var description = group.description; - - if (group.logo) { - description = '![Guild Logo](' + group.logo + ')\n\n \n\n' + description; - } - - if (group.leaderMessage) { - description = description + '\n\n \n\n' + group.leaderMessage; - } - - var set = { - description: description, - }; - - if (count % progressCount == 0) console.warn(count + ' ' + group._id); - - return dbGroups.update({_id: group._id}, {$set:set}); -} - -function displayData() { - console.warn('\n' + count + ' groups processed\n'); - return exiting(0); -} - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - -module.exports = processGroups; diff --git a/migrations/20171030_jackolanterns.js b/migrations/20171030_jackolanterns.js deleted file mode 100644 index 63be6de265..0000000000 --- a/migrations/20171030_jackolanterns.js +++ /dev/null @@ -1,111 +0,0 @@ -var migrationName = '20171030_jackolanterns.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award the Jack-O'-Lantern ladder: - * Ghost Jack-O-Lantern Mount to owners of Ghost Jack-O-Lantern Pet - * Ghost Jack-O-Lantern Pet to owners of Jack-O-Lantern Mount - * Jack-O-Lantern Mount to owners of Jack-O-Lantern Pet - * Jack-O-Lantern Pet to everyone else - */ - -var monk = require('monk'); -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbUsers = monk(connectionString).get('users', { castIds: false }); - -function processUsers(lastId) { - // specify a query to limit the affected users (empty for all users): - var query = { - 'migration':{$ne:migrationName}, - }; - - if (lastId) { - query._id = { - $gt: lastId - } - } - - dbUsers.find(query, { - sort: {_id: 1}, - limit: 250, - fields: [ - 'items.pets', - 'items.mounts', - ] // specify fields we are interested in to limit retrieved data (empty if we're not reading data): - }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); -} - -var progressCount = 1000; -var count = 0; - -function updateUsers (users) { - if (!users || users.length === 0) { - console.warn('All appropriate users found and modified.'); - displayData(); - return; - } - - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; - - return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); -} - -function updateUser (user) { - count++; - - var set = {}; - var inc = { - 'items.food.Candy_Skeleton': 1, - 'items.food.Candy_Base': 1, - 'items.food.Candy_CottonCandyBlue': 1, - 'items.food.Candy_CottonCandyPink': 1, - 'items.food.Candy_Shade': 1, - 'items.food.Candy_White': 1, - 'items.food.Candy_Golden': 1, - 'items.food.Candy_Zombie': 1, - 'items.food.Candy_Desert': 1, - 'items.food.Candy_Red': 1, - }; - - if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Ghost']) { - set = {'migration':migrationName, 'items.mounts.JackOLantern-Ghost': true}; - } else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Base']) { - set = {'migration':migrationName, 'items.pets.JackOLantern-Ghost': 5}; - } else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Base']) { - set = {'migration':migrationName, 'items.mounts.JackOLantern-Base': true}; - } else { - set = {'migration':migrationName, 'items.pets.JackOLantern-Base': 5}; - } - - dbUsers.update({_id: user._id}, {$set:set, $inc:inc}); - - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -} - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - -module.exports = processUsers; diff --git a/migrations/20171230_nye_hats.js b/migrations/20171230_nye_hats.js deleted file mode 100644 index 08db9a4158..0000000000 --- a/migrations/20171230_nye_hats.js +++ /dev/null @@ -1,103 +0,0 @@ -var migrationName = '20171230_nye_hats.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done - -/* - * Award New Year's Eve party hats to users in sequence - */ - -var monk = require('monk'); -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbUsers = monk(connectionString).get('users', { castIds: false }); - -function processUsers(lastId) { - // specify a query to limit the affected users (empty for all users): - var query = { - 'migration': {$ne:migrationName}, - 'auth.timestamps.loggedin': {$gt:new Date('2017-11-30')}, - }; - - if (lastId) { - query._id = { - $gt: lastId - } - } - - dbUsers.find(query, { - sort: {_id: 1}, - limit: 250, - fields: [ - 'items.gear.owned', - ] // specify fields we are interested in to limit retrieved data (empty if we're not reading data): - }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); -} - -var progressCount = 1000; -var count = 0; - -function updateUsers (users) { - if (!users || users.length === 0) { - console.warn('All appropriate users found and modified.'); - displayData(); - return; - } - - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; - - return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); -} - -function updateUser (user) { - count++; - - var set = {}; - var push = {}; - - if (typeof user.items.gear.owned.head_special_nye2016 !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.head_special_nye2017':false}; - push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_nye2017', '_id': monk.id()}}; - } else if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.head_special_nye2016':false}; - push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_nye2016', '_id': monk.id()}}; - } else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.head_special_nye2015':false}; - push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_nye2015', '_id': monk.id()}}; - } else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.head_special_nye2014':false}; - push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_nye2014', '_id': monk.id()}}; - } else { - set = {'migration':migrationName, 'items.gear.owned.head_special_nye':false}; - push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_nye', '_id': monk.id()}}; - } - - dbUsers.update({_id: user._id}, {$set: set, $push: push}); - - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); -} - -function displayData() { - console.warn('\n' + count + ' users processed\n'); - return exiting(0); -} - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - -module.exports = processUsers; diff --git a/migrations/api_v3/challenges.js b/migrations/api_v3/challenges.js deleted file mode 100644 index 94adbed6e5..0000000000 --- a/migrations/api_v3/challenges.js +++ /dev/null @@ -1,218 +0,0 @@ -// Migrate challenges collection to new schema (except for members) - -// The console-stamp module must be installed (not included in package.json) - -// It requires two environment variables: MONGODB_OLD and MONGODB_NEW - -// Due to some big user profiles it needs more RAM than is allowed by default by v8 (arounf 1.7GB). -// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM - -// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. -// We've now upgraded to lodash v4 but the code used in this migration has not been -// adapted to work with it. Before this migration is used again any lodash method should -// be checked for compatibility against the v4 changelog and changed if necessary. -// https://github.com/lodash/lodash/wiki/Changelog#v400 -console.log('Starting migrations/api_v3/challenges.js.'); - -require('babel-register'); -require('babel-polyfill'); - -var Bluebird = require('bluebird'); -var MongoDB = require('mongodb'); -var nconf = require('nconf'); -var mongoose = require('mongoose'); -var _ = require('lodash'); -var uuid = require('uuid'); -var consoleStamp = require('console-stamp'); -var fs = require('fs'); - -// Add timestamps to console messages -consoleStamp(console); - -// Initialize configuration -require('../../website/server/libs/api-v3/setupNconf')(); - -var MONGODB_OLD = nconf.get('MONGODB_OLD'); -var MONGODB_NEW = nconf.get('MONGODB_NEW'); - -var MongoClient = MongoDB.MongoClient; - -mongoose.Promise = Bluebird; // otherwise mongoose models won't work - -// Load new models -var NewChallenge = require('../../website/server/models/challenge').model; -var Tasks = require('../../website/server/models/task'); - -// To be defined later when MongoClient connects -var mongoDbOldInstance; -var oldChallengeCollection; - -var mongoDbNewInstance; -var newChallengeCollection; -var newTaskCollection; - -var BATCH_SIZE = 1000; - -var processedChallenges = 0; -var totoalProcessedTasks = 0; - -var newTasksIds = {}; // a map of old id -> [new id, challengeId] - -// Only process challenges that fall in a interval ie -> up to 0000-4000-0000-0000 -var AFTER_CHALLENGE_ID = nconf.get('AFTER_CHALLENGE_ID'); -var BEFORE_CHALLENGE_ID = nconf.get('BEFORE_CHALLENGE_ID'); - -function processChallenges (afterId) { - var processedTasks = 0; - var lastChallenge = null; - var oldChallenges; - - var query = {}; - - if (BEFORE_CHALLENGE_ID) { - query._id = {$lte: BEFORE_CHALLENGE_ID}; - } - - if ((afterId || AFTER_CHALLENGE_ID) && !query._id) { - query._id = {}; - } - - if (afterId) { - query._id.$gt = afterId; - } else if (AFTER_CHALLENGE_ID) { - query._id.$gt = AFTER_CHALLENGE_ID; - } - - var batchInsertTasks = newTaskCollection.initializeUnorderedBulkOp(); - var batchInsertChallenges = newChallengeCollection.initializeUnorderedBulkOp(); - - console.log(`Executing challenges query.\nMatching challenges after ${afterId ? afterId : AFTER_CHALLENGE_ID} and before ${BEFORE_CHALLENGE_ID} (included).`); - - return oldChallengeCollection - .find(query) - .sort({_id: 1}) - .limit(BATCH_SIZE) - .toArray() - .then(function (oldChallengesR) { - oldChallenges = oldChallengesR; - - console.log(`Processing ${oldChallenges.length} challenges. Already processed ${processedChallenges} challenges and ${totoalProcessedTasks} tasks.`); - - if (oldChallenges.length === BATCH_SIZE) { - lastChallenge = oldChallenges[oldChallenges.length - 1]._id; - } - - oldChallenges.forEach(function (oldChallenge) { - var oldTasks = oldChallenge.habits.concat(oldChallenge.dailys).concat(oldChallenge.rewards).concat(oldChallenge.todos); - delete oldChallenge.habits; - delete oldChallenge.dailys; - delete oldChallenge.rewards; - delete oldChallenge.todos; - - var createdAt = oldChallenge.timestamp; - - oldChallenge.memberCount = oldChallenge.members.length; - if (oldChallenge.prize <= 0) oldChallenge.prize = 0; - if (!oldChallenge.name) oldChallenge.name = 'challenge name'; - if (!oldChallenge.shortName) oldChallenge.name = 'challenge-name'; - - if (!oldChallenge.group) throw new Error('challenge.group is required'); - if (!oldChallenge.leader) throw new Error('challenge.leader is required'); - - - if (oldChallenge.leader === '9') { - oldChallenge.leader = '00000000-0000-4000-9000-000000000000'; - } - - if (oldChallenge.group === 'habitrpg') { - oldChallenge.group = '00000000-0000-4000-A000-000000000000'; - } - - delete oldChallenge.id; - - var newChallenge = new NewChallenge(oldChallenge); - - newChallenge.createdAt = createdAt; - - oldTasks.forEach(function (oldTask) { - oldTask._id = uuid.v4(); - oldTask._legacyId = oldTask.id; // store the old task id - delete oldTask.id; - - oldTask.challenge = oldTask.challenge || {}; - oldTask.challenge.id = newChallenge._id; - - if (newTasksIds[oldTask._legacyId + '-' + newChallenge._id]) { - throw new Error('duplicate :('); - } else { - newTasksIds[oldTask._legacyId + '-' + newChallenge._id] = oldTask._id; - } - - oldTask.tags = _.map(oldTask.tags || {}, function (tagPresent, tagId) { - return tagPresent && tagId; - }).filter(function (tag) { - return tag !== false; - }); - - if (!oldTask.text) oldTask.text = 'task text'; // required - - oldTask.createdAt = oldTask.dateCreated; - - newChallenge.tasksOrder[`${oldTask.type}s`].push(oldTask._id); - if (oldTask.completed) oldTask.completed = false; - - var newTask = new Tasks[oldTask.type](oldTask); - - batchInsertTasks.insert(newTask.toObject()); - processedTasks++; - }); - - batchInsertChallenges.insert(newChallenge.toObject()); - }); - - console.log(`Saving ${oldChallenges.length} challenges and ${processedTasks} tasks.`); - - return Bluebird.all([ - batchInsertChallenges.execute(), - batchInsertTasks.execute(), - ]); - }) - .then(function () { - totoalProcessedTasks += processedTasks; - processedChallenges += oldChallenges.length; - - console.log(`Saved ${oldChallenges.length} challenges and their tasks.`); - - if (lastChallenge) { - return processChallenges(lastChallenge); - } else { - console.log('Writing newTasksIds.json...') - fs.writeFileSync('newTasksIds.json', JSON.stringify(newTasksIds, null, 4), 'utf8'); - return console.log('Done!'); - } - }); -} - -// Connect to the databases -Bluebird.all([ - MongoClient.connect(MONGODB_OLD), - MongoClient.connect(MONGODB_NEW), -]) -.then(function (result) { - var oldInstance = result[0]; - var newInstance = result[1]; - - mongoDbOldInstance = oldInstance; - oldChallengeCollection = mongoDbOldInstance.collection('challenges'); - - mongoDbNewInstance = newInstance; - newChallengeCollection = mongoDbNewInstance.collection('challenges'); - newTaskCollection = mongoDbNewInstance.collection('tasks'); - - console.log(`Connected with MongoClient to ${MONGODB_OLD} and ${MONGODB_NEW}.`); - - return processChallenges(); -}) -.catch(function (err) { - console.error(err.stack || err); -}); diff --git a/migrations/api_v3/challengesMembers.js b/migrations/api_v3/challengesMembers.js deleted file mode 100644 index 349abde57b..0000000000 --- a/migrations/api_v3/challengesMembers.js +++ /dev/null @@ -1,149 +0,0 @@ -// Migrate challenges members -// Run AFTER users migration - -// The console-stamp module must be installed (not included in package.json) - -// It requires two environment variables: MONGODB_OLD and MONGODB_NEW - -// Due to some big user profiles it needs more RAM than is allowed by default by v8 (arounf 1.7GB). -// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM -console.log('Starting migrations/api_v3/challengesMembers.js.'); - -// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. -// We've now upgraded to lodash v4 but the code used in this migration has not been -// adapted to work with it. Before this migration is used again any lodash method should -// be checked for compatibility against the v4 changelog and changed if necessary. -// https://github.com/lodash/lodash/wiki/Changelog#v400 - -require('babel-register'); -require('babel-polyfill'); - -var Bluebird = require('bluebird'); -var MongoDB = require('mongodb'); -var nconf = require('nconf'); -var mongoose = require('mongoose'); -var _ = require('lodash'); -var uuid = require('uuid'); -var consoleStamp = require('console-stamp'); - -// Add timestamps to console messages -consoleStamp(console); - -// Initialize configuration -require('../../website/server/libs/api-v3/setupNconf')(); - -var MONGODB_OLD = nconf.get('MONGODB_OLD'); -var MONGODB_NEW = nconf.get('MONGODB_NEW'); - -var MongoClient = MongoDB.MongoClient; - -mongoose.Promise = Bluebird; // otherwise mongoose models won't work - -// To be defined later when MongoClient connects -var mongoDbOldInstance; -var oldChallengeCollection; - -var mongoDbNewInstance; -var newUserCollection; - -var BATCH_SIZE = 1000; - -var processedChallenges = 0; - -// Only process challenges that fall in a interval ie -> up to 0000-4000-0000-0000 -var AFTER_CHALLENGE_ID = nconf.get('AFTER_CHALLENGE_ID'); -var BEFORE_CHALLENGE_ID = nconf.get('BEFORE_CHALLENGE_ID'); - -function processChallenges (afterId) { - var processedTasks = 0; - var lastChallenge = null; - var oldChallenges; - - var query = {}; - - if (BEFORE_CHALLENGE_ID) { - query._id = {$lte: BEFORE_CHALLENGE_ID}; - } - - if ((afterId || AFTER_CHALLENGE_ID) && !query._id) { - query._id = {}; - } - - if (afterId) { - query._id.$gt = afterId; - } else if (AFTER_CHALLENGE_ID) { - query._id.$gt = AFTER_CHALLENGE_ID; - } - - console.log(`Executing challenges query.\nMatching challenges after ${afterId ? afterId : AFTER_CHALLENGE_ID} and before ${BEFORE_CHALLENGE_ID} (included).`); - - return oldChallengeCollection - .find(query) - .sort({_id: 1}) - .limit(BATCH_SIZE) - .toArray() - .then(function (oldChallengesR) { - oldChallenges = oldChallengesR; - - var promises = []; - - console.log(`Processing ${oldChallenges.length} challenges. Already processed ${processedChallenges} challenges.`); - - if (oldChallenges.length === BATCH_SIZE) { - lastChallenge = oldChallenges[oldChallenges.length - 1]._id; - } - - oldChallenges.forEach(function (oldChallenge) { - // Tyler Renelle - oldChallenge.members.forEach(function (id, index) { - if (id === '9') { - oldChallenge.members[index] = '00000000-0000-4000-9000-000000000000'; - } - }); - - promises.push(newUserCollection.updateMany({ - _id: {$in: oldChallenge.members || []}, - }, { - $push: {challenges: oldChallenge._id}, - }, {multi: true})); - }); - - console.log(`Migrating members of ${oldChallenges.length} challenges.`); - - return Bluebird.all(promises); - }) - .then(function () { - processedChallenges += oldChallenges.length; - - console.log(`Migrated members of ${oldChallenges.length} challenges.`); - - if (lastChallenge) { - return processChallenges(lastChallenge); - } else { - return console.log('Done!'); - } - }); -} - -// Connect to the databases -Bluebird.all([ - MongoClient.connect(MONGODB_OLD), - MongoClient.connect(MONGODB_NEW), -]) -.then(function (result) { - var oldInstance = result[0]; - var newInstance = result[1]; - - mongoDbOldInstance = oldInstance; - oldChallengeCollection = mongoDbOldInstance.collection('challenges'); - - mongoDbNewInstance = newInstance; - newUserCollection = mongoDbNewInstance.collection('users'); - - console.log(`Connected with MongoClient to ${MONGODB_OLD} and ${MONGODB_NEW}.`); - - return processChallenges(); -}) -.catch(function (err) { - console.error(err.stack || err); -}); diff --git a/migrations/api_v3/coupons.js b/migrations/api_v3/coupons.js deleted file mode 100644 index 8b79deba8d..0000000000 --- a/migrations/api_v3/coupons.js +++ /dev/null @@ -1,142 +0,0 @@ -// Migrate coupons collection to new schema - -// The console-stamp module must be installed (not included in package.json) - -// It requires two environment variables: MONGODB_OLD and MONGODB_NEW - -// Due to some big user profiles it needs more RAM than is allowed by default by v8 (arounf 1.7GB). -// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM -console.log('Starting migrations/api_v3/coupons.js.'); - -// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. -// We've now upgraded to lodash v4 but the code used in this migration has not been -// adapted to work with it. Before this migration is used again any lodash method should -// be checked for compatibility against the v4 changelog and changed if necessary. -// https://github.com/lodash/lodash/wiki/Changelog#v400 - -require('babel-register'); -require('babel-polyfill'); - -var Bluebird = require('bluebird'); -var MongoDB = require('mongodb'); -var nconf = require('nconf'); -var mongoose = require('mongoose'); -var _ = require('lodash'); -var uuid = require('uuid'); -var consoleStamp = require('console-stamp'); - -// Add timestamps to console messages -consoleStamp(console); - -// Initialize configuration -require('../../website/server/libs/api-v3/setupNconf')(); - -var MONGODB_OLD = nconf.get('MONGODB_OLD'); -var MONGODB_NEW = nconf.get('MONGODB_NEW'); - -var MongoClient = MongoDB.MongoClient; - -mongoose.Promise = Bluebird; // otherwise mongoose models won't work - -// Load new models -var Coupon = require('../../website/server/models/coupon').model; - -// To be defined later when MongoClient connects -var mongoDbOldInstance; -var oldCouponCollection; - -var mongoDbNewInstance; -var newCouponCollection; - -var BATCH_SIZE = 1000; - -var processedCoupons = 0; - -// Only process coupons that fall in a interval ie -> up to 0000-4000-0000-0000 -var AFTER_COUPON_ID = nconf.get('AFTER_COUPON_ID'); -var BEFORE_COUPON_ID = nconf.get('BEFORE_COUPON_ID'); - -function processCoupons (afterId) { - var processedTasks = 0; - var lastCoupon = null; - var oldCoupons; - - var query = {}; - - if (BEFORE_COUPON_ID) { - query._id = {$lte: BEFORE_COUPON_ID}; - } - - if ((afterId || AFTER_COUPON_ID) && !query._id) { - query._id = {}; - } - - if (afterId) { - query._id.$gt = afterId; - } else if (AFTER_COUPON_ID) { - query._id.$gt = AFTER_COUPON_ID; - } - - var batchInsertCoupons = newCouponCollection.initializeUnorderedBulkOp(); - - console.log(`Executing coupons query.\nMatching coupons after ${afterId ? afterId : AFTER_COUPON_ID} and before ${BEFORE_COUPON_ID} (included).`); - - return oldCouponCollection - .find(query) - .sort({_id: 1}) - .limit(BATCH_SIZE) - .toArray() - .then(function (oldCouponsR) { - oldCoupons = oldCouponsR; - - console.log(`Processing ${oldCoupons.length} coupons. Already processed ${processedCoupons} coupons.`); - - if (oldCoupons.length === BATCH_SIZE) { - lastCoupon = oldCoupons[oldCoupons.length - 1]._id; - } - - oldCoupons.forEach(function (oldCoupon) { - var newCoupon = new Coupon(oldCoupon); - - batchInsertCoupons.insert(newCoupon.toObject()); - }); - - console.log(`Saving ${oldCoupons.length} coupons.`); - - return batchInsertCoupons.execute(); - }) - .then(function () { - processedCoupons += oldCoupons.length; - - console.log(`Saved ${oldCoupons.length} coupons.`); - - if (lastCoupon) { - return processCoupons(lastCoupon); - } else { - return console.log('Done!'); - } - }); -} - -// Connect to the databases -Bluebird.all([ - MongoClient.connect(MONGODB_OLD), - MongoClient.connect(MONGODB_NEW), -]) -.then(function (result) { - var oldInstance = result[0]; - var newInstance = result[1]; - - mongoDbOldInstance = oldInstance; - oldCouponCollection = mongoDbOldInstance.collection('coupons'); - - mongoDbNewInstance = newInstance; - newCouponCollection = mongoDbNewInstance.collection('coupons'); - - console.log(`Connected with MongoClient to ${MONGODB_OLD} and ${MONGODB_NEW}.`); - - return processCoupons(); -}) -.catch(function (err) { - console.error(err.stack || err); -}); diff --git a/migrations/api_v3/emailUnsubscriptions.js b/migrations/api_v3/emailUnsubscriptions.js deleted file mode 100644 index 3e1629563e..0000000000 --- a/migrations/api_v3/emailUnsubscriptions.js +++ /dev/null @@ -1,143 +0,0 @@ -// Migrate unsubscriptions collection to new schema - -// The console-stamp module must be installed (not included in package.json) - -// It requires two environment variables: MONGODB_OLD and MONGODB_NEW - -// Due to some big user profiles it needs more RAM than is allowed by default by v8 (arounf 1.7GB). -// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM -console.log('Starting migrations/api_v3/unsubscriptions.js.'); - -// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. -// We've now upgraded to lodash v4 but the code used in this migration has not been -// adapted to work with it. Before this migration is used again any lodash method should -// be checked for compatibility against the v4 changelog and changed if necessary. -// https://github.com/lodash/lodash/wiki/Changelog#v400 - -require('babel-register'); -require('babel-polyfill'); - -var Bluebird = require('bluebird'); -var MongoDB = require('mongodb'); -var nconf = require('nconf'); -var mongoose = require('mongoose'); -var _ = require('lodash'); -var uuid = require('uuid'); -var consoleStamp = require('console-stamp'); - -// Add timestamps to console messages -consoleStamp(console); - -// Initialize configuration -require('../../website/server/libs/api-v3/setupNconf')(); - -var MONGODB_OLD = nconf.get('MONGODB_OLD'); -var MONGODB_NEW = nconf.get('MONGODB_NEW'); - -var MongoClient = MongoDB.MongoClient; - -mongoose.Promise = Bluebird; // otherwise mongoose models won't work - -// Load new models -var EmailUnsubscription = require('../../website/server/models/emailUnsubscription').model; - -// To be defined later when MongoClient connects -var mongoDbOldInstance; -var oldUnsubscriptionCollection; - -var mongoDbNewInstance; -var newUnsubscriptionCollection; - -var BATCH_SIZE = 1000; - -var processedUnsubscriptions = 0; - -// Only process unsubscriptions that fall in a interval ie -> up to 0000-4000-0000-0000 -var AFTER_UNSUBSCRIPTION_ID = nconf.get('AFTER_UNSUBSCRIPTION_ID'); -var BEFORE_UNSUBSCRIPTION_ID = nconf.get('BEFORE_UNSUBSCRIPTION_ID'); - -function processUnsubscriptions (afterId) { - var processedTasks = 0; - var lastUnsubscription = null; - var oldUnsubscriptions; - - var query = {}; - - if (BEFORE_UNSUBSCRIPTION_ID) { - query._id = {$lte: BEFORE_UNSUBSCRIPTION_ID}; - } - - if ((afterId || AFTER_UNSUBSCRIPTION_ID) && !query._id) { - query._id = {}; - } - - if (afterId) { - query._id.$gt = afterId; - } else if (AFTER_UNSUBSCRIPTION_ID) { - query._id.$gt = AFTER_UNSUBSCRIPTION_ID; - } - - var batchInsertUnsubscriptions = newUnsubscriptionCollection.initializeUnorderedBulkOp(); - - console.log(`Executing unsubscriptions query.\nMatching unsubscriptions after ${afterId ? afterId : AFTER_UNSUBSCRIPTION_ID} and before ${BEFORE_UNSUBSCRIPTION_ID} (included).`); - - return oldUnsubscriptionCollection - .find(query) - .sort({_id: 1}) - .limit(BATCH_SIZE) - .toArray() - .then(function (oldUnsubscriptionsR) { - oldUnsubscriptions = oldUnsubscriptionsR; - - console.log(`Processing ${oldUnsubscriptions.length} unsubscriptions. Already processed ${processedUnsubscriptions} unsubscriptions.`); - - if (oldUnsubscriptions.length === BATCH_SIZE) { - lastUnsubscription = oldUnsubscriptions[oldUnsubscriptions.length - 1]._id; - } - - oldUnsubscriptions.forEach(function (oldUnsubscription) { - oldUnsubscription.email = oldUnsubscription.email.toLowerCase(); - var newUnsubscription = new EmailUnsubscription(oldUnsubscription); - - batchInsertUnsubscriptions.insert(newUnsubscription.toObject()); - }); - - console.log(`Saving ${oldUnsubscriptions.length} unsubscriptions.`); - - return batchInsertUnsubscriptions.execute(); - }) - .then(function () { - processedUnsubscriptions += oldUnsubscriptions.length; - - console.log(`Saved ${oldUnsubscriptions.length} unsubscriptions.`); - - if (lastUnsubscription) { - return processUnsubscriptions(lastUnsubscription); - } else { - return console.log('Done!'); - } - }); -} - -// Connect to the databases -Bluebird.all([ - MongoClient.connect(MONGODB_OLD), - MongoClient.connect(MONGODB_NEW), -]) -.then(function (result) { - var oldInstance = result[0]; - var newInstance = result[1]; - - mongoDbOldInstance = oldInstance; - oldUnsubscriptionCollection = mongoDbOldInstance.collection('emailunsubscriptions'); - - mongoDbNewInstance = newInstance; - newUnsubscriptionCollection = mongoDbNewInstance.collection('emailunsubscriptions'); - - console.log(`Connected with MongoClient to ${MONGODB_OLD} and ${MONGODB_NEW}.`); - - return processUnsubscriptions(); -}) -.catch(function (err) { - console.error(err.stack || err); -}); diff --git a/migrations/api_v3/groups.js b/migrations/api_v3/groups.js deleted file mode 100644 index e623b9d062..0000000000 --- a/migrations/api_v3/groups.js +++ /dev/null @@ -1,217 +0,0 @@ -/* - members are not stored anymore - invites are not stored anymore - - tavern id and leader must be updated -*/ - -// Migrate groups collection to new schema -// Run AFTER users migration - -// The console-stamp module must be installed (not included in package.json) - -// It requires two environment variables: MONGODB_OLD and MONGODB_NEW - -// Due to some big user profiles it needs more RAM than is allowed by default by v8 (arounf 1.7GB). -// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM -console.log('Starting migrations/api_v3/groups.js.'); - -// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. -// We've now upgraded to lodash v4 but the code used in this migration has not been -// adapted to work with it. Before this migration is used again any lodash method should -// be checked for compatibility against the v4 changelog and changed if necessary. -// https://github.com/lodash/lodash/wiki/Changelog#v400 - -require('babel-register'); -require('babel-polyfill'); - -var Bluebird = require('bluebird'); -var MongoDB = require('mongodb'); -var nconf = require('nconf'); -var mongoose = require('mongoose'); -var _ = require('lodash'); -var uuid = require('uuid'); -var consoleStamp = require('console-stamp'); - -// Add timestamps to console messages -consoleStamp(console); - -// Initialize configuration -require('../../website/server/libs/api-v3/setupNconf')(); - -var MONGODB_OLD = nconf.get('MONGODB_OLD'); -var MONGODB_NEW = nconf.get('MONGODB_NEW'); - -var MongoClient = MongoDB.MongoClient; - -mongoose.Promise = Bluebird; // otherwise mongoose models won't work - -// Load new models -var NewGroup = require('../../website/server/models/group').model; - -var TAVERN_ID = require('../../website/server/models/group').TAVERN_ID; - -// To be defined later when MongoClient connects -var mongoDbOldInstance; -var oldGroupCollection; - -var mongoDbNewInstance; -var newGroupCollection; -var newUserCollection; - -var BATCH_SIZE = 1000; - -var processedGroups = 0; - -// Only process groups that fall in a interval ie -> up to 0000-4000-0000-0000 -var AFTER_GROUP_ID = nconf.get('AFTER_GROUP_ID'); -var BEFORE_GROUP_ID = nconf.get('BEFORE_GROUP_ID'); - -function processGroups (afterId) { - var processedTasks = 0; - var lastGroup = null; - var oldGroups; - - var query = {}; - - if (BEFORE_GROUP_ID) { - query._id = {$lte: BEFORE_GROUP_ID}; - } - - if ((afterId || AFTER_GROUP_ID) && !query._id) { - query._id = {}; - } - - if (afterId) { - query._id.$gt = afterId; - } else if (AFTER_GROUP_ID) { - query._id.$gt = AFTER_GROUP_ID; - } - - var batchInsertGroups = newGroupCollection.initializeUnorderedBulkOp(); - - console.log(`Executing groups query.\nMatching groups after ${afterId ? afterId : AFTER_GROUP_ID} and before ${BEFORE_GROUP_ID} (included).`); - - return oldGroupCollection - .find(query) - .sort({_id: 1}) - .limit(BATCH_SIZE) - .toArray() - .then(function (oldGroupsR) { - oldGroups = oldGroupsR; - - var promises = []; - - console.log(`Processing ${oldGroups.length} groups. Already processed ${processedGroups} groups.`); - - if (oldGroups.length === BATCH_SIZE) { - lastGroup = oldGroups[oldGroups.length - 1]._id; - } - - oldGroups.forEach(function (oldGroup) { - if ((!oldGroup.privacy || oldGroup.privacy === 'private') && (!oldGroup.members || oldGroup.members.length === 0)) return; // delete empty private groups TODO must also delete challenges or this won't work - - oldGroup.members = oldGroup.members || []; - oldGroup.memberCount = oldGroup.members ? oldGroup.members.length : 0; - oldGroup.challengeCount = oldGroup.challenges ? oldGroup.challenges.length : 0; - - if (oldGroup.balance <= 0) oldGroup.balance = 0; - if (!oldGroup.name) oldGroup.name = 'group name'; - if (!oldGroup.leaderOnly) oldGroup.leaderOnly = {}; - if (!oldGroup.leaderOnly.challenges) oldGroup.leaderOnly.challenges = false; - - // Tavern - if (oldGroup._id === 'habitrpg') { - oldGroup._id = TAVERN_ID; - oldGroup.leader = '7bde7864-ebc5-4ee2-a4b7-1070d464cdb0'; // Siena Leslie - } - - if (!oldGroup.type) { - // throw new Error('group.type is required'); - oldGroup.type = 'guild'; - } - - if (!oldGroup.leader) { - if (oldGroup.members && oldGroup.members.length > 0) { - oldGroup.leader = oldGroup.members[0]; - } else { - throw new Error('group.leader is required and no member available!'); - } - } - - if (!oldGroup.privacy) { - // throw new Error('group.privacy is required'); - oldGroup.privacy = 'private'; - } - - var updateMembers = {}; - - if (oldGroup.type === 'guild') { - updateMembers.$push = {guilds: oldGroup._id}; - } else if (oldGroup.type === 'party') { - updateMembers.$set = {'party._id': oldGroup._id}; - } - - if (oldGroup.members) { - // Tyler Renelle - oldGroup.members.forEach(function (id, index) { - if (id === '9') { - oldGroup.members[index] = '00000000-0000-4000-9000-000000000000'; - } - }); - - promises.push(newUserCollection.updateMany({ - _id: {$in: oldGroup.members}, - }, updateMembers, {multi: true})); - } - - var newGroup = new NewGroup(oldGroup); - - batchInsertGroups.insert(newGroup.toObject()); - }); - - console.log(`Saving ${oldGroups.length} groups and migrating members to users collection.`); - - promises.push(batchInsertGroups.execute()); - return Bluebird.all(promises); - }) - .then(function () { - processedGroups += oldGroups.length; - - console.log(`Saved ${oldGroups.length} groups and migrated their members to the user collection.`); - - if (lastGroup) { - return processGroups(lastGroup); - } else { - return console.log('Done!'); - } - }); -} - -// Connect to the databases -Bluebird.all([ - MongoClient.connect(MONGODB_OLD), - MongoClient.connect(MONGODB_NEW), -]) -.then(function (result) { - var oldInstance = result[0]; - var newInstance = result[1]; - - mongoDbOldInstance = oldInstance; - oldGroupCollection = mongoDbOldInstance.collection('groups'); - - mongoDbNewInstance = newInstance; - newGroupCollection = mongoDbNewInstance.collection('groups'); - newUserCollection = mongoDbNewInstance.collection('users'); - - console.log(`Connected with MongoClient to ${MONGODB_OLD} and ${MONGODB_NEW}.`); - - // First delete the tavern group created by having required the group model - return newGroupCollection.deleteOne({_id: TAVERN_ID}); -}) -.then(function () { - return processGroups(); -}) -.catch(function (err) { - console.error(err.stack || err); -}); diff --git a/migrations/api_v3/users.js b/migrations/api_v3/users.js deleted file mode 100644 index f2c8e2f52f..0000000000 --- a/migrations/api_v3/users.js +++ /dev/null @@ -1,268 +0,0 @@ -// Migrate users collection to new schema -// This should run AFTER challenges migration - -// The console-stamp module must be installed (not included in package.json) - -// It requires two environment variables: MONGODB_OLD and MONGODB_NEW - -// Due to some big user profiles it needs more RAM than is allowed by default by v8 (arounf 1.7GB). -// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM -console.log('Starting migrations/api_v3/users.js.'); - -// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. -// We've now upgraded to lodash v4 but the code used in this migration has not been -// adapted to work with it. Before this migration is used again any lodash method should -// be checked for compatibility against the v4 changelog and changed if necessary. -// https://github.com/lodash/lodash/wiki/Changelog#v400 - -require('babel-register'); -require('babel-polyfill'); - -var Bluebird = require('bluebird'); -var MongoDB = require('mongodb'); -var nconf = require('nconf'); -var mongoose = require('mongoose'); -var _ = require('lodash'); -var uuid = require('uuid'); -var consoleStamp = require('console-stamp'); -var common = require('../../common'); -var moment = require('moment'); - -// Add timestamps to console messages -consoleStamp(console); - -// Initialize configuration -require('../../website/server/libs/api-v3/setupNconf')(); - -var MONGODB_OLD = nconf.get('MONGODB_OLD'); -var MONGODB_NEW = nconf.get('MONGODB_NEW'); - -var taskDefaults = common.taskDefaults; -var MongoClient = MongoDB.MongoClient; - -mongoose.Promise = Bluebird; // otherwise mongoose models won't work - -// Load new models -var NewUser = require('../../website/server/models/user').model; -var NewTasks = require('../../website/server/models/task'); - -// To be defined later when MongoClient connects -var mongoDbOldInstance; -var oldUserCollection; - -var mongoDbNewInstance; -var newUserCollection; -var newTaskCollection; - -var BATCH_SIZE = 1000; - -var processedUsers = 0; -var totoalProcessedTasks = 0; - -var challengeTaskWithMatchingId = 0; -var challengeTaskNoMatchingId = 0; - -// Load the new tasks ids for challenges tasks -var newTasksIds = require('./newTasksIds.json'); - -// Only process users that fall in a interval ie up to -> 0000-4000-0000-0000 -var AFTER_USER_ID = nconf.get('AFTER_USER_ID'); -var BEFORE_USER_ID = nconf.get('BEFORE_USER_ID'); - -function processUsers (afterId) { - var processedTasks = 0; - var lastUser = null; - var oldUsers; - - var now = new Date(); - - var query = {}; - - if (BEFORE_USER_ID) { - query._id = {$lte: BEFORE_USER_ID}; - } - - if ((afterId || AFTER_USER_ID) && !query._id) { - query._id = {}; - } - - if (afterId) { - query._id.$gt = afterId; - } else if (AFTER_USER_ID) { - query._id.$gt = AFTER_USER_ID; - } - - var batchInsertTasks = newTaskCollection.initializeUnorderedBulkOp(); - var batchInsertUsers = newUserCollection.initializeUnorderedBulkOp(); - - console.log(`Executing users query.\nMatching users after ${afterId ? afterId : AFTER_USER_ID} and before ${BEFORE_USER_ID} (included).`); - - return oldUserCollection - .find(query) - .sort({_id: 1}) - .limit(BATCH_SIZE) - .toArray() - .then(function (oldUsersR) { - oldUsers = oldUsersR; - - console.log(`Processing ${oldUsers.length} users. Already processed ${processedUsers} users and ${totoalProcessedTasks} tasks.`); - - if (oldUsers.length === BATCH_SIZE) { - lastUser = oldUsers[oldUsers.length - 1]._id; - } - - oldUsers.forEach(function (oldUser) { - var oldTasks = oldUser.habits.concat(oldUser.dailys).concat(oldUser.rewards).concat(oldUser.todos); - delete oldUser.habits; - delete oldUser.dailys; - delete oldUser.rewards; - delete oldUser.todos; - - delete oldUser.id; - - // spookDust -> spookySparkles - - if (oldUser.achievements && oldUser.achievements.spookDust) { - oldUser.achievements.spookySparkles = oldUser.achievements.spookDust; - delete oldUser.achievements.spookDust; - } - - if (oldUser.items && oldUser.items.special && oldUser.items.special.spookDust) { - oldUser.items.special.spookySparkles = oldUser.items.special.spookDust; - delete oldUser.items.special.spookDust; - } - - if (oldUser.stats && oldUser.stats.buffs && oldUser.stats.buffs.spookySparkles) { - oldUser.stats.buffs.spookySparkles = oldUser.stats.buffs.spookDust; - delete oldUser.stats.buffs.spookDust; - } - - // end spookDust -> spookySparkles - - oldUser.tags = oldUser.tags.map(function (tag) { - return { - id: tag.id, - name: tag.name || 'tag name', - challenge: tag.challenge, - }; - }); - - if (oldUser._id === '9') { // Tyler Renelle - oldUser._id = '00000000-0000-4000-9000-000000000000'; - } - - var newUser = new NewUser(oldUser); - var isSubscribed = newUser.isSubscribed(); - - oldTasks.forEach(function (oldTask) { - oldTask._id = uuid.v4(); // create a new unique uuid - oldTask.userId = newUser._id; - oldTask._legacyId = oldTask.id; // store the old task id - delete oldTask.id; - - oldTask.challenge = oldTask.challenge || {}; - if (oldTask.challenge.id) { - if (oldTask.challenge.broken) { - oldTask.challenge.taskId = oldTask._legacyId; - } else { - var newId = newTasksIds[oldTask._legacyId + '-' + oldTask.challenge.id]; - - // Challenges' tasks ids changed - if (!newId && !oldTask.challenge.broken) { - challengeTaskNoMatchingId++; - oldTask.challenge.taskId = oldTask._legacyId; - oldTask.challenge.broken = 'CHALLENGE_TASK_NOT_FOUND'; - } else { - challengeTaskWithMatchingId++; - oldTask.challenge.taskId = newId; - } - } - } - - // Delete old completed todos - if (oldTask.type === 'todo' && oldTask.completed && (!oldTask.challenge.id || oldTask.challenge.broken)) { - if (moment(now).subtract(isSubscribed ? 90 : 30, 'days').toDate() > moment(oldTask.dateCompleted).toDate()) { - return; - } - } - - oldTask.createdAt = oldTask.dateCreated; - - if (!oldTask.text) oldTask.text = 'task text'; // required - oldTask.tags = _.map(oldTask.tags, function (tagPresent, tagId) { - return tagPresent && tagId; - }).filter(function (tag) { - return tag !== false; - }); - - if (oldTask.type !== 'todo' || (oldTask.type === 'todo' && !oldTask.completed)) { - newUser.tasksOrder[`${oldTask.type}s`].push(oldTask._id); - } - - var allTasksFields = ['_id', 'type', 'text', 'notes', 'tags', 'value', 'priority', 'attribute', 'challenge', 'reminders', 'userId', '_legacyId', 'createdAt']; - // using mongoose models is too slow - if (oldTask.type === 'habit') { - oldTask = _.pick(oldTask, allTasksFields.concat(['history', 'up', 'down'])); - } else if (oldTask.type === 'daily') { - oldTask = _.pick(oldTask, allTasksFields.concat(['completed', 'collapseChecklist', 'checklist', 'history', 'frequency', 'everyX', 'startDate', 'repeat', 'streak'])); - } else if (oldTask.type === 'todo') { - oldTask = _.pick(oldTask, allTasksFields.concat(['completed', 'collapseChecklist', 'checklist', 'date', 'dateCompleted'])); - } else if (oldTask.type === 'reward') { - oldTask = _.pick(oldTask, allTasksFields); - } else { - throw new Error('Task with no or invalid type!'); - } - - batchInsertTasks.insert(taskDefaults(oldTask)); - processedTasks++; - }); - - batchInsertUsers.insert(newUser.toObject()); - }); - - console.log(`Saving ${oldUsers.length} users and ${processedTasks} tasks.`); - - return Bluebird.all([ - batchInsertUsers.execute(), - batchInsertTasks.execute(), - ]); - }) - .then(function () { - totoalProcessedTasks += processedTasks; - processedUsers += oldUsers.length; - - console.log(`Saved ${oldUsers.length} users and their tasks.`); - console.log('Challenges\' tasks no matching id: ', challengeTaskNoMatchingId); - console.log('Challenges\' tasks with matching id: ', challengeTaskWithMatchingId); - - if (lastUser) { - return processUsers(lastUser); - } else { - return console.log('Done!'); - } - }); -} - -// Connect to the databases -Bluebird.all([ - MongoClient.connect(MONGODB_OLD), - MongoClient.connect(MONGODB_NEW), -]) -.then(function (result) { - var oldInstance = result[0]; - var newInstance = result[1]; - - mongoDbOldInstance = oldInstance; - oldUserCollection = mongoDbOldInstance.collection('users'); - - mongoDbNewInstance = newInstance; - newUserCollection = mongoDbNewInstance.collection('users'); - newTaskCollection = mongoDbNewInstance.collection('tasks'); - - console.log(`Connected with MongoClient to ${MONGODB_OLD} and ${MONGODB_NEW}.`); - - return processUsers(); -}) -.catch(function (err) { - console.error(err.stack || err); -}); diff --git a/migrations/apology_gems.js b/migrations/apology_gems.js deleted file mode 100644 index e22a428047..0000000000 --- a/migrations/apology_gems.js +++ /dev/null @@ -1 +0,0 @@ -db.users.update({_id:{$in:['']}},{$inc:{balance:0.5}},{multi:true}); \ No newline at end of file diff --git a/migrations/archive/2013/20130128_add_missing_crons.js b/migrations/archive/2013/20130128_add_missing_crons.js new file mode 100644 index 0000000000..5a28b83e69 --- /dev/null +++ b/migrations/archive/2013/20130128_add_missing_crons.js @@ -0,0 +1,5 @@ +db.users.update( + { lastCron: { $exists: false} }, + { $set: { lastCron: Number(new Date()) } }, + { multi: true } +); \ No newline at end of file diff --git a/migrations/archive/2013/20130128_merge_completed_todo_ids.js b/migrations/archive/2013/20130128_merge_completed_todo_ids.js new file mode 100644 index 0000000000..6b3f3d39af --- /dev/null +++ b/migrations/archive/2013/20130128_merge_completed_todo_ids.js @@ -0,0 +1,15 @@ +db.users.find({ completedIds: { $exists: true } }).forEach(function (user) { + let 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 }, + } + ); +}); \ No newline at end of file diff --git a/migrations/archive/2013/20130129_add_missing_preferences.js b/migrations/archive/2013/20130129_add_missing_preferences.js new file mode 100644 index 0000000000..d2710a5dbf --- /dev/null +++ b/migrations/archive/2013/20130129_add_missing_preferences.js @@ -0,0 +1,5 @@ +db.users.update( + {preferences: {$exists: false}}, + {$set: {preferences: {gender: 'm', armorSet: 'v1'}}}, + {multi: true} +); diff --git a/migrations/20130204_count_habits.js b/migrations/archive/2013/20130204_count_habits.js similarity index 55% rename from migrations/20130204_count_habits.js rename to migrations/archive/2013/20130204_count_habits.js index 172bee9e60..b5e3b50d6c 100644 --- a/migrations/20130204_count_habits.js +++ b/migrations/archive/2013/20130204_count_habits.js @@ -7,14 +7,14 @@ // 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} } ]}; +let 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; -}) +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; +}); diff --git a/migrations/archive/2013/20130204_user_public_private_paths.js b/migrations/archive/2013/20130204_user_public_private_paths.js new file mode 100644 index 0000000000..698187985c --- /dev/null +++ b/migrations/archive/2013/20130204_user_public_private_paths.js @@ -0,0 +1,101 @@ +// %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) { + let 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 + let 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 || Number(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); + } +}); \ No newline at end of file diff --git a/migrations/archive/2013/20130208_idLists_to_typeIds.js b/migrations/archive/2013/20130208_idLists_to_typeIds.js new file mode 100644 index 0000000000..ecf13fb341 --- /dev/null +++ b/migrations/archive/2013/20130208_idLists_to_typeIds.js @@ -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 + } + ); +}); \ No newline at end of file diff --git a/migrations/archive/2013/20130208_user_customizations.js b/migrations/archive/2013/20130208_user_customizations.js new file mode 100644 index 0000000000..93dee7aaf2 --- /dev/null +++ b/migrations/archive/2013/20130208_user_customizations.js @@ -0,0 +1,18 @@ +db.users.update( + {items: {$exists: 0}}, + {$set: {items: {weapon: 0, armor: 0, head: 0, shield: 0 }}}, + {multi: true} +); + +db.users.find().forEach(function (user) { + let 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}); +}); \ No newline at end of file diff --git a/migrations/archive/2013/20130307_exp_overflow.js b/migrations/archive/2013/20130307_exp_overflow.js new file mode 100644 index 0000000000..113a8989b2 --- /dev/null +++ b/migrations/archive/2013/20130307_exp_overflow.js @@ -0,0 +1,37 @@ +// 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) { + let 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) { + let 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); + } + } +}); \ No newline at end of file diff --git a/migrations/archive/2013/20130307_normalize_algo_values.js b/migrations/archive/2013/20130307_normalize_algo_values.js new file mode 100644 index 0000000000..a6f96754cc --- /dev/null +++ b/migrations/archive/2013/20130307_normalize_algo_values.js @@ -0,0 +1,46 @@ +// 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); + } +}); \ No newline at end of file diff --git a/migrations/archive/2013/20130307_remove_duff_histories.js b/migrations/archive/2013/20130307_remove_duff_histories.js new file mode 100644 index 0000000000..c2971df4ae --- /dev/null +++ b/migrations/archive/2013/20130307_remove_duff_histories.js @@ -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 Boolean(h.value); + }); + } + }); + + try { + db.users.update( + {_id: user._id}, + {$set: + { + tasks: user.tasks, + }, + }, + {multi: true} + ); + } catch (e) { + print(e); + } +}); \ No newline at end of file diff --git a/migrations/archive/2013/20130326_migrate_pets.js b/migrations/archive/2013/20130326_migrate_pets.js new file mode 100644 index 0000000000..57c6ad7534 --- /dev/null +++ b/migrations/archive/2013/20130326_migrate_pets.js @@ -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 + +let 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) { + let 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) { + let 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); + } +}); \ No newline at end of file diff --git a/migrations/archive/2013/20130327_apply_tokens.js b/migrations/archive/2013/20130327_apply_tokens.js new file mode 100644 index 0000000000..5a6a519b28 --- /dev/null +++ b/migrations/archive/2013/20130327_apply_tokens.js @@ -0,0 +1,109 @@ +/** + * 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 + +let 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; + + let 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) { + let userInTier = !_.isEmpty(_.intersection(tier.users, possibleUserIds)); + if (userInTier) { + let 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, 'flags.ads': 'hide' }, + $inc: { balance: tokenInc / 4 }, + } + ); + } catch (e) { + print(e); + } + } + }); +}); \ No newline at end of file diff --git a/migrations/archive/2013/20130503_max_gear_achievement.js b/migrations/archive/2013/20130503_max_gear_achievement.js new file mode 100644 index 0000000000..44cfe0a877 --- /dev/null +++ b/migrations/archive/2013/20130503_max_gear_achievement.js @@ -0,0 +1,23 @@ +/** + * 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) { + let 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); + } + } +}); \ No newline at end of file diff --git a/migrations/20130507_fix_broken_tags.js b/migrations/archive/2013/20130507_fix_broken_tags.js similarity index 58% rename from migrations/20130507_fix_broken_tags.js rename to migrations/archive/2013/20130507_fix_broken_tags.js index 1818442acd..9005a12dc4 100644 --- a/migrations/20130507_fix_broken_tags.js +++ b/migrations/archive/2013/20130507_fix_broken_tags.js @@ -4,9 +4,8 @@ * 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:[]}}); - } - -}) \ No newline at end of file +db.users.find().forEach(function (user) { + if (!_.isArray(user.tags)) { + db.users.update({_id: user._id}, {$set: {tags: []}}); + } +}); \ No newline at end of file diff --git a/migrations/archive/2013/20130508_add_backer_pets.js b/migrations/archive/2013/20130508_add_backer_pets.js new file mode 100644 index 0000000000..c6541580e7 --- /dev/null +++ b/migrations/archive/2013/20130508_add_backer_pets.js @@ -0,0 +1 @@ +db.users.update({'backer.tier': {$gte: 80}}, {$push: {'items.pets': 'Wolf-Cerberus'}}, {multi: true}); \ No newline at end of file diff --git a/migrations/20130508_fix_duff_party_subscriptions.js b/migrations/archive/2013/20130508_fix_duff_party_subscriptions.js similarity index 51% rename from migrations/20130508_fix_duff_party_subscriptions.js rename to migrations/archive/2013/20130508_fix_duff_party_subscriptions.js index 918e323e42..c6c53a3e79 100644 --- a/migrations/20130508_fix_duff_party_subscriptions.js +++ b/migrations/archive/2013/20130508_fix_duff_party_subscriptions.js @@ -13,29 +13,28 @@ // 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.ensureIndex({ members: 1}, {background: true}); -db.parties.find().forEach(function(party){ +db.parties.find().forEach(function (party) { + if (!party.members) { + return db.parties.remove({_id: party._id}); + } - 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) { + let i = party.members.indexOf(user._id); + party.members.splice(i, 1); } - // 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}); - // 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}}); - } - }) -}) + // else just set it + } else { + db.parties.update({_id: party._id}, {$set: {members: party.members}}); + } + }); +}); diff --git a/migrations/20130518_setup_groups.js b/migrations/archive/2013/20130518_setup_groups.js similarity index 54% rename from migrations/20130518_setup_groups.js rename to migrations/archive/2013/20130518_setup_groups.js index 0e04bfa1b9..3240c1f809 100644 --- a/migrations/20130518_setup_groups.js +++ b/migrations/archive/2013/20130518_setup_groups.js @@ -15,34 +15,34 @@ * 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.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}); +db.groups.update({}, {$set: {type: 'party'}}, {multi: true}); -//migrate invitation mechanisms +// migrate invitation mechanisms db.users.update( - {}, - { - $remove:{party:1}, - $set:{invitations:{party:null,guilds:[]}} - }, - {multi:1} + {}, + { + $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 +// 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: [] - } + _id: 'habitrpg', + leader: '9', + type: 'guild', + name: 'HabitRPG', + chat: tavern.messages, + info: { + blurb: '', + websites: [], + }, }); \ No newline at end of file diff --git a/migrations/archive/2013/20130602_survey_rewards.js b/migrations/archive/2013/20130602_survey_rewards.js new file mode 100644 index 0000000000..4c819c6e2e --- /dev/null +++ b/migrations/archive/2013/20130602_survey_rewards.js @@ -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 + +let members = []; +members = _.uniq(members); + +let 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} +); \ No newline at end of file diff --git a/migrations/archive/2013/20130612_survey_rewards_individual.js b/migrations/archive/2013/20130612_survey_rewards_individual.js new file mode 100644 index 0000000000..3a2e1fbed4 --- /dev/null +++ b/migrations/archive/2013/20130612_survey_rewards_individual.js @@ -0,0 +1,9 @@ +// mongo habitrpg migrations/20130612_survey_rewards_individual.js + +let query = {_id: ''}; + +db.users.update(query, + { + $set: { 'achievements.helpedHabit': true }, + $inc: { balance: 2.5 }, + }); \ No newline at end of file diff --git a/migrations/archive/2013/20130615_add_extra_indexes.js b/migrations/archive/2013/20130615_add_extra_indexes.js new file mode 100644 index 0000000000..233f266835 --- /dev/null +++ b/migrations/archive/2013/20130615_add_extra_indexes.js @@ -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}); \ No newline at end of file diff --git a/migrations/archive/2013/20130908_cleanup_corrupt_tags.js b/migrations/archive/2013/20130908_cleanup_corrupt_tags.js new file mode 100644 index 0000000000..8fdb6f9f54 --- /dev/null +++ b/migrations/archive/2013/20130908_cleanup_corrupt_tags.js @@ -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); + } +}); diff --git a/migrations/20130908_cleanup_derby_corruption.js b/migrations/archive/2013/20130908_cleanup_derby_corruption.js similarity index 61% rename from migrations/20130908_cleanup_derby_corruption.js rename to migrations/archive/2013/20130908_cleanup_derby_corruption.js index 6cac534587..d25eea6de9 100644 --- a/migrations/20130908_cleanup_derby_corruption.js +++ b/migrations/archive/2013/20130908_cleanup_derby_corruption.js @@ -1,4 +1,4 @@ -//mongo habitrpg ./node_modules/lodash/lodash.js migrations/20130908_cleanup_derby_corruption.js +// 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 @@ -9,49 +9,48 @@ // 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){ - +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) { + user.tasks = _.reduce(user.tasks, function (m, task, k) { if (!task || !task.id) return m; - if (isNaN(+task.value)) task.value = 0; + if (isNaN(Number(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; + _.each(user.stats, function (v, k) { + if (!v || isNaN(Number(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) { + ['habit', 'daily', 'todo', 'reward'].forEach(function (type) { + let idList = user[`${type }Ids`]; + let taskIds = _.pluck(_.where(user.tasks, {type}), 'id'); + let union = _.union(idList, taskIds); + let preened = _.filter(union, function (id) { return id && _.contains(taskIds, id); }); if (!_.isEqual(idList, preened)) { - user[type + "Ids"] = 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){ + 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) { + db.users.update({_id: user._id}, user); + } catch (e) { print(e); } -}) +}); diff --git a/migrations/20130908_remove_staged_users.js b/migrations/archive/2013/20130908_remove_staged_users.js similarity index 86% rename from migrations/20130908_remove_staged_users.js rename to migrations/archive/2013/20130908_remove_staged_users.js index 7bf75c0096..a9b575c5dd 100644 --- a/migrations/20130908_remove_staged_users.js +++ b/migrations/archive/2013/20130908_remove_staged_users.js @@ -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', }); \ No newline at end of file diff --git a/migrations/archive/2013/20131022_purchased_and_newStuff.js b/migrations/archive/2013/20131022_purchased_and_newStuff.js new file mode 100644 index 0000000000..0de25a7fe1 --- /dev/null +++ b/migrations/archive/2013/20131022_purchased_and_newStuff.js @@ -0,0 +1,5 @@ +db.users.find().forEach(function (user) { + if (!user.purchased) user.purchased = {hair: {}, skin: {}}; + user.purchased.ads = user.flags && Boolean(user.flags.ads); + db.users.update({_id: user._id}, {$set: {purchased: user.purchased, 'flags.newStuff': true}, $unset: {'flags.ads': 1}}); +}); \ No newline at end of file diff --git a/migrations/archive/2013/20131022_restore_ads.js b/migrations/archive/2013/20131022_restore_ads.js new file mode 100644 index 0000000000..141d6ea1cd --- /dev/null +++ b/migrations/archive/2013/20131022_restore_ads.js @@ -0,0 +1,12 @@ +// node .migrations/20131022_restore_ads.js +let mongo = require('mongoskin'); +let _ = require('lodash'); +let dbBackup = mongo.db('localhost:27017/habitrpg?auto_reconnect'); +let dbLive = mongo.db('localhost:27017/habitrpg2?auto_reconnect'); +let count = 89474; +dbBackup.collection('users').findEach({$or: [{'flags.ads': 'show'}, {'flags.ads': null}]}, {batchSize: 10}, function (err, item) { + if (err) return console.error({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!'); +}); \ No newline at end of file diff --git a/migrations/20131028_task_subdocs_tags_invites.js b/migrations/archive/2013/20131028_task_subdocs_tags_invites.js similarity index 51% rename from migrations/20131028_task_subdocs_tags_invites.js rename to migrations/archive/2013/20131028_task_subdocs_tags_invites.js index f4a8b720c2..7e4c38b286 100644 --- a/migrations/20131028_task_subdocs_tags_invites.js +++ b/migrations/archive/2013/20131028_task_subdocs_tags_invites.js @@ -3,7 +3,7 @@ // 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 +// 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 @@ -11,28 +11,27 @@ // 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){ - +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) { + 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}}); + 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]; + _.each(user.tasks, function (task) { + _.each(task.tags, function (val, key) { + _.each(user.tags, function (tag) { + if (key === tag.id) delete task.tags[key]; }); }); }); @@ -40,25 +39,25 @@ db.users.find().forEach(function(user){ // Fix corrupt dates // ------------------------- user.lastCron = new Date(user.lastCron); - if (user.lastCron == 'Invalid Date') user.lastCron = new Date(); + 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: 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:[]}); + _.defaults(user, {history: {}}); + _.defaults(user.history, {exp: [], todos: []}); // Add username // ------------------------- - if (!user.profile) user.profile = {name:undefined}; + if (!user.profile) user.profile = {name: undefined}; if (_.isEmpty(user.profile.name) && user.auth) { - var fb = user.auth.facebook; + let 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))) || + user.auth.local && user.auth.local.username || + fb && (fb.displayName || fb.name || fb.username || fb.first_name && `${fb.first_name } ${ fb.last_name}`) || 'Anonymous'; } @@ -71,62 +70,62 @@ db.users.find().forEach(function(user){ print(user._id); } } else { - _.each(['habit', 'daily', 'todo', 'reward'], function(type) { + _.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], + user[`${type }s`] = _.reduce(user[`${type }Ids`], function (m, tid) { + let 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 : ''; + 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 : '!'; + 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 : []; + 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 : []; + 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; + newTask.completed = _.isBoolean(task.completed) ? task.completed : false; break; } m.push(newTask); return m; }, []); - delete user[type + 'Ids']; + delete user[`${type }Ids`]; }); delete user.tasks; } try { - db.users.update({_id:user._id}, user); - } catch(e) { + 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} - }) +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'}}); \ No newline at end of file +db.groups.update({_id: 'habitrpg'}, {$set: {name: 'Tavern'}}); \ No newline at end of file diff --git a/migrations/archive/2013/20131102_restore_task_ids.js b/migrations/archive/2013/20131102_restore_task_ids.js new file mode 100644 index 0000000000..89361a4777 --- /dev/null +++ b/migrations/archive/2013/20131102_restore_task_ids.js @@ -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); + } +}); + diff --git a/migrations/archive/2013/20131104_remove_invalid_dues.js b/migrations/archive/2013/20131104_remove_invalid_dues.js new file mode 100644 index 0000000000..696a18004e --- /dev/null +++ b/migrations/archive/2013/20131104_remove_invalid_dues.js @@ -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}}); +}); \ No newline at end of file diff --git a/migrations/20131104_restore_lost_task_data.js b/migrations/archive/2013/20131104_restore_lost_task_data.js similarity index 55% rename from migrations/20131104_restore_lost_task_data.js rename to migrations/archive/2013/20131104_restore_lost_task_data.js index e77f57de3f..26b8692401 100644 --- a/migrations/20131104_restore_lost_task_data.js +++ b/migrations/archive/2013/20131104_restore_lost_task_data.js @@ -5,8 +5,8 @@ * 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'); +let mongo = require('mongoskin'); +let _ = 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 @@ -14,47 +14,47 @@ var _ = require('lodash'); // 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'); +let backupUsers = mongo.db('localhost:27017/habitrpg_old?auto_reconnect').collection('users'); +let liveUsers = mongo.db('localhost:27017/habitrpg_new?auto_reconnect').collection('users'); -backupUsers.count(function(err, count){ +backupUsers.count(function (err, count) { if (err) return console.error(err); - backupUsers.findEach({}, {batchSize:250}, function(err, before){ + 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){ + liveUsers.findOne({_id: before._id}, function (err, after) { if (err) return console.error(err); if (!after) { count--; - return console.log(before._id + ' deleted?'); + 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 (before._id === '9') console.log('lefnire processed'); + _.each(before.tasks, function (tBefore) { + let 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]; - }) + _.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 (Number(tBefore.streak) > tAfter.streak) + tAfter.streak = Number(tBefore.streak); - if (!!tBefore.date && !tAfter.date) + if (Boolean(tBefore.date) && !tAfter.date) tAfter.date = tBefore.date; // Restore deleted values - if (+tBefore.value != 0 && tAfter.value == 0) - tAfter.value = +tBefore.value; - }) + if (Number(tBefore.value) != 0 && tAfter.value === 0) + tAfter.value = Number(tBefore.value); + }); after._v++; - liveUsers.update({_id:after._id}, after); - if (--count <= 0) console.log("DONE!"); - }) + liveUsers.update({_id: after._id}, after); + if (--count <= 0) console.log('DONE!'); + }); }); }); \ No newline at end of file diff --git a/migrations/archive/2013/20131105_remove_history_ids.js b/migrations/archive/2013/20131105_remove_history_ids.js new file mode 100644 index 0000000000..c7e09a334a --- /dev/null +++ b/migrations/archive/2013/20131105_remove_history_ids.js @@ -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}}); +}); \ No newline at end of file diff --git a/migrations/20131107_from_backer_to_contributor.js b/migrations/archive/2013/20131107_from_backer_to_contributor.js similarity index 63% rename from migrations/20131107_from_backer_to_contributor.js rename to migrations/archive/2013/20131107_from_backer_to_contributor.js index d5a4ee851b..1c083ad6ff 100644 --- a/migrations/20131107_from_backer_to_contributor.js +++ b/migrations/archive/2013/20131107_from_backer_to_contributor.js @@ -1,9 +1,9 @@ db.users.find({ $or: [ - {'backer.admin':{$exists:1}}, - {'backer.contributor':{$exists:1}} - ] -},{backer:1}).forEach(function(user){ + {'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; @@ -14,5 +14,5 @@ db.users.find({ delete user.backer.contributor; } - db.users.update({_id:user._id}, {$set:{backer:user.backer, contributor:user.contributor}}); + db.users.update({_id: user._id}, {$set: {backer: user.backer, contributor: user.contributor}}); }); \ No newline at end of file diff --git a/migrations/archive/2013/20131108_add_gems_for_contribs.js b/migrations/archive/2013/20131108_add_gems_for_contribs.js new file mode 100644 index 0000000000..c05c88c4ec --- /dev/null +++ b/migrations/archive/2013/20131108_add_gems_for_contribs.js @@ -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 * 0.5} }); +}); \ No newline at end of file diff --git a/migrations/20131109_refactor_pets.js b/migrations/archive/2013/20131109_refactor_pets.js similarity index 61% rename from migrations/20131109_refactor_pets.js rename to migrations/archive/2013/20131109_refactor_pets.js index f5d9f7d567..2d73442077 100644 --- a/migrations/20131109_refactor_pets.js +++ b/migrations/archive/2013/20131109_refactor_pets.js @@ -1,10 +1,11 @@ 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){ - + {$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;}, {}); + user.items.pets = _.reduce(user.items.pets, function (m, v) { + m[v] = 5; return m; + }, {}); } if (!_.isString(user.items.currentPet)) { @@ -12,7 +13,7 @@ db.users.find( } if (_.isArray(user.items.eggs)) { - user.items.eggs = _.reduce(user.items.eggs, function(m,v){ + user.items.eggs = _.reduce(user.items.eggs, function (m, v) { if (!m[v.name]) m[v.name] = 0; m[v.name]++; return m; @@ -20,7 +21,7 @@ db.users.find( } if (_.isArray(user.items.hatchingPotions)) { - user.items.hatchingPotions = _.reduce(user.items.hatchingPotions, function(m,v){ + user.items.hatchingPotions = _.reduce(user.items.hatchingPotions, function (m, v) { if (!m[v]) m[v] = 0; m[v]++; return m; @@ -35,5 +36,5 @@ db.users.find( user.items.mounts['LionCub-Ethereal'] = true; } - db.users.update({_id:user._id}, {$set:{items:user.items}}); + db.users.update({_id: user._id}, {$set: {items: user.items}}); }); diff --git a/migrations/archive/2013/20131111_task_NaN.js b/migrations/archive/2013/20131111_task_NaN.js new file mode 100644 index 0000000000..e4df7c5ca6 --- /dev/null +++ b/migrations/archive/2013/20131111_task_NaN.js @@ -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 = Number(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}}); +}); \ No newline at end of file diff --git a/migrations/archive/2013/20131114_migrate_websites_to_blurb.js b/migrations/archive/2013/20131114_migrate_websites_to_blurb.js new file mode 100644 index 0000000000..f398be79a2 --- /dev/null +++ b/migrations/archive/2013/20131114_migrate_websites_to_blurb.js @@ -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}, + }); +}); diff --git a/migrations/archive/2013/20131115_update_gear_preferences.js b/migrations/archive/2013/20131115_update_gear_preferences.js new file mode 100644 index 0000000000..d6bb363762 --- /dev/null +++ b/migrations/archive/2013/20131115_update_gear_preferences.js @@ -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} +); diff --git a/migrations/archive/2013/20131117_fix_task_types.js b/migrations/archive/2013/20131117_fix_task_types.js new file mode 100644 index 0000000000..570f315bb1 --- /dev/null +++ b/migrations/archive/2013/20131117_fix_task_types.js @@ -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}}); +}); diff --git a/migrations/archive/2013/20131117_remove_undefined_pets.js b/migrations/archive/2013/20131117_remove_undefined_pets.js new file mode 100644 index 0000000000..bcfbe3d216 --- /dev/null +++ b/migrations/archive/2013/20131117_remove_undefined_pets.js @@ -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} }); +}); diff --git a/migrations/archive/2013/20131122_deleted_tags.js b/migrations/archive/2013/20131122_deleted_tags.js new file mode 100644 index 0000000000..0ca01616dc --- /dev/null +++ b/migrations/archive/2013/20131122_deleted_tags.js @@ -0,0 +1,13 @@ +// Cleanup broken tags +// ------------------------- +db.users.find().forEach(function (user) { + let 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); +}); \ No newline at end of file diff --git a/migrations/archive/2013/20131123_set_default_party_order.js b/migrations/archive/2013/20131123_set_default_party_order.js new file mode 100644 index 0000000000..07a222035b --- /dev/null +++ b/migrations/archive/2013/20131123_set_default_party_order.js @@ -0,0 +1,8 @@ +// Add default to randomize party members list +db.users.update( + {}, + {$set: { + 'party.order': 'random', + }}, + {multi: true} +); diff --git a/migrations/archive/2013/20131126_clean_dayStart.js b/migrations/archive/2013/20131126_clean_dayStart.js new file mode 100644 index 0000000000..12a0eb0b8f --- /dev/null +++ b/migrations/archive/2013/20131126_clean_dayStart.js @@ -0,0 +1,5 @@ +db.users.find({'preferences.dayStart': {$exists: 1}}, {'preferences.dayStart': 1}).forEach(function (user) { + let dayStart = Number(user.preferences.dayStart); + dayStart = _.isNaN(dayStart) || dayStart < 0 || dayStart > 24 ? 0 : dayStart; + db.users.update({_id: user._id}, {$set: {'preferences.dayStart': dayStart}}); +}); diff --git a/migrations/archive/2013/20131126_turkey_pet.js b/migrations/archive/2013/20131126_turkey_pet.js new file mode 100644 index 0000000000..f93848e926 --- /dev/null +++ b/migrations/archive/2013/20131126_turkey_pet.js @@ -0,0 +1 @@ +db.users.update({}, {$set: {'items.pets.Turkey-Base': 5, 'flags.newStuff': true}}, {multi: true}); \ No newline at end of file diff --git a/migrations/archive/2013/20131127_restore_dayStart.js b/migrations/archive/2013/20131127_restore_dayStart.js new file mode 100644 index 0000000000..e23bc00b59 --- /dev/null +++ b/migrations/archive/2013/20131127_restore_dayStart.js @@ -0,0 +1,42 @@ +// 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 + +let mongo = require('mongoskin'); +let _ = require('lodash'); + +let backupUsers = mongo.db('localhost:27017/habitrpg_old?auto_reconnect').collection('users'); +let liveUsers = mongo.db('localhost:27017/habitrpg_new?auto_reconnect').collection('users'); + +let query = {'preferences.dayStart': {$exists: 1, $ne: 0}}; +let 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.findOne({_id: before._id}, function (err, after) { + if (err) return console.error(err); + if (!after) { + count--; return console.log(`${before._id } deleted?`); + } + + let dayStart = Number(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!'); + }); + }); +}); \ No newline at end of file diff --git a/migrations/20131214_classes.coffee b/migrations/archive/2013/20131214_classes.coffee similarity index 100% rename from migrations/20131214_classes.coffee rename to migrations/archive/2013/20131214_classes.coffee diff --git a/migrations/20131217_unearned_backer_gear.js b/migrations/archive/2013/20131217_unearned_backer_gear.js similarity index 51% rename from migrations/20131217_unearned_backer_gear.js rename to migrations/archive/2013/20131217_unearned_backer_gear.js index 2c1baf9dc7..dd010912e5 100644 --- a/migrations/20131217_unearned_backer_gear.js +++ b/migrations/archive/2013/20131217_unearned_backer_gear.js @@ -1,20 +1,20 @@ -var query = { - '$or': [ +let 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} - ] + {'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; +db.users.find(query, {'items.gear.owned': 1, backer: 1}).forEach(function (user) { + let owned = user.items.gear.owned; + let 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}}); + db.users.update({_id: user._id}, {$set: {'items.gear.owned': owned}}); }); diff --git a/migrations/archive/2013/20131221_restore_NaN_history.js b/migrations/archive/2013/20131221_restore_NaN_history.js new file mode 100644 index 0000000000..babb7c3a9e --- /dev/null +++ b/migrations/archive/2013/20131221_restore_NaN_history.js @@ -0,0 +1,55 @@ +// 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 + */ +let mongo = require('mongoskin'); +let _ = require('lodash'); + +let backupUsers = mongo.db('localhost:27017/habitrpg_old?auto_reconnect').collection('users'); +let liveUsers = mongo.db('localhost:27017/habitrpg?auto_reconnect').collection('users'); + +function filterNaNs (h) { + return h && _.isNumber(Number(h.value)) && !_.isNaN(Number(h.value)); +} + +let fields = {history: 1, habits: 1, dailys: 1, migration: 1}; +let 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.findOne({_id: 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); + let 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'); + }); +}); \ No newline at end of file diff --git a/migrations/archive/2013/20131225_restore_streaks.js b/migrations/archive/2013/20131225_restore_streaks.js new file mode 100644 index 0000000000..e161ee1c91 --- /dev/null +++ b/migrations/archive/2013/20131225_restore_streaks.js @@ -0,0 +1,42 @@ +// 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 + */ +let mongo = require('mongoskin'); +let _ = require('lodash'); + +let backupUsers = mongo.db('localhost:27017/habitrpg_old?auto_reconnect').collection('users'); +let liveUsers = mongo.db('lefnire:mAdn3s5s@charlotte.mongohq.com:10015/habitrpg_large?auto_reconnect').collection('users'); + +let fields = {dailys: 1, migration: 1}; +let 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.findOne({_id: after._id}, fields, function (err, before) { + if (!before) err = '!before'; + if (err) { + count++; return console.error(err); + } + + _.each(before.dailys, function (d) { + let 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'); + }); +}); \ No newline at end of file diff --git a/migrations/archive/2014/20140119_task_creation_completion_dates.js b/migrations/archive/2014/20140119_task_creation_completion_dates.js new file mode 100644 index 0000000000..9414117cd8 --- /dev/null +++ b/migrations/archive/2014/20140119_task_creation_completion_dates.js @@ -0,0 +1,8 @@ +db.users.find({}, {todos: 1, dailys: 1, rewards: 1, habits: 1}).forEach(function (user) { + _.each(user.habits.concat(user.dailys).concat(user.todos).concat(user.rewards), function (t) { + t.dateCreated = t.created || new Date(); + delete t.created; + if (t.type === 'todo' && t.completed) t.dateCompleted = new Date(); + }); + db.users.update({_id: user._id}, {$set: {habits: user.habits, dailys: user.dailys, todos: user.todos, rewards: user.rewards}}); +}); diff --git a/migrations/archive/2014/20140130_birthdayEnd.js b/migrations/archive/2014/20140130_birthdayEnd.js new file mode 100644 index 0000000000..4c235d52eb --- /dev/null +++ b/migrations/archive/2014/20140130_birthdayEnd.js @@ -0,0 +1 @@ +db.users.update({}, {$set: {'achievements.habitBirthday': true}}, {multi: 1}); diff --git a/migrations/archive/2014/20140130_birthdayStart.js b/migrations/archive/2014/20140130_birthdayStart.js new file mode 100644 index 0000000000..93283c720b --- /dev/null +++ b/migrations/archive/2014/20140130_birthdayStart.js @@ -0,0 +1,12 @@ +db.users.update({}, {$set: { + 'items.food.Cake_Skeleton': 1, + 'items.food.Cake_Base': 1, + 'items.food.Cake_CottonCandyBlue': 1, + 'items.food.Cake_CottonCandyPink': 1, + 'items.food.Cake_Shade': 1, + 'items.food.Cake_White': 1, + 'items.food.Cake_Golden': 1, + 'items.food.Cake_Zombie': 1, + 'items.food.Cake_Desert': 1, + 'items.food.Cake_Red': 1, +}}, {multi: 1}); diff --git a/migrations/archive/2014/20140220_challenge_memberCount.js b/migrations/archive/2014/20140220_challenge_memberCount.js new file mode 100644 index 0000000000..4444165e86 --- /dev/null +++ b/migrations/archive/2014/20140220_challenge_memberCount.js @@ -0,0 +1,3 @@ +db.challenges.find({}, {members: 1}).forEach(function (chal) { + db.challenges.update({_id: chal._id}, {$set: {memberCount: chal.members.length}}); +}); diff --git a/migrations/archive/2014/20140301_missing_mysteries.js b/migrations/archive/2014/20140301_missing_mysteries.js new file mode 100644 index 0000000000..f5fb4019ed --- /dev/null +++ b/migrations/archive/2014/20140301_missing_mysteries.js @@ -0,0 +1,14 @@ +db.users.update( + { + 'purchased.plan.dateCreated': {$gte: new Date('2014-02-22'), $lt: new Date('2014-02-29')}, + 'items.gear.owned.armor_mystery_201402': null, + 'items.gear.owned.head_mystery_201402': null, + 'items.gear.owned.back_mystery_201402': null, + 'purchased.plan.mysteryItems': {$nin: ['armor_mystery_201402', 'head_mystery_201402', 'back_mystery_201402']}, + }, + // {_id:1,'purchased.plan':1,'items.gear.owned':1} + {$push: {'purchased.plan.mysteryItems': {$each: ['armor_mystery_201402', 'head_mystery_201402', 'back_mystery_201402']}}}, + {multi: true} +);/* .forEach(function(user){ + printjson(user); + });*/ diff --git a/migrations/archive/2014/20140610_missing_backer_mount.js b/migrations/archive/2014/20140610_missing_backer_mount.js new file mode 100644 index 0000000000..6b678d5869 --- /dev/null +++ b/migrations/archive/2014/20140610_missing_backer_mount.js @@ -0,0 +1 @@ +db.users.update({'backer.tier': {$gt: 69}}, {$set: {'items.mounts.LionCub-Ethereal': true}}, {multi: 1}); \ No newline at end of file diff --git a/migrations/archive/2014/20140712_wiped_quest_membership.js b/migrations/archive/2014/20140712_wiped_quest_membership.js new file mode 100644 index 0000000000..8ccd44bb40 --- /dev/null +++ b/migrations/archive/2014/20140712_wiped_quest_membership.js @@ -0,0 +1,11 @@ +// mongo habitrpg node_modules/lodash/lodash.js ./migrations/20140712_wiped_quest_membership.js +db.groups.find({type: 'party', 'quest.key': {$ne: null}, 'quest.active': true}, {quest: 1}).forEach(function (group) { + let activeMembers = _.reduce(group.quest.members, function (m, v, k) { + if (v === true) m.push(k); return m; + }, []); + db.users.update( + {_id: {$in: activeMembers}}, + {$set: {'party.quest.key': group.quest.key, 'party.quest.completed': null}}, + {multi: true} + ); +}); diff --git a/migrations/archive/2014/20140803_remove_undefined_notifications.js b/migrations/archive/2014/20140803_remove_undefined_notifications.js new file mode 100644 index 0000000000..1033244adc --- /dev/null +++ b/migrations/archive/2014/20140803_remove_undefined_notifications.js @@ -0,0 +1,13 @@ +let _ = require('lodash'); + +db.users.find({}).forEach(function (user) { + let newNewMessages = {}; + + _.each(user.newMessages, function (val, key) { + if (key != 'undefined') { + newNewMessages[key] = val; + } + }); + + db.users.update({_id: user._id}, {$set: {newMessages: newNewMessages}}); +}); diff --git a/migrations/20140823_remove_undefined_and_false_notifications.js b/migrations/archive/2014/20140823_remove_undefined_and_false_notifications.js similarity index 53% rename from migrations/20140823_remove_undefined_and_false_notifications.js rename to migrations/archive/2014/20140823_remove_undefined_and_false_notifications.js index a6eed2b838..5f949e4399 100644 --- a/migrations/20140823_remove_undefined_and_false_notifications.js +++ b/migrations/archive/2014/20140823_remove_undefined_and_false_notifications.js @@ -1,9 +1,9 @@ // node .migrations/20140823_remove_undefined_and_false_notifications.js -var migrationName = '20140823_remove_undefined_and_false_notifications'; +let migrationName = '20140823_remove_undefined_and_false_notifications'; -var authorName = 'Alys'; // in case script author needs to know when their ... -var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done +let authorName = 'Alys'; // in case script author needs to know when their ... +let authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; // ... own data is done // IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. // We've now upgraded to lodash v4 but the code used in this migration has not been @@ -15,8 +15,8 @@ var authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; //... own data is done * https://github.com/HabitRPG/habitrpg/pull/3907 */ -var mongo = require('mongoskin'); -var _ = require('lodash'); +let mongo = require('mongoskin'); +let _ = require('lodash'); // XXX @lefnire, choose wisely: // var liveUsers = mongo.db('lefnire:mAdn3s5s@charlotte.mongohq.com:10015/habitrpg_large?auto_reconnect').collection('users'); @@ -26,26 +26,28 @@ var _ = require('lodash'); // var liveUsers = mongo.db('localhost:27017/habitrpg?auto_reconnect').collection('users'); -var fields = {migration:1,newMessages:1}; -var progressCount = 1000; +let fields = {migration: 1, newMessages: 1}; +let progressCount = 1000; // var progressCount = 1; -var count = 0; -liveUsers.findEach({migration: {$ne:migrationName}}, fields, {batchSize:250}, function(err, user){ +let count = 0; +liveUsers.findEach({migration: {$ne: migrationName}}, fields, {batchSize: 250}, function (err, user) { count++; if (!user) err = '!user'; - if (err) {return console.error(err);} + if (err) { + return console.error(err); + } - var newNewMessages = {}; - _.each(user.newMessages,function(val,key){ + let newNewMessages = {}; + _.each(user.newMessages, function (val, key) { // console.log(key + " " + val.name); - if(key != "undefined" && val['value']){ + if (key != 'undefined' && val.value) { newNewMessages[key] = val; } - }) + }); - liveUsers.update({_id:user._id}, {$set:{newMessages:newNewMessages, migration:migrationName}, $inc:{_v:1}}); + liveUsers.update({_id: user._id}, {$set: {newMessages: newNewMessages, migration: migrationName}, $inc: {_v: 1}}); - if (count%progressCount == 0) console.log(count + ' ' + user._id); - if (user._id == '9') console.log('lefnire processed'); - if (user._id == authorUuid) console.log(authorName + ' processed'); + if (count % progressCount === 0) console.log(`${count } ${ user._id}`); + if (user._id === '9') console.log('lefnire processed'); + if (user._id === authorUuid) console.log(`${authorName } processed`); }); diff --git a/migrations/archive/2014/20140829_change_headAccessory_to_eyewear.js b/migrations/archive/2014/20140829_change_headAccessory_to_eyewear.js new file mode 100644 index 0000000000..4d73a951ce --- /dev/null +++ b/migrations/archive/2014/20140829_change_headAccessory_to_eyewear.js @@ -0,0 +1,83 @@ +// node .migrations/20140829_change_headAccessory_to_eyewear.js + +let migrationName = '20140829_change_headAccessory_to_eyewear'; +let authorName = 'Alys'; // in case script author needs to know when their ... +let authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; // ... own data is done + +// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. +// We've now upgraded to lodash v4 but the code used in this migration has not been +// adapted to work with it. Before this migration is used again any lodash method should +// be checked for compatibility against the v4 changelog and changed if necessary. +// https://github.com/lodash/lodash/wiki/Changelog#v400 + +/** + * https://github.com/HabitRPG/habitrpg/issues/3645 + */ +let mongo = require('mongoskin'); +let _ = require('lodash'); +let liveUsers = mongo.db('localhost:27017/habitrpg2?auto_reconnect').collection('users'); + +let fields = {migration: 1, + 'items.gear.costume.headAccessory': 1, + 'items.gear.equipped.headAccessory': 1, + 'items.gear.owned.headAccessory_special_wondercon_black': 1, + 'items.gear.owned.headAccessory_special_wondercon_red': 1, + 'items.gear.owned.headAccessory_special_summerRogue': 1, + 'items.gear.owned.headAccessory_special_summerWarrior': 1, +}; + +let progressCount = 1000; +let count = 0; +liveUsers.findEach({ $and: [ + { migration: {$ne: migrationName} }, + { $or: [ + {'items.gear.owned.headAccessory_special_summerRogue': {$exists: true}}, + {'items.gear.owned.headAccessory_special_summerWarrior': {$exists: true}}, + {'items.gear.owned.headAccessory_special_wondercon_red': {$exists: true}}, + {'items.gear.owned.headAccessory_special_wondercon_black': {$exists: true}}, + ]}, +]}, fields, {batchSize: 250}, function (err, user) { + count++; + if (!user) err = '!user'; + if (err) { + return console.error(err); + } + + let set = {migration: migrationName}; + let unset = {}; + + let oldToNew = { + headAccessory_special_summerRogue: 'eyewear_special_summerRogue', + headAccessory_special_summerWarrior: 'eyewear_special_summerWarrior', + headAccessory_special_wondercon_red: 'eyewear_special_wondercon_red', + headAccessory_special_wondercon_black: 'eyewear_special_wondercon_black', + }; + + // items.gear.costume, items.gear.equipped: + _.each(['costume', 'equipped'], function (type) { + _.each(oldToNew, function (newName, oldName) { + if (user.items.gear[type].headAccessory === oldName) { + unset[`items.gear.${type}.headAccessory`] = ''; + set[`items.gear.${type}.eyewear`] = newName; + } + }); + }); + + // items.gear.owned: + _.each(oldToNew, function (newName, oldName) { + if (oldName in user.items.gear.owned) { + unset[`items.gear.owned.${oldName}`] = ''; + set[`items.gear.owned.${newName}`] = user.items.gear.owned[oldName]; + } + }); + + // console.log(JSON.stringify(user, null, " ")); + // console.log("set: " + JSON.stringify(set, null, " ")); + // console.log("unset: " + JSON.stringify(unset, null, " ")); + + liveUsers.update({_id: user._id}, {$set: set, $unset: unset, $inc: {_v: 1}}); + + if (count % progressCount === 0) console.log(`${count } ${ user._id}`); + if (user._id === '9') console.log('lefnire processed'); + if (user._id === authorUuid) console.log(`${authorName } processed`); +}); diff --git a/migrations/archive/2014/20140831_increase_gems_for_previous_contributions.js b/migrations/archive/2014/20140831_increase_gems_for_previous_contributions.js new file mode 100644 index 0000000000..a90a85ae36 --- /dev/null +++ b/migrations/archive/2014/20140831_increase_gems_for_previous_contributions.js @@ -0,0 +1,144 @@ +// IMPORTANT: +// +// run like this to capture all output: +// +// node 20140831_increase_gems_for_previous_contributions.js > 20140831_increase_gems_for_previous_contributions_output.txt + +// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. +// We've now upgraded to lodash v4 but the code used in this migration has not been +// adapted to work with it. Before this migration is used again any lodash method should +// be checked for compatibility against the v4 changelog and changed if necessary. +// https://github.com/lodash/lodash/wiki/Changelog#v400 + +let migrationName = '20140831_increase_gems_for_previous_contributions'; + +/** + * https://github.com/HabitRPG/habitrpg/issues/3933 + * Increase Number of Gems for Contributors + * author: Alys (d904bd62-da08-416b-a816-ba797c9ee265) + * + * Increase everyone's gems per their contribution level. + * Originally they were given 2 gems per tier. + * Now they are given 3 gems per tier for tiers 1,2,3 + * and 4 gems per tier for tiers 4,5,6,7 + * So that means an EXTRA 1 for tier 1, + * 2 for tier 2, + * 3 for tier 3, + * 5 for tier 4, + * 7 for tier 5, + * 9 for tier 6, + * 11 for tier 7, + * 11 for tier 8 (moderators = tier 7 + admin privileges), + * none for tier 9 (staff) + */ + +let mongo = require('mongoskin'); +let _ = require('lodash'); + + +let dbUsers = mongo.db('localhost:27017/habitrpg?auto_reconnect').collection('users'); + + +let query = { + 'contributor.level': {$gt: 0, $lt: 9}, + migration: {$ne: migrationName}, +}; + +let fields = { + migration: 1, + 'contributor.level': 1, + balance: 1, +}; + +let userResults = {}; // each key is a UUID, each value is a string +// describing what changed for that user + +console.warn('Updating users...'); +let progressCount = 50; +let count = 0; +dbUsers.findEach(query, fields, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All users found. Fetching final balances...'); + return fetchFinalBalances(); + } + count++; + + let set = {migration: migrationName}; + + let tier = user.contributor.level; + let extraGems = tier; // tiers 1,2,3 + if (tier > 3) { + extraGems = 3 + (tier - 3) * 2; + } + if (tier === 8) { + extraGems = 11; + } + let extraBalance = extraGems / 4; + set.balance = user.balance + extraBalance; + + // Capture current state of user: + userResults[user._id] = + `${user._id } ` + ':\n' + + ` contrib tier : ${ tier }\n` + + ` balance before : ${ user.balance }\n` + + ` balance (gems) added : ${ extraBalance } (${ + extraGems })` + '\n' + + ` expected balance after: ${ user.balance + extraBalance }\n`; + + // Update user: + dbUsers.update({_id: user._id}, {$set: set, $inc: {_v: 1}}); + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); +}); + + +function fetchFinalBalances () { + let query = {_id: {$in: Object.keys(userResults)}}; + let fields = { + balance: 1, + }; + + let count1 = 0; + dbUsers.findEach(query, fields, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All final balances found.'); + return displayData(); + } + count1++; + userResults[user._id] = `${userResults[user._id] + + user._id } ` + ':\n' + + ` actual balance after : ${ user.balance }\n`; + if (count1 % progressCount === 0) console.warn(`${count1 } ${ user._id}`); + }); +} + + +function displayData () { + _.each(userResults, function (text, uuid) { + console.log(text); // text contains uuid + }); + console.log(`\n${ count + } users processed (should be roughly 335 according to the Hall)\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} diff --git a/migrations/archive/2014/20140914_upgrade_admin_contrib_tiers.js b/migrations/archive/2014/20140914_upgrade_admin_contrib_tiers.js new file mode 100644 index 0000000000..d2187d48ef --- /dev/null +++ b/migrations/archive/2014/20140914_upgrade_admin_contrib_tiers.js @@ -0,0 +1,86 @@ +let migrationName = '20140914_upgrade_admin_contrib_tiers'; +let authorName = 'Alys'; // in case script author needs to know when their ... +let authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; // ... own data is done + +/** + * https://github.com/HabitRPG/habitrpg/issues/3801 + * Convert Tier 8 contributors to Tier 9 (staff) (all current Tier 8s are admins). + * Convert Tier 7 contributors with admin flag to Tier 8 (moderators). + */ + +// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. +// We've now upgraded to lodash v4 but the code used in this migration has not been +// adapted to work with it. Before this migration is used again any lodash method should +// be checked for compatibility against the v4 changelog and changed if necessary. +// https://github.com/lodash/lodash/wiki/Changelog#v400 + +let mongo = require('mongoskin'); +let _ = require('lodash'); + +let dbUsers = mongo.db('localhost:27017/habitrpg?auto_reconnect').collection('users'); + +let query = + { 'contributor.level': {$gte: 7}, 'contributor.admin': true, migration: {$ne: migrationName} }; + +let fields = {migration: 1, + 'contributor.admin': 1, + 'contributor.level': 1, + 'auth.local.username': 1, + 'profile.name': 1, +}; + +let userResults = {}; // each key is a UUID, each value is a username; +// contains only the users changed + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + return displayData(); + } + count++; + + let set = {migration: migrationName}; + let inc = {'contributor.level': 1, _v: 1}; + + userResults[user._id] = user.profile.name; + + dbUsers.update({_id: user._id}, {$set: set, $inc: inc}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); + if (user._id === '9') console.warn('lefnire' + ' processed'); +}); + + +function displayData () { + console.log('users modified:'); + _.each(userResults, function (name, uuid) { + console.log(name); + }); + console.warn(`\n${ count + } users processed (should be 11 according to the Hall)\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + diff --git a/migrations/archive/2014/20140922_free_candy.js b/migrations/archive/2014/20140922_free_candy.js new file mode 100644 index 0000000000..c503fed034 --- /dev/null +++ b/migrations/archive/2014/20140922_free_candy.js @@ -0,0 +1,18 @@ +db.users.update( + {}, + { + $inc: { + 'items.food.Candy_Base': 1, + 'items.food.Candy_CottonCandyBlue': 1, + 'items.food.Candy_CottonCandyPink': 1, + 'items.food.Candy_Desert': 1, + 'items.food.Candy_Golden': 1, + 'items.food.Candy_Red': 1, + 'items.food.Candy_Shade': 1, + 'items.food.Candy_Skeleton': 1, + 'items.food.Candy_White': 1, + 'items.food.Candy_Zombie': 1, + }, + }, + {multi: 1} +); \ No newline at end of file diff --git a/migrations/archive/2014/20141006_jackolantern_pet.js b/migrations/archive/2014/20141006_jackolantern_pet.js new file mode 100644 index 0000000000..f55debe7ee --- /dev/null +++ b/migrations/archive/2014/20141006_jackolantern_pet.js @@ -0,0 +1 @@ +db.users.update({_id: '9'}, {$set: {'items.pets.JackOLantern-Base': 5, 'flags.newStuff': true}}, {multi: true}); \ No newline at end of file diff --git a/migrations/20141117_consecutive_months.js b/migrations/archive/2014/20141117_consecutive_months.js similarity index 54% rename from migrations/20141117_consecutive_months.js rename to migrations/archive/2014/20141117_consecutive_months.js index 7b2f418d94..6a416a7079 100644 --- a/migrations/20141117_consecutive_months.js +++ b/migrations/archive/2014/20141117_consecutive_months.js @@ -6,15 +6,15 @@ // require moment, lodash db.users.find( - {'purchased.plan.customerId':{$ne:null}}, - {_id:1, 'purchased.plan':1} -).forEach(function(user){ - var p = user.purchased.plan - , latestMonth = p.dateTerminated || new Date() // their last sub date, or on-going (now) - , count = moment(latestMonth).diff(p.dateCreated, 'months'); + {'purchased.plan.customerId': {$ne: null}}, + {_id: 1, 'purchased.plan': 1} +).forEach(function (user) { + let p = user.purchased.plan, + latestMonth = p.dateTerminated || new Date(), // their last sub date, or on-going (now) + count = moment(latestMonth).diff(p.dateCreated, 'months'); db.users.update({_id: user._id}, {$set: { 'purchased.plan.consecutive.count': count, - 'purchased.plan.consecutive.gemCapExtra': _.min([ Math.floor(count/3)*5, 25 ]), - 'purchased.plan.consecutive.trinkets': Math.floor(count/3) + 'purchased.plan.consecutive.gemCapExtra': _.min([Math.floor(count / 3) * 5, 25]), + 'purchased.plan.consecutive.trinkets': Math.floor(count / 3), }}); }); diff --git a/migrations/archive/2014/20141126_turkey_mounts.js b/migrations/archive/2014/20141126_turkey_mounts.js new file mode 100644 index 0000000000..a72e9e57a9 --- /dev/null +++ b/migrations/archive/2014/20141126_turkey_mounts.js @@ -0,0 +1,11 @@ +db.users.update( + {'items.pets.Turkey-Base': {$ne: null}}, + {$set: {'items.mounts.Turkey-Base': true}}, + {multi: 1} +); + +db.users.update( + {'items.pets.Turkey-Base': null}, + {$set: {'items.pets.Turkey-Base': 5}}, + {multi: 1} +); \ No newline at end of file diff --git a/migrations/archive/2014/20141211_NaN_consecutives.js b/migrations/archive/2014/20141211_NaN_consecutives.js new file mode 100644 index 0000000000..a718304d75 --- /dev/null +++ b/migrations/archive/2014/20141211_NaN_consecutives.js @@ -0,0 +1,4 @@ +db.users.update({'purchased.plan.consecutive.count': NaN}, {$set: {'purchased.plan.consecutive.count': 0}}, {multi: 1}); +db.users.update({'purchased.plan.consecutive.offset': NaN}, {$set: {'purchased.plan.consecutive.offset': 0}}, {multi: 1}); +db.users.update({'purchased.plan.consecutive.gemCapExtra': NaN}, {$set: {'purchased.plan.consecutive.gemCapExtra': 0}}, {multi: 1}); +db.users.update({'purchased.plan.consecutive.trinkets': NaN}, {$set: {'purchased.plan.consecutive.trinkets': 0}}, {multi: 1}); \ No newline at end of file diff --git a/migrations/archive/2014/20141230_new_years_hats.js b/migrations/archive/2014/20141230_new_years_hats.js new file mode 100644 index 0000000000..d2244135d7 --- /dev/null +++ b/migrations/archive/2014/20141230_new_years_hats.js @@ -0,0 +1,11 @@ +db.users.update( + {'items.gear.owned.head_special_nye': {$ne: null}}, + {$set: {'items.gear.owned.head_special_nye2014': false}}, + {multi: 1} +); + +db.users.update( + {'items.gear.owned.head_special_nye': null}, + {$set: {'items.gear.owned.head_special_nye': false}}, + {multi: 1} +); diff --git a/migrations/archive/2015/20150107_plan_dateUpdated_null.js b/migrations/archive/2015/20150107_plan_dateUpdated_null.js new file mode 100644 index 0000000000..865da3ed60 --- /dev/null +++ b/migrations/archive/2015/20150107_plan_dateUpdated_null.js @@ -0,0 +1,8 @@ +db.users.update( + {'purchased.plan.customerId': {$ne: null}, 'purchased.plan.dateUpdated': null}, + { + $set: {'purchased.plan.dateUpdated': new Date('12/01/2014')}, + $unset: {'purchased.plan.datedUpdated': ''}, + }, + {multi: true} +); diff --git a/migrations/archive/2015/20150124_mountmaster_fix.js b/migrations/archive/2015/20150124_mountmaster_fix.js new file mode 100644 index 0000000000..5a0bbce030 --- /dev/null +++ b/migrations/archive/2015/20150124_mountmaster_fix.js @@ -0,0 +1,95 @@ +let migrationName = '20150124_mountmaster_fix.js'; +let authorName = 'Alys'; // in case script author needs to know when their ... +let authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; // ... own data is done + +/** + * https://github.com/HabitRPG/habitrpg/pull/4374#issuecomment-71038795 + * Convert false to null for mounts that used to be owned. + */ + +let dbserver = 'localhost:27017'; // CHANGE THIS FOR PRODUCTION DATABASE + +// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. +// We've now upgraded to lodash v4 but the code used in this migration has not been +// adapted to work with it. Before this migration is used again any lodash method should +// be checked for compatibility against the v4 changelog and changed if necessary. +// https://github.com/lodash/lodash/wiki/Changelog#v400 + +let mongo = require('mongoskin'); +let _ = require('lodash'); + +let dbUsers = mongo.db(`${dbserver }/habitrpg?auto_reconnect`).collection('users'); + +let query = { + 'items.mounts': {$exists: true}, +}; + +let fields = { + 'items.mounts': 1, +}; + +let animals = ['Wolf-Base', 'Wolf-White', 'Wolf-Desert', 'Wolf-Red', 'Wolf-Shade', 'Wolf-Skeleton', 'Wolf-Zombie', 'Wolf-CottonCandyPink', 'Wolf-CottonCandyBlue', 'Wolf-Golden', 'TigerCub-Base', 'TigerCub-White', 'TigerCub-Desert', 'TigerCub-Red', 'TigerCub-Shade', 'TigerCub-Skeleton', 'TigerCub-Zombie', 'TigerCub-CottonCandyPink', 'TigerCub-CottonCandyBlue', 'TigerCub-Golden', 'PandaCub-Base', 'PandaCub-White', 'PandaCub-Desert', 'PandaCub-Red', 'PandaCub-Shade', 'PandaCub-Skeleton', 'PandaCub-Zombie', 'PandaCub-CottonCandyPink', 'PandaCub-CottonCandyBlue', 'PandaCub-Golden', 'LionCub-Base', 'LionCub-White', 'LionCub-Desert', 'LionCub-Red', 'LionCub-Shade', 'LionCub-Skeleton', 'LionCub-Zombie', 'LionCub-CottonCandyPink', 'LionCub-CottonCandyBlue', 'LionCub-Golden', 'Fox-Base', 'Fox-White', 'Fox-Desert', 'Fox-Red', 'Fox-Shade', 'Fox-Skeleton', 'Fox-Zombie', 'Fox-CottonCandyPink', 'Fox-CottonCandyBlue', 'Fox-Golden', 'FlyingPig-Base', 'FlyingPig-White', 'FlyingPig-Desert', 'FlyingPig-Red', 'FlyingPig-Shade', 'FlyingPig-Skeleton', 'FlyingPig-Zombie', 'FlyingPig-CottonCandyPink', 'FlyingPig-CottonCandyBlue', 'FlyingPig-Golden', 'Dragon-Base', 'Dragon-White', 'Dragon-Desert', 'Dragon-Red', 'Dragon-Shade', 'Dragon-Skeleton', 'Dragon-Zombie', 'Dragon-CottonCandyPink', 'Dragon-CottonCandyBlue', 'Dragon-Golden', 'Cactus-Base', 'Cactus-White', 'Cactus-Desert', 'Cactus-Red', 'Cactus-Shade', 'Cactus-Skeleton', 'Cactus-Zombie', 'Cactus-CottonCandyPink', 'Cactus-CottonCandyBlue', 'Cactus-Golden', 'BearCub-Base', 'BearCub-White', 'BearCub-Desert', 'BearCub-Red', 'BearCub-Shade', 'BearCub-Skeleton', 'BearCub-Zombie', 'BearCub-CottonCandyPink', 'BearCub-CottonCandyBlue', 'BearCub-Golden']; // all Gen1 mounts + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + return displayData(); + } + count++; + + let mounts = user.items.mounts; + let changed = false; + for (let a in animals) { + if (mounts[animals[a]] === false) { + mounts[animals[a]] = null; + changed = true; + } + } + + if (changed) { + dbUsers.update( + { _id: user._id}, + { + $set: { migration: migrationName, + 'items.mounts': mounts, + }, + } + ); + } + + // var set = {'migration': migrationName}; + // var inc = {'xyz':1, _v:1}; + // dbUsers.update({_id:user._id}, {$set:set, $inc:inc}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); + if (user._id === '9') console.warn('lefnire' + ' processed'); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} diff --git a/migrations/archive/2015/20150130_birthday_goodies.js b/migrations/archive/2015/20150130_birthday_goodies.js new file mode 100644 index 0000000000..e9a141c0af --- /dev/null +++ b/migrations/archive/2015/20150130_birthday_goodies.js @@ -0,0 +1,36 @@ +db.users.update( + {'items.gear.owned.armor_special_birthday': {$ne: null}}, + {$set: {'items.gear.owned.armor_special_birthday2015': false}}, + {multi: 1} +); + +db.users.update( + {'items.gear.owned.armor_special_birthday': null}, + {$set: {'items.gear.owned.armor_special_birthday': false}}, + {multi: 1} +); + +db.users.update({}, {$inc: { + 'items.food.Cake_Skeleton': 1, + 'items.food.Cake_Base': 1, + 'items.food.Cake_CottonCandyBlue': 1, + 'items.food.Cake_CottonCandyPink': 1, + 'items.food.Cake_Shade': 1, + 'items.food.Cake_White': 1, + 'items.food.Cake_Golden': 1, + 'items.food.Cake_Zombie': 1, + 'items.food.Cake_Desert': 1, + 'items.food.Cake_Red': 1, +}}, {multi: 1}); + +db.users.update( + {'achievements.habitBirthday': true}, + {$set: {'achievements.habitBirthdays': 1}}, + {multi: 1} +); + +db.users.update( + {}, + {$inc: {'achievements.habitBirthdays': 1}}, + {multi: 1} +); \ No newline at end of file diff --git a/migrations/archive/2015/20150131_birthday_goodies_fix_remove_robe.js b/migrations/archive/2015/20150131_birthday_goodies_fix_remove_robe.js new file mode 100644 index 0000000000..2ed8b2dc34 --- /dev/null +++ b/migrations/archive/2015/20150131_birthday_goodies_fix_remove_robe.js @@ -0,0 +1,85 @@ +let migrationName = '20150131_birthday_goodies_fix__one_birthday__1'; +let authorName = 'Alys'; // in case script author needs to know when their ... +let authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; // ... own data is done + +/* + * remove new birthday robes and second achievement from people who shouldn't have them + */ + +let dbserver = 'localhost:27017'; // CHANGE THIS FOR PRODUCTION DATABASE + +// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. +// We've now upgraded to lodash v4 but the code used in this migration has not been +// adapted to work with it. Before this migration is used again any lodash method should +// be checked for compatibility against the v4 changelog and changed if necessary. +// https://github.com/lodash/lodash/wiki/Changelog#v400 + +let mongo = require('mongoskin'); +let _ = require('lodash'); + +let dbUsers = mongo.db(`${dbserver }/habitrpg?auto_reconnect`).collection('users'); + +// 'auth.timestamps.created':{$gt:new Date('2014-02-01')}, +let query = { + 'achievements.habitBirthdays': 1, + 'auth.timestamps.loggedin': {$gt: new Date('2014-12-20')}, +}; + +// '_id': 'c03e41bd-501f-438c-9553-a7afdf52a08c', +// 'achievements.habitBirthday':{$exists:false}, +// 'items.gear.owned.armor_special_birthday2015':1 + +let fields = { + // 'auth.timestamps.created':1, + // 'achievements.habitBirthday':1, + // 'achievements.habitBirthdays':1, + 'items.gear.owned.armor_special_birthday2015': 1, + // 'items.gear.owned.armor_special':1 +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + return displayData(); + } + count++; + + let unset = {'items.gear.owned.armor_special_birthday2015': 1}; + // var set = {'migration':migrationName, 'achievements.habitBirthdays':1 }; + // var inc = {'xyz':1, _v:1}; + dbUsers.update({_id: user._id}, {$unset: unset}); // , $inc:inc}); + // dbUsers.update({_id:user._id}, {$unset:unset, $set:set}); + // console.warn(user.auth.timestamps.created); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); + if (user._id === '9') console.warn('lefnire' + ' processed'); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} diff --git a/migrations/archive/2015/20150201_convert_creation_date_from_string_to_object.js b/migrations/archive/2015/20150201_convert_creation_date_from_string_to_object.js new file mode 100644 index 0000000000..f5c9a87b06 --- /dev/null +++ b/migrations/archive/2015/20150201_convert_creation_date_from_string_to_object.js @@ -0,0 +1,119 @@ +let migrationName = '20150201_convert_creation_date_from_string_to_object__no_date_recent_signup'; +// // var migrationName = '20150201_convert_creation_date_from_string_to_object'; + +let authorName = 'Alys'; // in case script author needs to know when their ... +let authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; // ... own data is done + +/* + * For users that have no value for auth.timestamps.created, assign them + * a recent value. + * + * NOTE: + * Before this script was used as described above, it was first used to + * find all users that have a auth.timestamps.created field that is a string + * rather than a date object and set it to be a date object. The code used + * for this has been commented out with four slashes: //// + * + * https://github.com/HabitRPG/habitrpg/issues/4601#issuecomment-72339846 + */ + +let dbserver = 'localhost:27017'; // CHANGE THIS FOR PRODUCTION DATABASE + +// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. +// We've now upgraded to lodash v4 but the code used in this migration has not been +// adapted to work with it. Before this migration is used again any lodash method should +// be checked for compatibility against the v4 changelog and changed if necessary. +// https://github.com/lodash/lodash/wiki/Changelog#v400 + +let mongo = require('mongoskin'); +let _ = require('lodash'); +let moment = require('moment'); + +let dbUsers = mongo.db(`${dbserver }/habitrpg?auto_reconnect`).collection('users'); + +let uuidArrayRecent = [ // recent users with no creation dates + '1a0d4b75-73ed-4937-974d-d504d6398884', + '1c7ebe27-1250-4f95-ba10-965580adbfd7', + '5f972121-4a6d-411c-95e9-7093d3e89b66', + 'ae85818a-e336-4ccd-945e-c15cef975102', + 'ba273976-d9fc-466c-975f-38559d34a824', +]; + +let query = { + _id: {$in: uuidArrayRecent}, + // // 'auth':{$exists:true}, + // // 'auth.timestamps':{$exists:true}, + // // 'auth.timestamps.created':{$not: {$lt:new Date('2018-01-01')}} +}; + +let fields = { + _id: 1, + 'auth.timestamps.created': 1, +}; +// 'achievements.habitBirthdays':1 + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + return displayData(); + } + count++; + + // // var oldDate = user.auth.timestamps.created; + // // var newDate = moment(oldDate).toDate(); + let oldDate = 'none'; + let newDate = moment('2015-01-11').toDate(); + console.warn(`${user._id } === ${ oldDate } === ${ newDate}`); + + // // var set = { 'migration': migrationName, + // // 'auth.timestamps.created': newDate, + // // 'achievements.habitBirthdays': 2, + // // 'items.gear.owned.head_special_nye':true, + // // 'items.gear.owned.head_special_nye2014':true, + // // 'items.gear.owned.armor_special_birthday':true, + // // 'items.gear.owned.armor_special_birthday2015':true, + // // }; + + let set = { migration: migrationName, + 'auth.timestamps.created': newDate, + 'achievements.habitBirthdays': 1, + 'items.gear.owned.armor_special_birthday': true, + }; + + // var unset = {'items.gear.owned.armor_special_birthday2015': 1}; + // var inc = {'xyz':1, _v:1}; + dbUsers.update({_id: user._id}, {$set: set}); + // dbUsers.update({_id:user._id}, {$unset:unset, $set:set, $inc:inc}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); + if (user._id === '9') console.warn('lefnire' + ' processed'); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} diff --git a/migrations/archive/2015/20150201_recapture_emails_phase_update.js b/migrations/archive/2015/20150201_recapture_emails_phase_update.js new file mode 100644 index 0000000000..26cf9a25d1 --- /dev/null +++ b/migrations/archive/2015/20150201_recapture_emails_phase_update.js @@ -0,0 +1,7 @@ +db.users.update({ + 'flags.recaptureEmailsPhase': { + $gt: 0, + }, +}, {$inc: { + 'flags.recaptureEmailsPhase': 1, +}}, {multi: 1}); \ No newline at end of file diff --git a/migrations/archive/2015/20150218_interactive_tour.js b/migrations/archive/2015/20150218_interactive_tour.js new file mode 100644 index 0000000000..6223f17d6a --- /dev/null +++ b/migrations/archive/2015/20150218_interactive_tour.js @@ -0,0 +1,10 @@ +db.users.update({}, {$set: { + 'flags.tour.intro': -2, + // 'flags.tour.classes':-2, + 'flags.tour.stats': -2, + 'flags.tour.tavern': -2, + 'flags.tour.party': -2, + 'flags.tour.guilds': -2, + 'flags.tour.challenges': -2, + 'flags.tour.market': -2, +}}, {multi: 1}); \ No newline at end of file diff --git a/migrations/archive/2015/20150224_force_resting_in_inn.js b/migrations/archive/2015/20150224_force_resting_in_inn.js new file mode 100644 index 0000000000..d148419628 --- /dev/null +++ b/migrations/archive/2015/20150224_force_resting_in_inn.js @@ -0,0 +1,71 @@ +let migrationName = '20150224_force_resting_in_inn'; +let authorName = 'Alys'; // in case script author needs to know when their ... +let authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; // ... own data is done + +/* + * force all active players to rest in the inn due to massive server fail + */ + +// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. +// We've now upgraded to lodash v4 but the code used in this migration has not been +// adapted to work with it. Before this migration is used again any lodash method should +// be checked for compatibility against the v4 changelog and changed if necessary. +// https://github.com/lodash/lodash/wiki/Changelog#v400 + +let dbserver = 'localhost:27017'; // CHANGE THIS FOR PRODUCTION DATABASE + +let mongo = require('mongoskin'); +let _ = require('lodash'); + +let dbUsers = mongo.db(`${dbserver }/habitrpg?auto_reconnect`).collection('users'); + +let query = { + 'auth.timestamps.loggedin': {$gt: new Date('2015-02-22')}, +}; + +let fields = { + 'preferences.sleep': 1, +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + return displayData(); + } + count++; + + let set = {migration: migrationName, 'preferences.sleep': 1 }; + dbUsers.update({_id: user._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); + if (user._id === '9') console.warn('lefnire' + ' processed'); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} diff --git a/migrations/archive/2015/20150310_survey_achievements.js b/migrations/archive/2015/20150310_survey_achievements.js new file mode 100644 index 0000000000..ea343416d5 --- /dev/null +++ b/migrations/archive/2015/20150310_survey_achievements.js @@ -0,0 +1,5 @@ +db.users.update( + {'achievements.helpedHabit': true}, + {$set: {'achievements.habitSurveys': 1}}, + {multi: 1} +); \ No newline at end of file diff --git a/migrations/20150325_egg_quest.js b/migrations/archive/2015/20150325_egg_quest.js similarity index 56% rename from migrations/20150325_egg_quest.js rename to migrations/archive/2015/20150325_egg_quest.js index 43ca0ad1b7..d28b7dccea 100644 --- a/migrations/20150325_egg_quest.js +++ b/migrations/archive/2015/20150325_egg_quest.js @@ -3,6 +3,6 @@ */ db.users.update( {}, - {$inc:{'items.quests.egg':1}}, - {multi:1} -) \ No newline at end of file + {$inc: {'items.quests.egg': 1}}, + {multi: 1} +); \ No newline at end of file diff --git a/migrations/archive/2015/20150604_ultimateGearSets.js b/migrations/archive/2015/20150604_ultimateGearSets.js new file mode 100644 index 0000000000..aa69647666 --- /dev/null +++ b/migrations/archive/2015/20150604_ultimateGearSets.js @@ -0,0 +1,141 @@ +// var migrationName = '20150604_ultimateGearSets'; +// var authorName = 'Sabe'; // in case script author needs to know when their ... +// var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done + +let migrationName = '20150620_ultimateGearSets'; +let authorName = 'Alys'; // in case script author needs to know when their ... +let authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; // ... own data is done + +/* + * grant the new ultimateGearSets achievement for existing users' collected equipment + * + * + * Changed by Alys on 20150620 to assign false values to + * 'achievements.ultimateGearSets' when true values are not appropriate, + * because of https://github.com/HabitRPG/habitrpg/issues/5427 + * + * Minimal changes were made so the code isn't as efficient or clean + * as it could be, but it's (hopefully) one-use-only and minimal changes + * means minimal new testing. + */ + +// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. +// We've now upgraded to lodash v4 but the code used in this migration has not been +// adapted to work with it. Before this migration is used again any lodash method should +// be checked for compatibility against the v4 changelog and changed if necessary. +// https://github.com/lodash/lodash/wiki/Changelog#v400 + +let dbserver = 'localhost:27017'; // FOR TEST DATABASE +// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379' // FOR PRODUCTION DATABASE +let dbname = 'habitrpg'; + +let mongo = require('mongoskin'); +let _ = require('lodash'); + +let dbUsers = mongo.db(`${dbserver }/${ dbname }?auto_reconnect`).collection('users'); + +let fields = { + 'achievements.ultimateGearSets': 1, + 'items.gear.owned': 1, +}; + + +// Changes 20150620: All users have to be processed now (non-achievers need +// false values). +let query = { +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + return displayData(); + } + count++; + + let achievements = {}; + let changeUser = false; + // Changes 20150620: 'changeUser' now indicates that the user must have the + // Enchanted Armoire unlocked. + if (typeof user.items.gear.owned.weapon_wizard_6 !== 'undefined' && + typeof user.items.gear.owned.armor_wizard_5 !== 'undefined' && + typeof user.items.gear.owned.head_wizard_5 !== 'undefined' + ) { + achievements.wizard = true; + changeUser = true; + } else { + // Changes 20150620: false added for all classes (here and below) + achievements.wizard = false; + } + + if (typeof user.items.gear.owned.weapon_warrior_6 !== 'undefined' && + typeof user.items.gear.owned.armor_warrior_5 !== 'undefined' && + typeof user.items.gear.owned.head_warrior_5 !== 'undefined' && + typeof user.items.gear.owned.shield_warrior_5 !== 'undefined' + ) { + achievements.warrior = true; + changeUser = true; + } else { + achievements.warrior = false; + } + + if (typeof user.items.gear.owned.weapon_healer_6 !== 'undefined' && + typeof user.items.gear.owned.armor_healer_5 !== 'undefined' && + typeof user.items.gear.owned.head_healer_5 !== 'undefined' && + typeof user.items.gear.owned.shield_healer_5 !== 'undefined' + ) { + achievements.healer = true; + changeUser = true; + } else { + achievements.healer = false; + } + + if (typeof user.items.gear.owned.weapon_rogue_6 !== 'undefined' && + typeof user.items.gear.owned.armor_rogue_5 !== 'undefined' && + typeof user.items.gear.owned.head_rogue_5 !== 'undefined' && + typeof user.items.gear.owned.shield_rogue_6 !== 'undefined' + ) { + achievements.rogue = true; + changeUser = true; + } else { + achievements.rogue = false; + } + + // Changes 20150620: $set is now run for all users. + let set = {migration: migrationName, 'achievements.ultimateGearSets': achievements}; + if (changeUser) { // user has at least one Ultimate Gear achievement + set['flags.armoireEnabled'] = true; + } + dbUsers.update({_id: user._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); + if (user._id === '9') console.warn('lefnire' + ' processed'); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} diff --git a/migrations/20150706_orca_mounts.js b/migrations/archive/2015/20150706_orca_mounts.js similarity index 52% rename from migrations/20150706_orca_mounts.js rename to migrations/archive/2015/20150706_orca_mounts.js index e8b6fe662a..92dc55e77c 100644 --- a/migrations/20150706_orca_mounts.js +++ b/migrations/archive/2015/20150706_orca_mounts.js @@ -3,6 +3,6 @@ */ db.users.update( {}, - {$set:{'items.mounts.Orca-Base':true}}, - {multi:true} + {$set: {'items.mounts.Orca-Base': true}}, + {multi: true} ); diff --git a/migrations/archive/2015/20150731_purple_gryphon.js b/migrations/archive/2015/20150731_purple_gryphon.js new file mode 100644 index 0000000000..e75f8e9c97 --- /dev/null +++ b/migrations/archive/2015/20150731_purple_gryphon.js @@ -0,0 +1,5 @@ +db.users.update( + {}, + {$set: {'items.mounts.Gryphon-RoyalPurple': true}}, + {multi: true} +); diff --git a/migrations/archive/2015/20150731_veteran_tiger.js b/migrations/archive/2015/20150731_veteran_tiger.js new file mode 100644 index 0000000000..1494dd0ad8 --- /dev/null +++ b/migrations/archive/2015/20150731_veteran_tiger.js @@ -0,0 +1,5 @@ +db.users.update( + {'items.pets.Wolf-Veteran': {$ne: null}}, + {$set: {'items.pets.Tiger-Veteran': 5}}, + {multi: true} +); diff --git a/migrations/archive/2015/20150731_veteran_wolf.js b/migrations/archive/2015/20150731_veteran_wolf.js new file mode 100644 index 0000000000..f060100957 --- /dev/null +++ b/migrations/archive/2015/20150731_veteran_wolf.js @@ -0,0 +1,7 @@ +// Run after the Veteran Tiger script, not before! + +db.users.update( + {'items.pets.Wolf-Veteran': {$exists: false}}, + {$set: {'items.pets.Wolf-Veteran': 5}}, + {multi: true} +); diff --git a/migrations/archive/2015/20150906_groups_fix_leaders.js b/migrations/archive/2015/20150906_groups_fix_leaders.js new file mode 100644 index 0000000000..252328571d --- /dev/null +++ b/migrations/archive/2015/20150906_groups_fix_leaders.js @@ -0,0 +1,79 @@ +/* + * Make sure leaders are existing users + */ + +let mongo = require('mongoskin'); +let async = require('async'); + +let dbserver = 'url'; +let dbname = 'dbname'; +let countGroups = 0; +let countUsers = 0; + +let db = mongo.db(`${dbserver }/${ dbname }?auto_reconnect`); +let dbUsers = db.collection('users'); +let dbGroups = db.collection('groups'); + +console.log('Begins work on db'); + +function findGroups (gt) { + let query = {}; + if (gt) query._id = {$gt: gt}; + + console.log(query); + + dbGroups.find(query, { + fields: {_id: 1, members: 1, leader: 1}, + limit: 10000, + sort: { + _id: 1, + }, + }).toArray(function (err, groups) { + if (err) throw err; + + let lastGroup = null; + if (groups.length === 10000) { + lastGroup = groups[groups.length - 1]; + } + + async.eachLimit(groups, 30, function (group, cb1) { + countGroups++; + console.log('Group: ', countGroups, group._id); + + let members = group.members; + + dbUsers.findOne({_id: group.leader}, {fields: {_id: 1}}, function (err, user) { + if (err) return cb1(err); + + // If leader has deleted account + if (!user && group._id !== 'habitrpg' && members && members[0]) { + dbGroups.update({ + _id: group._id, + }, { + $set: { + // Set first user as new leader + leader: members[0], + }, + }, { + multi: false, + }, function (err, res) { + if (err) return cb1(err); + + console.log('Updated: ', res); + return cb1(); + }); + } else { + return cb1(); + } + }); + }, function (err) { + if (err) throw err; + + if (lastGroup && lastGroup._id) { + findGroups(lastGroup._id); + } + }); + }); +} + +findGroups(); \ No newline at end of file diff --git a/migrations/archive/2015/20150906_groups_remove_deleted_users.js b/migrations/archive/2015/20150906_groups_remove_deleted_users.js new file mode 100644 index 0000000000..d04b91341b --- /dev/null +++ b/migrations/archive/2015/20150906_groups_remove_deleted_users.js @@ -0,0 +1,86 @@ +/* + * Remove deleted accounts from groups + */ + +let mongo = require('mongoskin'); +let async = require('async'); + +let dbserver = 'url'; +let dbname = 'dbname'; +let countGroups = 0; +let countUsers = 0; + +let db = mongo.db(`${dbserver }/${ dbname }?auto_reconnect`); +let dbUsers = db.collection('users'); +let dbGroups = db.collection('groups'); + +console.log('Begins work on db'); + +function findGroups (gt) { + let query = {}; + if (gt) query._id = {$gt: gt}; + + console.log(query); + + dbGroups.find(query, { + fields: {_id: 1, members: 1}, + limit: 10000, + sort: { + _id: 1, + }, + }).toArray(function (err, groups) { + if (err) throw err; + + let lastGroup = null; + if (groups.length === 10000) { + lastGroup = groups[groups.length - 1]; + } + + async.eachLimit(groups, 3, function (group, cb1) { + countGroups++; + console.log('Group: ', countGroups, group._id); + + let members = group.members; + + // Remove users who deleted their account + async.eachLimit(members, 15, function (member, cb2) { + dbUsers.findOne({_id: member}, {fields: {_id: 1}}, function (err, user) { + if (err) return cb2(err); + + if (!user) { + countUsers++; + console.log('User removed n. ', countUsers, 'user id ', member, 'group id ', group._id); + + dbGroups.update({ + _id: group._id, + }, { + $pull: {members: member}, + $inc: {memberCount: -1}, + }, { + multi: false, + }, function (err, res) { + if (err) return cb2(err); + + console.log('Updated: ', res); + return cb2(); + }); + } else { + cb2(); + } + }); + }, function (err) { + if (err) return cb1(err); + + cb1(); + }); + }, function (err) { + if (err) throw err; + + if (lastGroup && lastGroup._id) { + findGroups(lastGroup._id); + } + }); + }); +} + +findGroups(); \ No newline at end of file diff --git a/migrations/archive/2015/20150906_groups_remove_empty.js b/migrations/archive/2015/20150906_groups_remove_empty.js new file mode 100644 index 0000000000..b82313e014 --- /dev/null +++ b/migrations/archive/2015/20150906_groups_remove_empty.js @@ -0,0 +1,21 @@ +/* + * Remove empty private groups + */ + +let mongo = require('mongoskin'); + +let dbserver = 'url'; +let dbname = 'name'; + +let db = mongo.db(`${dbserver }/${ dbname }?auto_reconnect`); +let dbGroups = db.collection('groups'); + +console.log('Begins work on db'); + +dbGroups.findEach({ + memberCount: 0, +}, {_id: 1}, function (err, res) { + if (err) throw err; + + console.log(res); +}); \ No newline at end of file diff --git a/migrations/archive/2015/20150906_sync_groups_with_firebase.js b/migrations/archive/2015/20150906_sync_groups_with_firebase.js new file mode 100644 index 0000000000..9b17d02a09 --- /dev/null +++ b/migrations/archive/2015/20150906_sync_groups_with_firebase.js @@ -0,0 +1,43 @@ +/* + * Sync groups with Firebase + */ + +let mongo = require('mongoskin'); +let Firebase = require('Firebase'); + +let dbserver = 'mongodb://url'; +let dbname = 'db'; + +let db = mongo.db(`${dbserver }/${ dbname }?auto_reconnect`); +let dbGroups = db.collection('groups'); +let countGroups = 0; + +let firebaseRef = new Firebase('https://' + 'firebase-app' + '.firebaseio.com'); + +// TODO handle sync errors with firebase? +firebaseRef.authWithCustomToken('firebase-secret', function (err, authData) { + if (err) throw new Error('Impossible to authenticate Firebase'); + + console.log('Firebase connected, begins work on db'); + + dbGroups.findEach({}, {_id: 1, members: 1}, {batchSize: 100}, function (err, group) { + if (err) throw err; + if (group._id !== 'habitrpg') return; + + countGroups++; + console.log('Group: ', countGroups); + + firebaseRef.child(`rooms/${ group._id}`) + .set({ + name: group.name, + }); + + group.members.forEach(function (member) { + firebaseRef.child(`members/${ group._id }/${ userId}`) + .set(true); + + firebaseRef.child(`users/${ member }/rooms/${ group._id}`) + .set(true); + }); + }); +}); \ No newline at end of file diff --git a/migrations/archive/2015/20151013_jackolanterns.js b/migrations/archive/2015/20151013_jackolanterns.js new file mode 100644 index 0000000000..358678f2fa --- /dev/null +++ b/migrations/archive/2015/20151013_jackolanterns.js @@ -0,0 +1,74 @@ +let migrationName = '20151013_jackolanterns.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award Jack-O'-Lantern mounts to users who already have the pet version, award pet if they don't + */ + +let dbserver = 'localhost:27017'; // FOR TEST DATABASE +// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE +let dbname = 'habitrpg'; + +let mongo = require('mongoskin'); +let _ = require('lodash'); + +let dbUsers = mongo.db(`${dbserver }/${ dbname }?auto_reconnect`).collection('users'); + +// specify a query to limit the affected users (empty for all users): +let query = { +}; + +// specify fields we are interested in to limit retrieved data (empty if we're not reading data): +let fields = { + 'items.pets.JackOLantern-Base': 1, +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + return displayData(); + } + count++; + + // specify user data to change: + let set = {}; + if (user.items.pets['JackOLantern-Base']) { + set = {migration: migrationName, 'items.mounts.JackOLantern-Base': true}; + } else { + set = {migration: migrationName, 'items.pets.JackOLantern-Base': 5}; + } + + dbUsers.update({_id: user._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} diff --git a/migrations/archive/2015/20151021_usernames_emails_lowercase.js b/migrations/archive/2015/20151021_usernames_emails_lowercase.js new file mode 100644 index 0000000000..ec46c5642b --- /dev/null +++ b/migrations/archive/2015/20151021_usernames_emails_lowercase.js @@ -0,0 +1,63 @@ +/* + * Migrate email to lowerCase version and add auth.local.lowerCaseUsername email + */ + +let mongo = require('mongoskin'); +let async = require('async'); + +let dbserver = 'url'; +let dbname = 'dbname'; +let countUsers = 0; + +let db = mongo.db(`${dbserver }/${ dbname }?auto_reconnect`); +let dbUsers = db.collection('users'); + +console.log('Begins work on db'); + +function findUsers (gt) { + let query = {}; + if (gt) query._id = {$gt: gt}; + + console.log(query); + + dbUsers.find(query, { + fields: {_id: 1, auth: 1}, + limit: 10000, + sort: { + _id: 1, + }, + }).toArray(function (err, users) { + if (err) throw err; + + let lastUser = null; + if (users.length === 10000) { + lastUser = users[users.length - 1]; + } + + async.eachLimit(users, 20, function (user, cb) { + countUsers++; + console.log('User: ', countUsers, user._id); + + let update = { + $set: {}, + }; + + if (user.auth && user.auth.local) { + if (user.auth.local.username) update.$set['auth.local.lowerCaseUsername'] = user.auth.local.username.toLowerCase(); + if (user.auth.local.email) update.$set['auth.local.email'] = user.auth.local.email.toLowerCase(); + } + + dbUsers.update({ + _id: user._id, + }, update, cb); + }, function (err) { + if (err) throw err; + + if (lastUser && lastUser._id) { + findUsers(lastUser._id); + } + }); + }); +} + +findUsers(); diff --git a/migrations/archive/2015/20151105_tutorial_flags.js b/migrations/archive/2015/20151105_tutorial_flags.js new file mode 100644 index 0000000000..7023af9fae --- /dev/null +++ b/migrations/archive/2015/20151105_tutorial_flags.js @@ -0,0 +1,70 @@ +let migrationName = '20151105_tutorial_flags_v1'; +let authorName = 'Alys'; // in case script author needs to know when their ... +let authorUuid = 'd904bd62-da08-416b-a816-ba797c9ee265'; // ... own data is done + +/* + * set flags.tutorial.ios and flags.tutorial.main flags to true in preparation + * for the release of a new iOS tutorial + * + */ + +// var dbserver = 'localhost:27017' // FOR TEST DATABASE +let dbserver = 'alys:@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE +let dbname = 'habitrpg'; + +let mongo = require('mongoskin'); +let _ = require('lodash'); + +let dbUsers = mongo.db(`${dbserver }/${ dbname }?auto_reconnect`).collection('users'); + +let fields = { +}; + + +let query = { + 'auth.timestamps.loggedin': {$gt: new Date('2015-10-20')}, +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + return displayData(); + } + count++; + + // var set = {'migration':migrationName, 'flags.tutorial.ios':true, 'flags.tutorial.main':true }; + let set = {migration: migrationName, 'flags.tutorial.ios': {} }; + + dbUsers.update({_id: user._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} diff --git a/migrations/archive/2015/20151116_costume_contest_award.js b/migrations/archive/2015/20151116_costume_contest_award.js new file mode 100644 index 0000000000..556697f6ba --- /dev/null +++ b/migrations/archive/2015/20151116_costume_contest_award.js @@ -0,0 +1,109 @@ +let migrationName = '20151116_costume_contest.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award Costume Contest achievement to 2015 winners + */ + +let dbserver = 'localhost:27017'; // FOR TEST DATABASE +// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE +let dbname = 'habitrpg'; + +let mongo = require('mongoskin'); +let _ = require('lodash'); + +let dbUsers = mongo.db(`${dbserver }/${ dbname }?auto_reconnect`).collection('users'); + +// specify a query to limit the affected users (empty for all users): +let query = { + _id: { + $in: [ + 'e411dab3-a4ca-414d-bdbd-b6940b3bdeb3', + '35ced5cc-c33a-45c8-93dc-16000ee66fde', + 'ab3f0549-7247-4fd5-975b-efcff98c79c3', + 'b1261fd2-eb25-46b4-97a9-ae7a0dc8a131', + '1f27893f-3808-4724-9725-f46dab93faca', + '216a0c23-6afd-4a5e-b434-d386a10862a2', + '2d6ef231-50b4-4a22-90e7-45eb97147a2c', + '98b8cf4f-89bd-4b0a-988d-02629a217232', + 'c5183dfa-c741-43ce-935e-c6d89b41a030', + '262a7afb-6b57-4d81-88e0-80d2e9f6cbdc', + '33991e0a-de55-4986-ac81-af78491a84de', + '7adf6ada-3c05-4054-b5df-fa7d49d3b9eb', + '235a1cbd-48c5-41b1-afb4-59d2f8645c57', + 'b7617a61-188b-4332-bf4d-32268fa77f2b', + '672c1ce0-9f47-44f0-a3f3-8cc3c6c5a9cb', + 'd0a3217a-7b92-48d6-b39a-b1b1be96702e', + '5ef910dc-1d22-47d9-aa38-a60132c60679', + '370a44c8-e94a-4a2c-91f2-33166926db1f', + '1b0b3ef3-28bd-4046-a49b-e1c83e281baf', + '75b93321-66b9-49bd-9076-052499c1d2bf', + 'd97516e4-81d0-4f60-bf03-95f7330925ab', + '3e13cc79-de38-420d-822e-9e9da309ce6b', + '0e471dc1-ecb0-4388-a891-b873a237d2cf', + 'ca3da398-4f73-4304-b838-af3669ed4cbb', + '44cdf105-8bda-4197-9d1a-1bcb83b4dc84', + '5419830c-b837-4573-ae82-4718ab95b7f1', + 'ac6fbe37-b0dc-40d8-ba14-77dde66fbfa8', + '8789ba18-a498-46b9-b367-3b929a0acb94', + '52fce1a9-9b0a-4e26-95dc-adc12f52e752', + '21bf71ac-399c-470b-abe0-cc49a03b6a8b', + 'f1618ce2-552e-4f23-bc76-e73d63ebedd0', + '4cc0c749-d943-4090-b529-42bc665b7244', + 'e259682e-cb5c-4d94-b472-ceedc66d7484', + 'fa197a4b-e065-4551-803a-c8a5b9970f9d', + ], + }, +}; + +// specify fields we are interested in to limit retrieved data (empty if we're not reading data): +let fields = { +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + return displayData(); + } + count++; + + // specify user data to change: + let set = {migration: migrationName}; + let inc = {'achievements.costumeContests': 1}; + + dbUsers.update({_id: user._id}, {$set: set}); + dbUsers.update({_id: user._id}, {$inc: inc}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + diff --git a/migrations/archive/2015/20151116_costume_contest_to_number.js b/migrations/archive/2015/20151116_costume_contest_to_number.js new file mode 100644 index 0000000000..68660941f3 --- /dev/null +++ b/migrations/archive/2015/20151116_costume_contest_to_number.js @@ -0,0 +1,71 @@ +let migrationName = '20151116_costume_contest_to_number.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Change Costume Contest achievement from Boolean to Number, so people can win repeatedly + */ + +let dbserver = 'localhost:27017'; // FOR TEST DATABASE +// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE +let dbname = 'habitrpg'; + +let mongo = require('mongoskin'); +let _ = require('lodash'); + +let dbUsers = mongo.db(`${dbserver }/${ dbname }?auto_reconnect`).collection('users'); + +// specify a query to limit the affected users (empty for all users): +let query = { + 'achievements.costumeContest': true, +}; + +// specify fields we are interested in to limit retrieved data (empty if we're not reading data): +let fields = { + 'achievements.costumeContest': 1, +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + return displayData(); + } + count++; + + // specify user data to change: + let set = {'achievements.costumeContests': 1}; + + dbUsers.update({_id: user._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + diff --git a/migrations/archive/2015/20151125_turkey_ladder.js b/migrations/archive/2015/20151125_turkey_ladder.js new file mode 100644 index 0000000000..6328030f25 --- /dev/null +++ b/migrations/archive/2015/20151125_turkey_ladder.js @@ -0,0 +1,78 @@ +let migrationName = '20151125_turkey_ladder.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award Gilded Turkey pet to Turkey mount owners, Turkey Mount if they only have Turkey Pet, + * and Turkey Pet otherwise + */ + +let dbserver = 'localhost:27017'; // FOR TEST DATABASE +// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE +let dbname = 'habitrpg'; + +let mongo = require('mongoskin'); +let _ = require('lodash'); + +let dbUsers = mongo.db(`${dbserver }/${ dbname }?auto_reconnect`).collection('users'); + +// specify a query to limit the affected users (empty for all users): +let query = { +}; + +// specify fields we are interested in to limit retrieved data (empty if we're not reading data): +let fields = { + 'items.pets.Turkey-Base': 1, + 'items.mounts.Turkey-Base': 1, +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + return displayData(); + } + count++; + + // specify user data to change: + let set = {}; + if (user.items.mounts['Turkey-Base']) { + set = {migration: migrationName, 'items.pets.Turkey-Gilded': 5}; + } else if (user.items.pets['Turkey-Base']) { + set = {migration: migrationName, 'items.mounts.Turkey-Base': true}; + } else { + set = {migration: migrationName, 'items.pets.Turkey-Base': 5}; + } + + dbUsers.update({_id: user._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} diff --git a/migrations/archive/2015/20151229_new_years_hats.js b/migrations/archive/2015/20151229_new_years_hats.js new file mode 100644 index 0000000000..74788369a1 --- /dev/null +++ b/migrations/archive/2015/20151229_new_years_hats.js @@ -0,0 +1,77 @@ +let migrationName = '20151229_new_years_hats.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award 2015 party hat if user has 2014 hat, 2014 hat if they have the 2013 hat, + * and 2013 hat otherwise + */ + +let dbserver = 'localhost:27017'; // FOR TEST DATABASE +// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE +let dbname = 'habitrpg'; + +let mongo = require('mongoskin'); +let _ = require('lodash'); + +let dbUsers = mongo.db(`${dbserver }/${ dbname }?auto_reconnect`).collection('users'); + +// specify a query to limit the affected users (empty for all users): +let query = { +}; + +// specify fields we are interested in to limit retrieved data (empty if we're not reading data): +let fields = { + 'items.gear.owned': 1, +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + return displayData(); + } + count++; + + // specify user data to change: + let set = {}; + if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('head_special_nye2014')) { + set = {migration: migrationName, 'items.gear.owned.head_special_nye2015': false}; + } else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('head_special_nye')) { + set = {migration: migrationName, 'items.gear.owned.head_special_nye2014': false}; + } else { + set = {migration: migrationName, 'items.gear.owned.head_special_nye': false}; + } + + dbUsers.update({_id: user._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} diff --git a/migrations/20160111_challenges_condense_same_day_history_entries.js b/migrations/archive/2016/20160111_challenges_condense_same_day_history_entries.js similarity index 56% rename from migrations/20160111_challenges_condense_same_day_history_entries.js rename to migrations/archive/2016/20160111_challenges_condense_same_day_history_entries.js index 69411e8530..76a7e14dfb 100644 --- a/migrations/20160111_challenges_condense_same_day_history_entries.js +++ b/migrations/archive/2016/20160111_challenges_condense_same_day_history_entries.js @@ -1,11 +1,11 @@ -var migrationName = '20160111_challenges_condense_same_day_history_entries.js'; +let migrationName = '20160111_challenges_condense_same_day_history_entries.js'; /* * Compress challenges tasks history entries so that only one entry per day is kept */ -var dbserver = ''; -var dbname = ''; +let dbserver = ''; +let dbname = ''; // IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. // We've now upgraded to lodash v4 but the code used in this migration has not been @@ -13,34 +13,34 @@ var dbname = ''; // be checked for compatibility against the v4 changelog and changed if necessary. // https://github.com/lodash/lodash/wiki/Changelog#v400 -var mongo = require('mongoskin'); -var _ = require('lodash'); -var moment = require('moment'); +let mongo = require('mongoskin'); +let _ = require('lodash'); +let moment = require('moment'); -var dbChallenges = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('challenges'); +let dbChallenges = mongo.db(`${dbserver }/${ dbname }?auto_reconnect`).collection('challenges'); // Find all challenges -var query = { +let query = { }; // we only want habits and dailies (rewards and todos don't have history) -var fields = { - 'habits': 1, - 'dailys': 1, +let fields = { + habits: 1, + dailys: 1, }; function compressEntries (history) { return _.chain(history) - .filter(function(entry) { - return !!entry; + .filter(function (entry) { + return Boolean(entry); }) - .groupBy(function(entry) { // group by day + .groupBy(function (entry) { // group by day return moment(entry.date).format('YYYYMMDD'); }) - .sortBy(function(entry, key) { // sort by date and transform back to array of array of entries + .sortBy(function (entry, key) { // sort by date and transform back to array of array of entries return key; }) - .map(function(entries) { // aggregate the value + .map(function (entries) { // aggregate the value return { date: Number(entries[0].date), value: _.reduce(entries, function (previousValue, entry) { @@ -49,14 +49,16 @@ function compressEntries (history) { }; }) .value(); -}; +} console.warn('Updating challenges...'); -var progressCount = 100; -var count = 0; +let progressCount = 100; +let count = 0; -dbChallenges.findEach(query, fields, {batchSize: 250}, function(err, challenge) { - if (err) { return exiting(1, 'ERROR! ' + err); } +dbChallenges.findEach(query, fields, {batchSize: 250}, function (err, challenge) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } if (!challenge) { console.warn('All appropriate challenges found.'); return displayData(); @@ -64,49 +66,54 @@ dbChallenges.findEach(query, fields, {batchSize: 250}, function(err, challenge) count++; // specify challenge data to change: - var set = {}; + let set = {}; if (challenge.habits && challenge.habits.length > 0) { - challenge.habits.forEach(function(habit, index) { + challenge.habits.forEach(function (habit, index) { if (habit.history && habit.history.length > 1) { - var originalL = habit.history.length; + let originalL = habit.history.length; habit.history = compressEntries(habit.history); if (originalL > 1000) console.log(originalL, habit.history.length); - set['habits.' + index + '.history'] = habit.history; + set[`habits.${ index }.history`] = habit.history; } }); } if (challenge.dailys && challenge.dailys.length > 0) { - challenge.dailys.forEach(function(daily, index) { + challenge.dailys.forEach(function (daily, index) { if (daily.history && daily.history.length > 1) { - var originalL = daily.history.length; + let originalL = daily.history.length; daily.history = compressEntries(daily.history); if (originalL > 1000) console.log(originalL, daily.history.length); - set['dailys.' + index + '.history'] = daily.history; + set[`dailys.${ index }.history`] = daily.history; } }); } - dbChallenges.update({_id: challenge._id}, {$set: set}, function(err) { - if(err) throw err; + dbChallenges.update({_id: challenge._id}, {$set: set}, function (err) { + if (err) throw err; console.log('updated a challenge'); }); - if (count%progressCount == 0) console.warn(count + ' ' + challenge._id); + if (count % progressCount === 0) console.warn(`${count } ${ challenge._id}`); }); -function displayData() { - console.warn('\n' + count + ' challenges processed\n'); +function displayData () { + console.warn(`\n${ count } challenges processed\n`); return exiting(0); } -function exiting(code, msg) { +function exiting (code, msg) { code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } + if (code && !msg) { + msg = 'ERROR!'; + } if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } + if (code) { + console.error(msg); + } else { + console.log(msg); + } } process.exit(code); } diff --git a/migrations/20160129_habit_birthday.js b/migrations/archive/2016/20160129_habit_birthday.js similarity index 50% rename from migrations/20160129_habit_birthday.js rename to migrations/archive/2016/20160129_habit_birthday.js index f4be13c50e..25e50ea0dd 100644 --- a/migrations/20160129_habit_birthday.js +++ b/migrations/archive/2016/20160129_habit_birthday.js @@ -1,15 +1,15 @@ -var migrationName = '20160129_habit_birthday.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done +let migrationName = '20160129_habit_birthday.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done /* * Award 2016 party robes if user has 2015 robes, 2015 robes if they have the 2014 robes, * and 2014 robes otherwise */ -var dbserver = 'localhost:27017'; // FOR TEST DATABASE +let dbserver = 'localhost:27017'; // FOR TEST DATABASE // var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE -var dbname = 'habitrpg'; +let dbname = 'habitrpg'; // IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. // We've now upgraded to lodash v4 but the code used in this migration has not been @@ -17,25 +17,27 @@ var dbname = 'habitrpg'; // 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'); +let mongo = require('mongoskin'); +let _ = require('lodash'); -var dbUsers = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('users'); +let dbUsers = mongo.db(`${dbserver }/${ dbname }?auto_reconnect`).collection('users'); // specify a query to limit the affected users (empty for all users): -var query = { +let query = { }; // specify fields we are interested in to limit retrieved data (empty if we're not reading data): -var fields = { +let fields = { 'items.gear.owned': 1, }; console.warn('Updating users...'); -var progressCount = 1000; -var count = 0; -dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { - if (err) { return exiting(1, 'ERROR! ' + err); } +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } if (!user) { console.warn('All appropriate users found and modified.'); return displayData(); @@ -43,7 +45,7 @@ dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { count++; // specify user data to change: - var set = {'migration':migrationName}; + let set = {migration: migrationName}; if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2015')) { set['items.gear.owned.armor_special_birthday2016'] = false; } else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday')) { @@ -52,40 +54,45 @@ dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) { set['items.gear.owned.armor_special_birthday'] = false; } - var inc = { - 'items.food.Cake_Skeleton':1, - 'items.food.Cake_Base':1, - 'items.food.Cake_CottonCandyBlue':1, - 'items.food.Cake_CottonCandyPink':1, - 'items.food.Cake_Shade':1, - 'items.food.Cake_White':1, - 'items.food.Cake_Golden':1, - 'items.food.Cake_Zombie':1, - 'items.food.Cake_Desert':1, - 'items.food.Cake_Red':1, - 'achievements.habitBirthdays':1 + let inc = { + 'items.food.Cake_Skeleton': 1, + 'items.food.Cake_Base': 1, + 'items.food.Cake_CottonCandyBlue': 1, + 'items.food.Cake_CottonCandyPink': 1, + 'items.food.Cake_Shade': 1, + 'items.food.Cake_White': 1, + 'items.food.Cake_Golden': 1, + 'items.food.Cake_Zombie': 1, + 'items.food.Cake_Desert': 1, + 'items.food.Cake_Red': 1, + 'achievements.habitBirthdays': 1, }; - dbUsers.update({_id:user._id}, {$set:set}); - dbUsers.update({_id:user._id}, {$inc:inc}); + dbUsers.update({_id: user._id}, {$set: set}); + dbUsers.update({_id: user._id}, {$inc: inc}); - if (count%progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); }); -function displayData() { - console.warn('\n' + count + ' users processed\n'); +function displayData () { + console.warn(`\n${ count } users processed\n`); return exiting(0); } -function exiting(code, msg) { +function exiting (code, msg) { code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } + if (code && !msg) { + msg = 'ERROR!'; + } if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } + if (code) { + console.error(msg); + } else { + console.log(msg); + } } process.exit(code); -} +} diff --git a/migrations/archive/2016/20160521_veteran_ladder.js b/migrations/archive/2016/20160521_veteran_ladder.js new file mode 100644 index 0000000000..cc538faf6d --- /dev/null +++ b/migrations/archive/2016/20160521_veteran_ladder.js @@ -0,0 +1,89 @@ +let migrationName = '20160521_veteran_ladder.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award Gilded Turkey pet to Turkey mount owners, Turkey Mount if they only have Turkey Pet, + * and Turkey Pet otherwise + */ + +let dbserver = 'localhost:27017'; // FOR TEST DATABASE +// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE +let dbname = 'habitrpg'; + +let mongo = require('mongoskin'); +let _ = 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 + +let dbUsers = mongo.db(`${dbserver }/${ dbname }?auto_reconnect`).collection('users'); + +// specify a query to limit the affected users (empty for all users): +let query = { + 'auth.timestamps.loggedin': {$gt: new Date('2016-05-01')}, // remove when running migration a second time +}; + +// specify fields we are interested in to limit retrieved data (empty if we're not reading data): +let fields = { + migration: 1, + 'items.pets.Wolf-Veteran': 1, + 'items.pets.Tiger-Veteran': 1, +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + return displayData(); + } + count++; + + // specify user data to change: + let set = {}; + if (user.migration !== migrationName) { + if (user.items.pets['Tiger-Veteran']) { + set = {migration: migrationName, 'items.pets.Lion-Veteran': 5}; + } else if (user.items.pets['Wolf-Veteran']) { + set = {migration: migrationName, 'items.pets.Tiger-Veteran': 5}; + } else { + set = {migration: migrationName, 'items.pets.Wolf-Veteran': 5}; + } + } + + dbUsers.update({_id: user._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + diff --git a/migrations/archive/2016/20160527_fix_empty_checklist_id.js b/migrations/archive/2016/20160527_fix_empty_checklist_id.js new file mode 100644 index 0000000000..eacb7498de --- /dev/null +++ b/migrations/archive/2016/20160527_fix_empty_checklist_id.js @@ -0,0 +1,91 @@ +let uuid = require('uuid').v4; +let mongo = require('mongodb').MongoClient; +let _ = 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 + +let taskIds = require('checklists-no-id.json').map(function (obj) { + return obj._id; +}); + +// Fix empty task.checklistt.id + +let progressCount = 100; +let count = 0; + +function displayData () { + console.warn(`\n${ count } tasks processed\n`); + return exiting(0); +} + +function exiting (code, msg) { + code = code || 0; // 0 = success + + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } +} + +mongo.connect('db url') + .then(function (db) { + let dbTasks = db.collection('tasks'); + + // specify a query to limit the affected tasks (empty for all tasks): + let query = { + _id: { $in: taskIds }, + }; + + // specify fields we are interested in to limit retrieved data (empty if we're not reading data): + let fields = { + checklist: 1, + }; + + console.warn('Updating tasks...'); + + dbTasks.find(query, fields, {batchSize: 250}).toArray(function (err, tasks) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + + tasks.forEach(function (task) { + let checklist = task.checklist || []; + checklist.forEach(function (item) { + if (!item.id || item.id === '') { + item.id = uuid(); + } + }); + + // specify user data to change: + let set = { + checklist, + }; + // console.log(set); + + dbTasks.update({_id: task._id}, {$set: set}, function (err, res) { + if (err) console.error('Error while updating', err); + }); + + count++; + if (count % progressCount === 0) console.warn(`${count } ${ task._id}`); + }); + + if (count === tasks.length) { + console.warn('All appropriate tasks found and modified.'); + return displayData(); + } + }); + }) + .catch(function (err) { + throw err; + }); \ No newline at end of file diff --git a/migrations/20160529_fix_challenges.js b/migrations/archive/2016/20160529_fix_challenges.js similarity index 87% rename from migrations/20160529_fix_challenges.js rename to migrations/archive/2016/20160529_fix_challenges.js index 51c31ee0c9..035a5c30ed 100644 --- a/migrations/20160529_fix_challenges.js +++ b/migrations/archive/2016/20160529_fix_challenges.js @@ -1,6 +1,6 @@ 'use strict'; -/**************************************** +/** ************************************** * Reason: After the api v3 maintenance migration, some challenge tasks * became unlinked from their challenges. We're still not sure why, * but this re-links them @@ -33,7 +33,7 @@ const TASK_UPDATE_DATA = require('../challenge_fixes.json'); let db; let count = 0; -var timer = setInterval(function(){ +let timer = setInterval(function () { count++; if (count % 30 === 0) { logger.warn('Process has been running for', count / 60, 'minutes'); @@ -48,7 +48,7 @@ connectToDb() // .then(correctUserTasks) .then(updateTasks) .then(closeDb) - .catch(reportError) + .catch(reportError); function connectToDb () { return new Promise((resolve, reject) => { @@ -81,11 +81,11 @@ function findBrokenChallengeTasks () { logger.info('Looking for broken tasks...'); // return db.collection('tasks').find({'challenge.broken': 'CHALLENGE_TASK_NOT_FOUND'}).toArray() - return db.collection('tasks').find({'_id': { '$in': TASK_IDS }}).toArray() - .then((tasks) => { - logger.success('Found', tasks.length, 'broken tasks.'); - return Promise.resolve(tasks); - }); + return db.collection('tasks').find({_id: { $in: TASK_IDS }}).toArray() + .then((tasks) => { + logger.success('Found', tasks.length, 'broken tasks.'); + return Promise.resolve(tasks); + }); } function getDataFromTasks (tasks) { @@ -114,12 +114,12 @@ function getDataFromTasks (tasks) { function getUserChallenges (data) { logger.info('Collecting user challenges...'); - return db.collection('users').find({_id: { '$in': data.users }}, {challenges: 1}).toArray().then((docs) => { + return db.collection('users').find({_id: { $in: data.users }}, {challenges: 1}).toArray().then((docs) => { logger.success('Found', docs.length, 'users from broken challenge tasks.'); let challenges = []; docs.forEach((user) => { - challenges.push.apply(challenges, user.challenges); + challenges.push(...user.challenges); }); challenges = unique(challenges); @@ -146,7 +146,7 @@ function getUserChallenges (data) { function getChallengeTasks (data) { logger.info('Looking up original challenge tasks...'); - return db.collection('tasks').find({'userId': null, 'challenge.id': { '$in': data.challenges }}, [ 'text', 'type', 'challenge', '_legacyId' ]).toArray().then((docs) => { + return db.collection('tasks').find({userId: null, 'challenge.id': { $in: data.challenges }}, ['text', 'type', 'challenge', '_legacyId']).toArray().then((docs) => { logger.success('Found', docs.length, 'challenge tasks.'); let challengeTasks = {}; @@ -190,14 +190,14 @@ function correctUserTasks (data) { let foundTask = userTasks.find((task) => { return TASK_IDS.indexOf(task._id) > -1 && task._legacyId === legacyId && task.type === type && task.text === text; - }) + }); if (foundTask && !tasksToUpdate[foundTask._id]) { tasksToUpdate[foundTask._id] = { id: chal, broken: null, // NOTE: this caused a lot of problems taskId, - } + }; } else if (foundTask && taskId !== tasksToUpdate[foundTask._id].taskId) { logger.error('Duplicate task found, id:', foundTask._id); duplicateTasks[foundTask._id] = duplicateTasks[foundTask._id] || [tasksToUpdate[foundTask._id].taskId]; @@ -236,12 +236,12 @@ function updateTasks (data) { logger.info(promiseCount, 'updates started'); } - return db.collection('tasks').findOneAndUpdate({_id: taskId, 'challenge.broken': 'CHALLENGE_TASK_NOT_FOUND'}, {$set: {challenge: tasksToUpdate[taskId]}}, {returnOriginal: false}) + return db.collection('tasks').findOneAndUpdate({_id: taskId, 'challenge.broken': 'CHALLENGE_TASK_NOT_FOUND'}, {$set: {challenge: tasksToUpdate[taskId]}}, {returnOriginal: false}); } return Promise.map(taskIdsToUpdate, queue.wrap(updateTaskById)).then((result) => { - let updates = result.filter(res => res.lastErrorObject.updatedExisting) - let failures = result.filter(res => !res.lastErrorObject.updatedExisting); + let updates = result.filter(res => res && res.lastErrorObject.updatedExisting); + let failures = result.filter(res => res && !res.lastErrorObject.updatedExisting); logger.success(updates.length, 'tasks have been fixed'); @@ -256,9 +256,9 @@ function updateTasks (data) { } function closeDb (data) { - logger.success('The process took ' + count + ' seconds'); + logger.success(`The process took ${ count } seconds`); - clearInterval(timer) + clearInterval(timer); db.close(); } diff --git a/migrations/20160530_fix_tasks_from_null_value_in_challenges_broken.js b/migrations/archive/2016/20160530_fix_tasks_from_null_value_in_challenges_broken.js similarity index 88% rename from migrations/20160530_fix_tasks_from_null_value_in_challenges_broken.js rename to migrations/archive/2016/20160530_fix_tasks_from_null_value_in_challenges_broken.js index f5ae6c9925..e010e34b35 100644 --- a/migrations/20160530_fix_tasks_from_null_value_in_challenges_broken.js +++ b/migrations/archive/2016/20160530_fix_tasks_from_null_value_in_challenges_broken.js @@ -1,6 +1,6 @@ 'use strict'; -/**************************************** +/** ************************************** * Reason: After running the 20160529_fix_challenges.js migration * challenge.broken was set to null, which is not a valid value * which caused cron to fail and run many times, messing up daily values, @@ -28,7 +28,7 @@ const NEW_DB_URI = 'mongodb://username:password@dsXXXXXX-a0.mlab.com:XXXXX,dsXXX let oldDb, newDb, OldTasks, NewTasks, OldUsers, NewUsers; let count = 0; -var timer = setInterval(function(){ +let timer = setInterval(function () { count++; if (count % 30 === 0) { logger.warn('Process has been running for', count / 60, 'minutes'); @@ -46,7 +46,7 @@ Promise.all([ .then(getValuesOfOldTasksFromBackup) .then(updateNewTasks) .then(closeDb) - .catch(reportError) + .catch(reportError); function connectToDb (dbUri, type) { return new Promise((resolve, reject) => { @@ -92,14 +92,12 @@ function getAffectedUsersEmail () { users.forEach((user) => { if (user.preferences.emailNotifications.newPM && user.inbox.optOut !== true) { pmsWithEmail.push(user._id); + } else if (user.auth && user.auth.local && user.auth.local.email) { + emails.push(user.auth.local.email); + } else if (user.auth && user.auth.facebook && user.auth.facebook.email) { + emails.push(user.auth.facebook.email); } else { - if (user.auth && user.auth.local && user.auth.local.email) { - emails.push(user.auth.local.email); - } else if (user.auth && user.auth.facebook && user.auth.facebook.email) { - emails.push(user.auth.facebook.email); - } else { - missing.push(user._id); - } + missing.push(user._id); } }); @@ -156,7 +154,9 @@ function determineIfTasksNeedAdjusting (tasks) { let prodTask = tasks[i]; let backupTask = backupTasksAsObject[prodTask._id]; - if (!backupTask) { continue; } + if (!backupTask) { + continue; + } let historyDifference = prodTask.history.length - backupTask.history.length; @@ -194,16 +194,16 @@ function updateNewTasks (oldTasks) { function updateTaskById (task) { promiseCount++; - if (promiseCount % 100=== 0) { + if (promiseCount % 100 === 0) { logger.info(promiseCount, 'updates started'); } - return NewTasks.findOneAndUpdate({_id: task._id}, {$set: {value: task.value, streak: task.streak, history: task.history}}, {returnOriginal: false}) + return NewTasks.findOneAndUpdate({_id: task._id}, {$set: {value: task.value, streak: task.streak, history: task.history}}, {returnOriginal: false}); } return Promise.map(oldTasks, queue.wrap(updateTaskById)).then((result) => { - let updates = result.filter(res => res.lastErrorObject && res.lastErrorObject.updatedExisting) - let failures = result.filter(res => !(res.lastErrorObject && res.lastErrorObject.updatedExisting)); + let updates = result.filter(res => res && res.lastErrorObject && res.lastErrorObject.updatedExisting); + let failures = result.filter(res => res && !(res.lastErrorObject && res.lastErrorObject.updatedExisting)); logger.success(updates.length, 'tasks have been fixed'); @@ -220,9 +220,9 @@ function unique (array) { } function closeDb () { - logger.success('The process took ' + count + ' seconds'); + logger.success(`The process took ${ count } seconds`); - clearInterval(timer) + clearInterval(timer); oldDb.close(); newDb.close(); diff --git a/migrations/20160602_convert_quest_collection.js b/migrations/archive/2016/20160602_convert_quest_collection.js similarity index 87% rename from migrations/20160602_convert_quest_collection.js rename to migrations/archive/2016/20160602_convert_quest_collection.js index 172575cf1e..04db90f1c2 100644 --- a/migrations/20160602_convert_quest_collection.js +++ b/migrations/archive/2016/20160602_convert_quest_collection.js @@ -1,6 +1,6 @@ 'use strict'; -/**************************************** +/** ************************************** * Author: Blade Barringer @crookedneighbor * * Reason: Collection quest data on the client is unreliable @@ -33,7 +33,7 @@ const COLLECTION_QUESTS = [ 'moonstone1', 'goldenknight1', 'dilatoryDistress1', -] +]; let Users, Groups; @@ -43,14 +43,14 @@ connectToDb(DB_URI).then((db) => { return Promise.resolve(); }) -.then(findUsersWithCollectionData) -.then(getUsersCollectionData) -.then(transformCollectionData) -.then(cleanUpEmptyCollectionData) -.then(() => { - timer.stop(); - closeDb(); -}).catch(reportError); + .then(findUsersWithCollectionData) + .then(getUsersCollectionData) + .then(transformCollectionData) + .then(cleanUpEmptyCollectionData) + .then(() => { + timer.stop(); + closeDb(); + }).catch(reportError); function reportError (err) { logger.error('Uh oh, an error occurred'); @@ -68,14 +68,14 @@ function findUsersWithCollectionData () { let members = groups.reduce((array, party) => { let questers = Object.keys(party.quest.members); - array.push.apply(array, questers); + array.push(...questers); return array; }, []); logger.success('Found', members.length, 'users on collection quests'); return Promise.resolve(members); - }) + }); } function getUsersCollectionData (users) { @@ -89,7 +89,7 @@ function getUsersCollectionData (users) { if (!collect) return array; if (typeof collect === 'number') return array; - for (var i in collect) { + for (let i in collect) { if (collect.hasOwnProperty(i)) { total += collect[i]; } @@ -104,7 +104,7 @@ function getUsersCollectionData (users) { } function updateUserById (user) { - return Users.findOneAndUpdate({_id: user._id}, {$set: {'party.quest.progress.collect': user.collect}}, {returnOriginal: false}) + return Users.findOneAndUpdate({_id: user._id}, {$set: {'party.quest.progress.collect': user.collect}}, {returnOriginal: false}); } @@ -114,8 +114,8 @@ function transformCollectionData (users) { logger.info('About to update', users.length, 'user collection items...'); return Promise.map(users, queue.wrap(updateUserById)).then((result) => { - let updates = result.filter(res => res.lastErrorObject && res.lastErrorObject.updatedExisting) - let failures = result.filter(res => !(res.lastErrorObject && res.lastErrorObject.updatedExisting)); + let updates = result.filter(res => res && res.lastErrorObject && res.lastErrorObject.updatedExisting); + let failures = result.filter(res => res && !(res.lastErrorObject && res.lastErrorObject.updatedExisting)); logger.success(updates.length, 'users have been fixed'); diff --git a/migrations/20160605_convert_quest_collection_again.js b/migrations/archive/2016/20160605_convert_quest_collection_again.js similarity index 86% rename from migrations/20160605_convert_quest_collection_again.js rename to migrations/archive/2016/20160605_convert_quest_collection_again.js index a4e2e0b29e..5b9cc1e95c 100644 --- a/migrations/20160605_convert_quest_collection_again.js +++ b/migrations/archive/2016/20160605_convert_quest_collection_again.js @@ -1,6 +1,6 @@ 'use strict'; -/**************************************** +/** ************************************** * Author: Blade Barringer @crookedneighbor * * Reason: The android app uses quest.progress.collect @@ -30,7 +30,7 @@ const COLLECTION_QUESTS = [ 'moonstone1', 'goldenknight1', 'dilatoryDistress1', -] +]; let Users, Groups; @@ -40,14 +40,14 @@ connectToDb(DB_URI).then((db) => { return Promise.resolve(); }) -.then(findUsersWithCollectionData) -.then(getUsersCollectionData) -.then(transferCollectionData) -.then(cleanUpBadCollectionData) -.then(() => { - timer.stop(); - closeDb(); -}).catch(reportError); + .then(findUsersWithCollectionData) + .then(getUsersCollectionData) + .then(transferCollectionData) + .then(cleanUpBadCollectionData) + .then(() => { + timer.stop(); + closeDb(); + }).catch(reportError); function reportError (err) { logger.error('Uh oh, an error occurred'); @@ -65,14 +65,14 @@ function findUsersWithCollectionData () { let members = groups.reduce((array, party) => { let questers = Object.keys(party.quest.members); - array.push.apply(array, questers); + array.push(...questers); return array; }, []); logger.success('Found', members.length, 'users on collection quests'); return Promise.resolve(members); - }) + }); } function getUsersCollectionData (users) { @@ -95,7 +95,7 @@ function getUsersCollectionData (users) { } function updateUserById (user) { - return Users.findOneAndUpdate({_id: user._id}, {$set: {'party.quest.progress.collectedItems': user.collect}}, {returnOriginal: false}) + return Users.findOneAndUpdate({_id: user._id}, {$set: {'party.quest.progress.collectedItems': user.collect}}, {returnOriginal: false}); } @@ -105,8 +105,8 @@ function transferCollectionData (users) { logger.info('About to update', users.length, 'user collection items...'); return Promise.map(users, queue.wrap(updateUserById)).then((result) => { - let updates = result.filter(res => res.lastErrorObject && res.lastErrorObject.updatedExisting) - let failures = result.filter(res => !(res.lastErrorObject && res.lastErrorObject.updatedExisting)); + let updates = result.filter(res => res && res.lastErrorObject && res.lastErrorObject.updatedExisting); + let failures = result.filter(res => res && !(res.lastErrorObject && res.lastErrorObject.updatedExisting)); logger.success(updates.length, 'users have been fixed'); diff --git a/migrations/20160615_fix_bad_emails.js b/migrations/archive/2016/20160615_fix_bad_emails.js similarity index 82% rename from migrations/20160615_fix_bad_emails.js rename to migrations/archive/2016/20160615_fix_bad_emails.js index 0413452e31..97f29cefcf 100644 --- a/migrations/20160615_fix_bad_emails.js +++ b/migrations/archive/2016/20160615_fix_bad_emails.js @@ -1,6 +1,6 @@ 'use strict'; -/**************************************** +/** ************************************** * Author: Blade Barringer @crookedneighbor * * Reason: Old code didn't properly validate email @@ -33,11 +33,11 @@ connectToDb(DB_URI).then((db) => { }) // cached the lookup as a json file // .then(findUsersWithBadEmails) -.then(correctEmails) -.then(() => { - timer.stop(); - closeDb(); -}).catch(reportError); + .then(correctEmails) + .then(() => { + timer.stop(); + closeDb(); + }).catch(reportError); function reportError (err) { logger.error('Uh oh, an error occurred'); @@ -58,8 +58,8 @@ function findUsersWithBadEmails (users) { return { _id: user._id, email: user.auth.local.email }; }); - logger.warn('number of invalid emails:', invalidEmails.length) - console.log(result) + logger.warn('number of invalid emails:', invalidEmails.length); + console.log(result); return Promise.resolve(invalidEmails); }); @@ -68,8 +68,8 @@ function findUsersWithBadEmails (users) { function updateUserById (user) { return Users.findOneAndUpdate({ _id: user._id}, - {$set: {'auth.local.email': user._id + '@example.com'} - }, {returnOriginal: false}) + {$set: {'auth.local.email': `${user._id }@example.com`}, + }, {returnOriginal: false}); } // cached lookup of bad emails @@ -81,8 +81,8 @@ function correctEmails () { logger.warn('About to update', emails.length, 'user email addresses...'); return Promise.map(emails, queue.wrap(updateUserById)).then((result) => { - let updates = result.filter(res => res.lastErrorObject && res.lastErrorObject.updatedExisting) - let failures = result.filter(res => !(res.lastErrorObject && res.lastErrorObject.updatedExisting)); + let updates = result.filter(res => res && res.lastErrorObject && res.lastErrorObject.updatedExisting); + let failures = result.filter(res => res && !(res.lastErrorObject && res.lastErrorObject.updatedExisting)); logger.warn(updates.length, 'users have been fixed'); diff --git a/migrations/archive/2016/20160731_naming_day.js b/migrations/archive/2016/20160731_naming_day.js new file mode 100644 index 0000000000..dfbbf362b9 --- /dev/null +++ b/migrations/archive/2016/20160731_naming_day.js @@ -0,0 +1,89 @@ +let migrationName = '20160731_naming_day.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award Royal Purple Gryphon pet to Royal Purple Gryphon mount owners, mount to everyone else + */ + +let mongo = require('mongoskin'); + +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE + +let dbUsers = mongo.db(connectionString).collection('users'); + +// specify a query to limit the affected users (empty for all users): +let query = { + migration: {$ne: migrationName}, + 'auth.timestamps.loggedin': {$gt: new Date('2016-07-30')}, // Extend timeframe each run of migration +}; + +// specify fields we are interested in to limit retrieved data (empty if we're not reading data): +let fields = { + 'items.mounts': 1, +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + setTimeout(displayData, 300000); + return; + } + count++; + + // specify user data to change: + let set = {}; + let inc = {}; + inc = { + 'achievements.habiticaDays': 1, + 'items.food.Cake_Skeleton': 1, + 'items.food.Cake_Base': 1, + 'items.food.Cake_CottonCandyBlue': 1, + 'items.food.Cake_CottonCandyPink': 1, + 'items.food.Cake_Shade': 1, + 'items.food.Cake_White': 1, + 'items.food.Cake_Golden': 1, + 'items.food.Cake_Zombie': 1, + 'items.food.Cake_Desert': 1, + 'items.food.Cake_Red': 1, + }; + if (user.items.mounts['Gryphon-RoyalPurple']) { + set = {migration: migrationName, 'items.pets.Gryphon-RoyalPurple': 5}; + } else { + set = {migration: migrationName, 'items.mounts.Gryphon-RoyalPurple': true}; + } + + dbUsers.update({_id: user._id}, {$set: set, $inc: inc}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + diff --git a/migrations/archive/2016/20160731_takeThis.js b/migrations/archive/2016/20160731_takeThis.js new file mode 100644 index 0000000000..6a7e858840 --- /dev/null +++ b/migrations/archive/2016/20160731_takeThis.js @@ -0,0 +1,78 @@ +let migrationName = '20160731_takeThis.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award Take This Sword to Take This challenge participants who already own the Shield + * and Take This Shield to the rest of the list + */ + +let mongo = require('mongoskin'); + +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE + +let dbUsers = mongo.db(connectionString).collection('users'); + +// specify a query to limit the affected users (empty for all users): +let query = { + migration: {$ne: migrationName}, + 'auth.timestamps.loggedin': {$gt: new Date('2016-07-30')}, // Extend timeframe each run of migration + challenges: {$in: ['da8859b2-5c6e-4aa5-b8b2-8db93d5de9fc']}, +}; + +// specify fields we are interested in to limit retrieved data (empty if we're not reading data): +let fields = { + 'items.gear.owned': 1, +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + setTimeout(displayData, 300000); + return; + } + count++; + + // specify user data to change: + let set = {}; + + if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.weapon_special_takeThis': false}; + } else { + set = {migration: migrationName, 'items.gear.owned.shield_special_takeThis': false}; + } + + dbUsers.update({_id: user._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + diff --git a/migrations/archive/2016/20160831_takeThis.js b/migrations/archive/2016/20160831_takeThis.js new file mode 100644 index 0000000000..733b514b19 --- /dev/null +++ b/migrations/archive/2016/20160831_takeThis.js @@ -0,0 +1,79 @@ +let migrationName = '20160831_takeThis.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award Take This Sword to Take This challenge participants who already own the Shield + * and Take This Shield to the rest of the list + */ + +let mongo = require('mongoskin'); + +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE + +let dbUsers = mongo.db(connectionString).collection('users'); + +// specify a query to limit the affected users (empty for all users): +let query = { + migration: {$ne: migrationName}, + challenges: {$in: ['ee2b3c87-13f0-422a-af3c-309102d4f7e6']}, +}; + +// specify fields we are interested in to limit retrieved data (empty if we're not reading data): +let fields = { + 'items.gear.owned': 1, +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + setTimeout(displayData, 300000); + return; + } + count++; + + // specify user data to change: + let set = {}; + + if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.armor_special_takeThis': false}; + } else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.weapon_special_takeThis': false}; + } else { + set = {migration: migrationName, 'items.gear.owned.shield_special_takeThis': false}; + } + + dbUsers.update({_id: user._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + diff --git a/migrations/20161002_add_missing_webhook_type.js b/migrations/archive/2016/20161002_add_missing_webhook_type.js similarity index 81% rename from migrations/20161002_add_missing_webhook_type.js rename to migrations/archive/2016/20161002_add_missing_webhook_type.js index f3f7b93422..8a0dd5ecbe 100644 --- a/migrations/20161002_add_missing_webhook_type.js +++ b/migrations/archive/2016/20161002_add_missing_webhook_type.js @@ -1,6 +1,6 @@ 'use strict'; -/**************************************** +/** ************************************** * Author: Blade Barringer @crookedneighbor * * Reason: Webhooks have been moved from @@ -24,7 +24,7 @@ const MIGRATION_NAME = '20161002_add_missing_webhook_type.js'; const DB_URI = 'mongodb://localhost/prod-copy-1'; const LOGGEDIN_DATE_RANGE = { - $gte: new Date("2016-09-30T00:00:00.000Z"), + $gte: new Date('2016-09-30T00:00:00.000Z'), // $lte: new Date("2016-09-25T00:00:00.000Z"), }; @@ -33,12 +33,12 @@ let Users; connectToDb(DB_URI).then((db) => { Users = db.collection('users'); }) -.then(findUsersWithWebhooks) -.then(correctWebhooks) -.then(() => { - timer.stop(); - closeDb(); -}).catch(reportError); + .then(findUsersWithWebhooks) + .then(correctWebhooks) + .then(() => { + timer.stop(); + closeDb(); + }).catch(reportError); function reportError (err) { logger.error('Uh oh, an error occurred'); @@ -53,14 +53,14 @@ const USER_IDS = require('../../ids_of_webhooks_to_update.json'); function findUsersWithWebhooks () { logger.warn('Fetching users with webhooks...'); - return Users.find({'_id': {$in: USER_IDS}}, ['preferences.webhooks']).toArray().then((docs) => { + return Users.find({_id: {$in: USER_IDS}}, ['preferences.webhooks']).toArray().then((docs) => { // return Users.find({'preferences.webhooks': {$ne: {} }}, ['preferences.webhooks']).toArray().then((docs) => { // TODO: Run this after the initial migration to catch any webhooks that may have been aded since the prod backup download // return Users.find({'preferences.webhooks': {$ne: {} }, 'auth.timestamps.loggedin': LOGGEDIN_DATE_RANGE}, ['preferences.webhooks']).toArray().then((docs) => { let updates = docs.map((user) => { let oldWebhooks = user.preferences.webhooks; let webhooks = Object.keys(oldWebhooks).map((id) => { - let webhook = oldWebhooks[id] + let webhook = oldWebhooks[id]; webhook.type = 'taskActivity'; webhook.label = ''; @@ -79,7 +79,7 @@ function findUsersWithWebhooks () { return { webhooks, id: user._id, - } + }; }); return Promise.resolve(updates); @@ -92,8 +92,8 @@ function updateUserById (user) { return Users.findOneAndUpdate({ _id: userId}, - {$set: {webhooks: webhooks, migration: MIGRATION_NAME} - }, {returnOriginal: false}) + {$set: {webhooks, migration: MIGRATION_NAME}, + }, {returnOriginal: false}); } function correctWebhooks (users) { @@ -102,8 +102,8 @@ function correctWebhooks (users) { logger.warn('About to update', users.length, 'users...'); return Promise.map(users, queue.wrap(updateUserById)).then((result) => { - let updates = result.filter(res => res.lastErrorObject && res.lastErrorObject.updatedExisting) - let failures = result.filter(res => !(res.lastErrorObject && res.lastErrorObject.updatedExisting)); + let updates = result.filter(res => res && res.lastErrorObject && res.lastErrorObject.updatedExisting); + let failures = result.filter(res => res && !(res.lastErrorObject && res.lastErrorObject.updatedExisting)); logger.warn(updates.length, 'users have been fixed'); diff --git a/migrations/archive/2016/20161002_takeThis.js b/migrations/archive/2016/20161002_takeThis.js new file mode 100644 index 0000000000..f8880a6c7b --- /dev/null +++ b/migrations/archive/2016/20161002_takeThis.js @@ -0,0 +1,80 @@ +let migrationName = '20161002_takeThis.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award Take This ladder items to participants in this month's challenge + */ + +let mongo = require('mongoskin'); + +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE + +let dbUsers = mongo.db(connectionString).collection('users'); + +// specify a query to limit the affected users (empty for all users): +let query = { + migration: {$ne: migrationName}, + challenges: {$in: ['4bbf63b5-10bc-49f9-8e95-5bd2ac99cd1c']}, +}; + +// specify fields we are interested in to limit retrieved data (empty if we're not reading data): +let fields = { + 'items.gear.owned': 1, +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + setTimeout(displayData, 300000); + return; + } + count++; + + // specify user data to change: + let set = {}; + + if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.head_special_takeThis': false}; + } else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.armor_special_takeThis': false}; + } else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.weapon_special_takeThis': false}; + } else { + set = {migration: migrationName, 'items.gear.owned.shield_special_takeThis': false}; + } + + dbUsers.update({_id: user._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + diff --git a/migrations/archive/2016/20161030-jackolanterns.js b/migrations/archive/2016/20161030-jackolanterns.js new file mode 100644 index 0000000000..6be94e311f --- /dev/null +++ b/migrations/archive/2016/20161030-jackolanterns.js @@ -0,0 +1,93 @@ +let migrationName = '20161030-jackolanterns.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * set the newStuff flag in all user accounts so they see a Bailey message + */ + +let mongo = require('mongoskin'); + +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE + +let dbUsers = mongo.db(connectionString).collection('users'); + +// specify a query to limit the affected users (empty for all users): +let query = { + 'auth.timestamps.loggedin': {$gt: new Date('2016-10-01')}, // remove when running migration a second time +}; + +// specify fields we are interested in to limit retrieved data (empty if we're not reading data): +let fields = { + migration: 1, + 'items.pets.JackOLantern-Base': 1, + 'items.mounts.JackOLantern-Base': 1, +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + setTimeout(displayData, 300000); + return; + } + count++; + + // specify user data to change: + let set = {}; + let inc = {}; + if (user.migration !== migrationName) { + if (user.items.mounts['JackOLantern-Base']) { + set = {migration: migrationName, 'items.pets.JackOLantern-Ghost': 5}; + } else if (user.items.pets['JackOLantern-Base']) { + set = {migration: migrationName, 'items.mounts.JackOLantern-Base': true}; + } else { + set = {migration: migrationName, 'items.pets.JackOLantern-Base': 5}; + } + inc = { + 'items.food.Candy_Base': 1, + 'items.food.Candy_CottonCandyBlue': 1, + 'items.food.Candy_CottonCandyPink': 1, + 'items.food.Candy_Desert': 1, + 'items.food.Candy_Golden': 1, + 'items.food.Candy_Red': 1, + 'items.food.Candy_Shade': 1, + 'items.food.Candy_Skeleton': 1, + 'items.food.Candy_White': 1, + 'items.food.Candy_Zombie': 1, + }; + } + + dbUsers.update({_id: user._id}, {$set: set, $inc: inc}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + diff --git a/migrations/archive/2016/20161102_takeThis.js b/migrations/archive/2016/20161102_takeThis.js new file mode 100644 index 0000000000..137dc17dee --- /dev/null +++ b/migrations/archive/2016/20161102_takeThis.js @@ -0,0 +1,82 @@ +let migrationName = '20161102_takeThis.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award Take This ladder items to participants in this month's challenge + */ + +let mongo = require('mongoskin'); + +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE + +let dbUsers = mongo.db(connectionString).collection('users'); + +// specify a query to limit the affected users (empty for all users): +let query = { + migration: {$ne: migrationName}, + challenges: {$in: ['d1be0965-e909-4d30-82fa-9a0011f885b2']}, +}; + +// specify fields we are interested in to limit retrieved data (empty if we're not reading data): +let fields = { + 'items.gear.owned': 1, +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + setTimeout(displayData, 300000); + return; + } + count++; + + // specify user data to change: + let set = {}; + + if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.body_special_takeThis': false}; + } else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.head_special_takeThis': false}; + } else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.armor_special_takeThis': false}; + } else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.weapon_special_takeThis': false}; + } else { + set = {migration: migrationName, 'items.gear.owned.shield_special_takeThis': false}; + } + + dbUsers.update({_id: user._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + diff --git a/migrations/archive/2016/20161122_turkey_ladder.js b/migrations/archive/2016/20161122_turkey_ladder.js new file mode 100644 index 0000000000..9853af9212 --- /dev/null +++ b/migrations/archive/2016/20161122_turkey_ladder.js @@ -0,0 +1,81 @@ +let migrationName = '20161122_turkey_ladder.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Yearly Turkey Day award. Turkey pet, Turkey mount, Gilded Turkey pet, Gilded Turkey mount + */ + +let mongo = require('mongoskin'); + +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE + +let dbUsers = mongo.db(connectionString).collection('users'); + +// specify a query to limit the affected users (empty for all users): +let query = { + migration: {$ne: migrationName}, + 'auth.timestamps.loggedin': {$gt: new Date('2016-10-31')}, // Extend timeframe each run of migration +}; + +// specify fields we are interested in to limit retrieved data (empty if we're not reading data): +let fields = { + migration: 1, + 'items.mounts': 1, + 'items.pets': 1, +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + setTimeout(displayData, 300000); + return; + } + count++; + + // specify user data to change: + let set = {}; + + if (user.items.pets['Turkey-Gilded']) { + set = {migration: migrationName, 'items.mounts.Turkey-Gilded': true}; + } else if (user.items.mounts['Turkey-Base']) { + set = {migration: migrationName, 'items.pets.Turkey-Gilded': 5}; + } else if (user.items.pets['Turkey-Base']) { + set = {migration: migrationName, 'items.mounts.Turkey-Base': true}; + } else { + set = {migration: migrationName, 'items.pets.Turkey-Base': 5}; + } + + dbUsers.update({_id: user._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} diff --git a/migrations/archive/2016/20161230_nye_hats.js b/migrations/archive/2016/20161230_nye_hats.js new file mode 100644 index 0000000000..a4c9b491a6 --- /dev/null +++ b/migrations/archive/2016/20161230_nye_hats.js @@ -0,0 +1,80 @@ +let migrationName = '20161230_nye_hats.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Yearly New Year's party hat award + */ + +let mongo = require('mongoskin'); + +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE + +let dbUsers = mongo.db(connectionString).collection('users'); + +// specify a query to limit the affected users (empty for all users): +let query = { + migration: {$ne: migrationName}, + 'auth.timestamps.loggedin': {$gt: new Date('2016-11-30')}, // Remove after first run +}; + +// specify fields we are interested in to limit retrieved data (empty if we're not reading data): +let fields = { + 'items.gear.owned': 1, +}; + +console.warn('Updating users...'); +let progressCount = 1000; +let count = 0; +dbUsers.findEach(query, fields, {batchSize: 250}, function (err, user) { + if (err) { + return exiting(1, `ERROR! ${ err}`); + } + if (!user) { + console.warn('All appropriate users found and modified.'); + setTimeout(displayData, 300000); + return; + } + count++; + + // specify user data to change: + let set = {}; + + if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.head_special_nye2016': false}; + } else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.head_special_nye2015': false}; + } else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.head_special_nye2014': false}; + } else { + set = {migration: migrationName, 'items.gear.owned.head_special_nye': false}; + } + + dbUsers.update({_id: user._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +}); + + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + diff --git a/migrations/20170111_announce_collection_quest_change_in_parties.js b/migrations/archive/2017/20170111_announce_collection_quest_change_in_parties.js similarity index 78% rename from migrations/20170111_announce_collection_quest_change_in_parties.js rename to migrations/archive/2017/20170111_announce_collection_quest_change_in_parties.js index aabd6703f8..35361d4094 100644 --- a/migrations/20170111_announce_collection_quest_change_in_parties.js +++ b/migrations/archive/2017/20170111_announce_collection_quest_change_in_parties.js @@ -1,6 +1,6 @@ 'use strict'; -/**************************************** +/** ************************************** * Author: @Alys * * Reason: Collection quests are being changed @@ -43,13 +43,13 @@ connectToDb(DB_URI).then((db) => { return Promise.resolve(); }) -.then(findPartiesWithCollectionQuest) + .then(findPartiesWithCollectionQuest) // .then(displayGroups) // for testing only -.then(addMessageToGroups) -.then(() => { - timer.stop(); - closeDb(); -}).catch(reportError); + .then(addMessageToGroups) + .then(() => { + timer.stop(); + closeDb(); + }).catch(reportError); function reportError (err) { logger.error('Uh oh, an error occurred'); @@ -61,11 +61,11 @@ function reportError (err) { function findPartiesWithCollectionQuest () { logger.info('Looking up groups on collection quests...'); - return Groups.find({'quest.key': {$in: COLLECTION_QUESTS}}, ['name','quest']).toArray().then((groups) => { + return Groups.find({'quest.key': {$in: COLLECTION_QUESTS}}, ['name', 'quest']).toArray().then((groups) => { logger.success('Found', groups.length, 'parties on collection quests'); return Promise.resolve(groups); - }) + }); } function displayGroups (groups) { // for testing only @@ -75,16 +75,16 @@ function displayGroups (groups) { // for testing only } function updateGroupById (group) { - var newMessage = { - 'id' : uuid.v4(), - 'text' : message, - 'timestamp': Date.now(), - 'likes': {}, - 'flags': {}, - 'flagCount': 0, - 'uuid': 'system' + let newMessage = { + id: uuid.v4(), + text: message, + timestamp: Date.now(), + likes: {}, + flags: {}, + flagCount: 0, + uuid: 'system', }; - return Groups.findOneAndUpdate({_id: group._id}, {$push:{"chat" :{$each: [newMessage], $position:0}}}, {returnOriginal: false}); + return Groups.findOneAndUpdate({_id: group._id}, {$push: {chat: {$each: [newMessage], $position: 0}}}, {returnOriginal: false}); // Does not set the newMessage flag for all party members because I don't think it's essential and // I don't want to run the extra code (extra database load, extra opportunity for bugs). } @@ -95,8 +95,8 @@ function addMessageToGroups (groups) { logger.info('About to update', groups.length, 'parties...'); return Promise.map(groups, queue.wrap(updateGroupById)).then((result) => { - let updates = result.filter(res => res.lastErrorObject && res.lastErrorObject.updatedExisting) - let failures = result.filter(res => !(res.lastErrorObject && res.lastErrorObject.updatedExisting)); + let updates = result.filter(res => res && res.lastErrorObject && res.lastErrorObject.updatedExisting); + let failures = result.filter(res => res && !(res.lastErrorObject && res.lastErrorObject.updatedExisting)); logger.success(updates.length, 'parties have been notified'); diff --git a/migrations/archive/2017/20170120_missing_incentive.js b/migrations/archive/2017/20170120_missing_incentive.js new file mode 100644 index 0000000000..626fd900eb --- /dev/null +++ b/migrations/archive/2017/20170120_missing_incentive.js @@ -0,0 +1,118 @@ +let migrationName = '20170120_missing_incentive.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award missing Royal Purple Hatching Potion to users with 55+ check-ins + * Reduce users with impossible check-in counts to a reasonable number + */ + +import monk from 'monk'; +import common from '../website/common'; + +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbUsers = monk(connectionString).get('users', { castIds: false }); + +function processUsers (lastId) { + // specify a query to limit the affected users (empty for all users): + let query = { + loginIncentives: {$gt: 54}, + migration: {$ne: migrationName}, + }; + + if (lastId) { + query._id = { + $gt: lastId, + }; + } + + dbUsers.find(query, { + sort: {_id: 1}, + limit: 250, + fields: [], // specify fields we are interested in to limit retrieved data (empty if we're not reading data): + }) + .then(updateUsers) + .catch(function (err) { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); +} + +let progressCount = 1000; +let count = 0; + +function updateUsers (users) { + if (!users || users.length === 0) { + console.warn('All appropriate users found and modified.'); + displayData(); + return; + } + + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; + + return Promise.all(userPromises) + .then(function () { + processUsers(lastUser._id); + }); +} + +function updateUser (user) { + count++; + + let language = user.preferences.language || 'en'; + let set = {migration: migrationName}; + let inc = {'items.hatchingPotions.RoyalPurple': 1}; + if (user.loginIncentives > 58) { + set = {migration: migrationName, loginIncentives: 58}; + } + let push = { + notifications: { + type: 'LOGIN_INCENTIVE', + data: { + nextRewardAt: 60, + rewardKey: [ + 'Pet_HatchingPotion_Purple', + ], + rewardText: common.i18n.t('potion', {potionType: common.i18n.t('hatchingPotionRoyalPurple', language)}, language), + reward: [ + { + premium: true, + key: 'RoyalPurple', + limited: true, + value: 2, + }, + ], + message: common.i18n.t('unlockedCheckInReward', language), + }, + id: common.uuid(), + }, + }; + + dbUsers.update({_id: user._id}, {$set: set, $push: push, $inc: inc}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +} + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + +module.exports = processUsers; diff --git a/migrations/archive/2017/20170131_habit_birthday.js b/migrations/archive/2017/20170131_habit_birthday.js new file mode 100644 index 0000000000..01efd5ce6a --- /dev/null +++ b/migrations/archive/2017/20170131_habit_birthday.js @@ -0,0 +1,114 @@ +let migrationName = '20170131_habit_birthday.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award 2017 party robes if user has 2016 robes, 2016 robes if they have the 2015 robes, + * 2015 robes if they have the 2014 robes, and 2014 robes otherwise. Also cake! + */ + +let monk = require('monk'); +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbUsers = monk(connectionString).get('users', { castIds: false }); + +function processUsers (lastId) { + // specify a query to limit the affected users (empty for all users): + let query = { + migration: {$ne: migrationName}, + 'auth.timestamps.loggedin': {$gt: new Date('2017-01-24')}, // remove after first run to cover remaining users + }; + + if (lastId) { + query._id = { + $gt: lastId, + }; + } + + dbUsers.find(query, { + sort: {_id: 1}, + limit: 250, + fields: [ // specify fields we are interested in to limit retrieved data (empty if we're not reading data) + 'items.gear.owned', + ], + }) + .then(updateUsers) + .catch(function (err) { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); +} + +let progressCount = 1000; +let count = 0; + +function updateUsers (users) { + if (!users || users.length === 0) { + console.warn('All appropriate users found and modified.'); + displayData(); + return; + } + + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; + + return Promise.all(userPromises) + .then(function () { + processUsers(lastUser._id); + }); +} + +function updateUser (user) { + count++; + + let set = {migration: migrationName}; + if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2016')) { + set['items.gear.owned.armor_special_birthday2017'] = false; + } else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2015')) { + set['items.gear.owned.armor_special_birthday2016'] = false; + } else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday')) { + set['items.gear.owned.armor_special_birthday2015'] = false; + } else { + set['items.gear.owned.armor_special_birthday'] = false; + } + + let inc = { + 'items.food.Cake_Skeleton': 1, + 'items.food.Cake_Base': 1, + 'items.food.Cake_CottonCandyBlue': 1, + 'items.food.Cake_CottonCandyPink': 1, + 'items.food.Cake_Shade': 1, + 'items.food.Cake_White': 1, + 'items.food.Cake_Golden': 1, + 'items.food.Cake_Zombie': 1, + 'items.food.Cake_Desert': 1, + 'items.food.Cake_Red': 1, + 'achievements.habitBirthdays': 1, + }; + + dbUsers.update({_id: user._id}, {$set: set, $inc: inc}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +} + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + +module.exports = processUsers; diff --git a/migrations/archive/2017/20170418_subscriber_jackalopes.js b/migrations/archive/2017/20170418_subscriber_jackalopes.js new file mode 100644 index 0000000000..856f35bd13 --- /dev/null +++ b/migrations/archive/2017/20170418_subscriber_jackalopes.js @@ -0,0 +1,93 @@ +let migrationName = '20170418_subscriber_jackalopes.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award Royal Purple Jackalope pet to all current subscribers + */ + +let monk = require('monk'); +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbUsers = monk(connectionString).get('users', { castIds: false }); +let now = new Date(); + +function processUsers (lastId) { + // specify a query to limit the affected users (empty for all users): + let query = { + 'purchased.plan.customerId': {$type: 2}, + $or: [ + {'purchased.plan.dateTerminated': null}, + {'purchased.plan.dateTerminated': {$exists: false}}, + {'purchased.plan.dateTerminated': {$gt: now}}, + ], + }; + + if (lastId) { + query._id = { + $gt: lastId, + }; + } + + dbUsers.find(query, { + sort: {_id: 1}, + limit: 250, + fields: [], // specify fields we are interested in to limit retrieved data (empty if we're not reading data): + }) + .then(updateUsers) + .catch(function (err) { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); +} + +let progressCount = 1000; +let count = 0; + +function updateUsers (users) { + if (!users || users.length === 0) { + console.warn('All appropriate users found and modified.'); + displayData(); + return; + } + + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; + + return Promise.all(userPromises) + .then(function () { + processUsers(lastUser._id); + }); +} + +function updateUser (user) { + count++; + + let set = {'items.pets.Jackalope-RoyalPurple': 5}; + + dbUsers.update({_id: user._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +} + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + +module.exports = processUsers; diff --git a/migrations/20170425_missing_incentives.js b/migrations/archive/2017/20170425_missing_incentives.js similarity index 70% rename from migrations/20170425_missing_incentives.js rename to migrations/archive/2017/20170425_missing_incentives.js index be8c224719..9c431b5661 100644 --- a/migrations/20170425_missing_incentives.js +++ b/migrations/archive/2017/20170425_missing_incentives.js @@ -1,6 +1,6 @@ -var migrationName = '20170425_missing_incentives'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done +let migrationName = '20170425_missing_incentives'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done /* * Award missing Royal Purple Hatching Potion to users with 55+ check-ins @@ -10,36 +10,36 @@ var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done import monk from 'monk'; import common from '../website/common'; -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbUsers = monk(connectionString).get('users', { castIds: false }); +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbUsers = monk(connectionString).get('users', { castIds: false }); -function processUsers(lastId) { +function processUsers (lastId) { // specify a query to limit the affected users (empty for all users): - var query = { - 'loginIncentives': {$gt:99}, - 'migration': {$ne: migrationName}, + let query = { + loginIncentives: {$gt: 99}, + migration: {$ne: migrationName}, }; if (lastId) { query._id = { - $gt: lastId - } + $gt: lastId, + }; } dbUsers.find(query, { sort: {_id: 1}, limit: 250, - fields: [] // specify fields we are interested in to limit retrieved data (empty if we're not reading data): + fields: [], // specify fields we are interested in to limit retrieved data (empty if we're not reading data): }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); + .then(updateUsers) + .catch(function (err) { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); } -var progressCount = 1000; -var count = 0; +let progressCount = 1000; +let count = 0; function updateUsers (users) { if (!users || users.length === 0) { @@ -48,20 +48,20 @@ function updateUsers (users) { return; } - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); + .then(function () { + processUsers(lastUser._id); + }); } function updateUser (user) { count++; - var language = user.preferences.language || 'en'; - var set = {'migration': migrationName}; - var inc = { + let language = user.preferences.language || 'en'; + let set = {migration: migrationName}; + let inc = { 'items.eggs.BearCub': 0, 'items.eggs.Cactus': 0, 'items.eggs.Dragon': 0, @@ -93,7 +93,7 @@ function updateUser (user) { 'items.hatchingPotions.White': 0, 'items.hatchingPotions.Zombie': 0, }; - var nextReward; + let nextReward; if (user.loginIncentives >= 105) { inc['items.hatchingPotions.RoyalPurple'] += 1; @@ -167,39 +167,44 @@ function updateUser (user) { nextReward = 160; } - var push = { - 'notifications': { - 'type': 'LOGIN_INCENTIVE', - 'data': { - 'nextRewardAt': nextReward, - 'rewardKey': [ + let push = { + notifications: { + type: 'LOGIN_INCENTIVE', + data: { + nextRewardAt: nextReward, + rewardKey: [ 'shop_armoire', ], - 'rewardText': common.i18n.t('checkInRewards', language), - 'reward': [], - 'message': common.i18n.t('backloggedCheckInRewards', language), + rewardText: common.i18n.t('checkInRewards', language), + reward: [], + message: common.i18n.t('backloggedCheckInRewards', language), }, - 'id': common.uuid(), - } + id: common.uuid(), + }, }; - dbUsers.update({_id: user._id}, {$set:set, $push:push, $inc:inc}); + dbUsers.update({_id: user._id}, {$set: set, $push: push, $inc: inc}); - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); } -function displayData() { - console.warn('\n' + count + ' users processed\n'); +function displayData () { + console.warn(`\n${ count } users processed\n`); return exiting(0); } -function exiting(code, msg) { +function exiting (code, msg) { code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } + if (code && !msg) { + msg = 'ERROR!'; + } if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } + if (code) { + console.error(msg); + } else { + console.log(msg); + } } process.exit(code); } diff --git a/migrations/20170616_achievements.js b/migrations/archive/2017/20170616_achievements.js similarity index 56% rename from migrations/20170616_achievements.js rename to migrations/archive/2017/20170616_achievements.js index a780c33f3d..53b8be3b73 100644 --- a/migrations/20170616_achievements.js +++ b/migrations/archive/2017/20170616_achievements.js @@ -1,6 +1,6 @@ -var migrationName = '20170616_achievements'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done +let migrationName = '20170616_achievements'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done /* * Updates to achievements for June 16, 2017 biweekly merge @@ -10,27 +10,27 @@ var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done import monk from 'monk'; -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbUsers = monk(connectionString).get('users', { castIds: false }); +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbUsers = monk(connectionString).get('users', { castIds: false }); -function processUsers(lastId) { +function processUsers (lastId) { // specify a query to limit the affected users (empty for all users): - var query = { + let query = { $or: [ - {'achievements.quests.dilatoryDistress1': {$gt:0}}, - {'achievements.quests.egg': {$gt:0}}, - {'achievements.quests.goldenknight1': {$gt:0}}, - {'achievements.quests.moonstone1': {$gt:0}}, - {'achievements.quests.vice2': {$gt:0}}, + {'achievements.quests.dilatoryDistress1': {$gt: 0}}, + {'achievements.quests.egg': {$gt: 0}}, + {'achievements.quests.goldenknight1': {$gt: 0}}, + {'achievements.quests.moonstone1': {$gt: 0}}, + {'achievements.quests.vice2': {$gt: 0}}, {'achievements.challenges': {$exists: true, $ne: []}}, - {'challenges': {$exists: true, $ne: []}}, + {challenges: {$exists: true, $ne: []}}, ], }; if (lastId) { query._id = { - $gt: lastId - } + $gt: lastId, + }; } dbUsers.find(query, { @@ -41,15 +41,15 @@ function processUsers(lastId) { 'challenges', ], }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); + .then(updateUsers) + .catch(function (err) { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); } -var progressCount = 1000; -var count = 0; +let progressCount = 1000; +let count = 0; function updateUsers (users) { if (!users || users.length === 0) { @@ -58,18 +58,18 @@ function updateUsers (users) { return; } - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); + .then(function () { + processUsers(lastUser._id); + }); } function updateUser (user) { count++; - var set = {'migration': migrationName}; + let set = {migration: migrationName}; if (user.challenges.length > 0 || user.achievements.challenges.length > 0) { set['achievements.joinedChallenge'] = true; @@ -90,23 +90,28 @@ function updateUser (user) { set['achievements.quests.vice2'] = Math.ceil(user.achievements.quests.vice2 * 1.5); } - dbUsers.update({_id: user._id}, {$set:set}); + dbUsers.update({_id: user._id}, {$set: set}); - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); } -function displayData() { - console.warn('\n' + count + ' users processed\n'); +function displayData () { + console.warn(`\n${ count } users processed\n`); return exiting(0); } -function exiting(code, msg) { +function exiting (code, msg) { code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } + if (code && !msg) { + msg = 'ERROR!'; + } if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } + if (code) { + console.error(msg); + } else { + console.log(msg); + } } process.exit(code); } diff --git a/migrations/archive/2017/20170711_orcas.js b/migrations/archive/2017/20170711_orcas.js new file mode 100644 index 0000000000..8c9793fe43 --- /dev/null +++ b/migrations/archive/2017/20170711_orcas.js @@ -0,0 +1,95 @@ +let migrationName = '20170711_orcas.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award Orca pets to owners of Orca mount, and Orca mount to everyone else + */ + +let monk = require('monk'); +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbUsers = monk(connectionString).get('users', { castIds: false }); + +function processUsers (lastId) { + // specify a query to limit the affected users (empty for all users): + let query = { + migration: {$ne: migrationName}, + }; + + if (lastId) { + query._id = { + $gt: lastId, + }; + } + + dbUsers.find(query, { + sort: {_id: 1}, + limit: 250, + fields: [ + 'items.mounts', + ], // specify fields we are interested in to limit retrieved data (empty if we're not reading data): + }) + .then(updateUsers) + .catch(function (err) { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); +} + +let progressCount = 1000; +let count = 0; + +function updateUsers (users) { + if (!users || users.length === 0) { + console.warn('All appropriate users found and modified.'); + displayData(); + return; + } + + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; + + return Promise.all(userPromises) + .then(function () { + processUsers(lastUser._id); + }); +} + +function updateUser (user) { + count++; + + let set = {}; + + if (user.items.mounts['Orca-Base']) { + set = {migration: migrationName, 'items.pets.Orca-Base': 5}; + } else { + set = {migration: migrationName, 'items.mounts.Orca-Base': true}; + } + + dbUsers.update({_id: user._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +} + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + +module.exports = processUsers; diff --git a/migrations/archive/2017/20170731_naming_day.js b/migrations/archive/2017/20170731_naming_day.js new file mode 100644 index 0000000000..0aac178dba --- /dev/null +++ b/migrations/archive/2017/20170731_naming_day.js @@ -0,0 +1,114 @@ +let migrationName = '20170731_naming_day.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award Royal Purple Gryphon Helm to Royal Purple Gryphon pet owners, + * award Royal Purple Gryphon pet to Royal Purple Gryphon mount owners, + * award Royal Purple Gryphon mount to everyone else + */ + +let monk = require('monk'); +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbUsers = monk(connectionString).get('users', { castIds: false }); + +function processUsers (lastId) { + // specify a query to limit the affected users (empty for all users): + let query = { + migration: {$ne: migrationName}, + 'auth.timestamps.loggedin': {$gt: new Date('2017-01-01')}, + }; + + if (lastId) { + query._id = { + $gt: lastId, + }; + } + + dbUsers.find(query, { + sort: {_id: 1}, + limit: 250, + fields: [ + 'items.mounts', + 'items.pets', + ], // specify fields we are interested in to limit retrieved data (empty if we're not reading data): + }) + .then(updateUsers) + .catch(function (err) { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); +} + +let progressCount = 1000; +let count = 0; + +function updateUsers (users) { + if (!users || users.length === 0) { + console.warn('All appropriate users found and modified.'); + displayData(); + return; + } + + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; + + return Promise.all(userPromises) + .then(function () { + processUsers(lastUser._id); + }); +} + +function updateUser (user) { + count++; + + let set = {}; + let inc = { + 'achievements.habiticaDays': 1, + 'items.food.Cake_Skeleton': 1, + 'items.food.Cake_Base': 1, + 'items.food.Cake_CottonCandyBlue': 1, + 'items.food.Cake_CottonCandyPink': 1, + 'items.food.Cake_Shade': 1, + 'items.food.Cake_White': 1, + 'items.food.Cake_Golden': 1, + 'items.food.Cake_Zombie': 1, + 'items.food.Cake_Desert': 1, + 'items.food.Cake_Red': 1, + }; + + if (user.items.pets['Gryphon-RoyalPurple']) { + set = {migration: migrationName, 'items.gear.owned.head_special_namingDay2017': false}; + } else if (user.items.mounts['Gryphon-RoyalPurple']) { + set = {migration: migrationName, 'items.pets.Gryphon-RoyalPurple': 5}; + } else { + set = {migration: migrationName, 'items.mounts.Gryphon-RoyalPurple': true}; + } + + dbUsers.update({_id: user._id}, {$set: set, $inc: inc}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +} + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + +module.exports = processUsers; diff --git a/migrations/archive/2017/20170928_redesign_guilds.js b/migrations/archive/2017/20170928_redesign_guilds.js new file mode 100644 index 0000000000..89bd8f5261 --- /dev/null +++ b/migrations/archive/2017/20170928_redesign_guilds.js @@ -0,0 +1,102 @@ +let migrationName = '20170928_redesign_guilds.js'; + +/* + * Copy Guild Leader messages to end of Guild descriptions + * Copy Guild logos to beginning of Guild descriptions + */ + +let monk = require('monk'); +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbGroups = monk(connectionString).get('groups', { castIds: false }); + +function processGroups (lastId) { + // specify a query to limit the affected groups (empty for all groups): + let query = { + }; + + let fields = { + description: 1, + logo: 1, + leaderMessage: 1, + }; + + if (lastId) { + query._id = { + $gt: lastId, + }; + } + + return dbGroups.find(query, { + fields, + sort: {_id: 1}, + limit: 250, + }) + .then(updateGroups) + .catch(function (err) { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); +} + +let progressCount = 1000; +let count = 0; + +function updateGroups (groups) { + if (!groups || groups.length === 0) { + console.warn('All appropriate groups found and modified.'); + displayData(); + return; + } + + let groupPromises = groups.map(updateGroup); + let lastGroup = groups[groups.length - 1]; + + return Promise.all(groupPromises) + .then(function () { + processGroups(lastGroup._id); + }); +} + +function updateGroup (group) { + count++; + + let description = group.description; + + if (group.logo) { + description = `![Guild Logo](${ group.logo })\n\n \n\n${ description}`; + } + + if (group.leaderMessage) { + description = `${description }\n\n \n\n${ group.leaderMessage}`; + } + + let set = { + description, + }; + + if (count % progressCount === 0) console.warn(`${count } ${ group._id}`); + + return dbGroups.update({_id: group._id}, {$set: set}); +} + +function displayData () { + console.warn(`\n${ count } groups processed\n`); + return exiting(0); +} + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + +module.exports = processGroups; diff --git a/migrations/20170928_redesign_launch.js b/migrations/archive/2017/20170928_redesign_launch.js similarity index 50% rename from migrations/20170928_redesign_launch.js rename to migrations/archive/2017/20170928_redesign_launch.js index 1139652050..e83d838413 100644 --- a/migrations/20170928_redesign_launch.js +++ b/migrations/archive/2017/20170928_redesign_launch.js @@ -1,53 +1,53 @@ import { selectGearToPin } from '../website/common/script/ops/pinnedGearUtils'; -var getItemInfo = require('../website/common/script/libs/getItemInfo'); +let getItemInfo = require('../website/common/script/libs/getItemInfo'); -var migrationName = '20170928_redesign_launch.js'; -var authorName = 'paglias'; // in case script author needs to know when their ... -var authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; //... own data is done +let migrationName = '20170928_redesign_launch.js'; +let authorName = 'paglias'; // in case script author needs to know when their ... +let authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; // ... own data is done /* * Migrate existing in app rewards lists to pinned items * Award Veteran Pets */ -var monk = require('monk'); -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbUsers = monk(connectionString).get('users', { castIds: false }); +let monk = require('monk'); +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbUsers = monk(connectionString).get('users', { castIds: false }); -function processUsers(lastId) { +function processUsers (lastId) { // specify a query to limit the affected users (empty for all users): - var query = { - 'migration': {$ne:migrationName}, + let query = { + migration: {$ne: migrationName}, 'auth.timestamps.loggedin': {$gt: new Date('2017-09-21')}, }; - var fields = { + let fields = { 'items.pets': 1, 'items.gear': 1, 'stats.class': 1, - } + }; if (lastId) { query._id = { - $gt: lastId - } + $gt: lastId, + }; } return dbUsers.find(query, { - fields: fields, + fields, sort: {_id: 1}, limit: 250, }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); + .then(updateUsers) + .catch(function (err) { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); } -var progressCount = 1000; -var count = 0; +let progressCount = 1000; +let count = 0; function updateUsers (users) { if (!users || users.length === 0) { @@ -56,22 +56,22 @@ function updateUsers (users) { return; } - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); + .then(function () { + processUsers(lastUser._id); + }); } function updateUser (user) { count++; - var set = {'migration': migrationName}; + let set = {migration: migrationName}; - var oldRewardsList = selectGearToPin(user); - var newPinnedItems = [ + let oldRewardsList = selectGearToPin(user); + let newPinnedItems = [ { type: 'armoire', path: 'armoire', @@ -83,13 +83,13 @@ function updateUser (user) { ]; oldRewardsList.forEach(item => { - var type = 'marketGear'; + let type = 'marketGear'; - var itemInfo = getItemInfo(user, 'marketGear', item); + let itemInfo = getItemInfo(user, 'marketGear', item); newPinnedItems.push({ type, path: itemInfo.path, - }) + }); }); set.pinnedItems = newPinnedItems; @@ -104,23 +104,28 @@ function updateUser (user) { set['items.pets.Wolf-Veteran'] = 5; } - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); - return dbUsers.update({_id: user._id}, {$set:set}); + return dbUsers.update({_id: user._id}, {$set: set}); } -function displayData() { - console.warn('\n' + count + ' users processed\n'); +function displayData () { + console.warn(`\n${ count } users processed\n`); return exiting(0); } -function exiting(code, msg) { +function exiting (code, msg) { code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } + if (code && !msg) { + msg = 'ERROR!'; + } if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } + if (code) { + console.error(msg); + } else { + console.log(msg); + } } process.exit(code); } diff --git a/migrations/archive/2017/20171030_jackolanterns.js b/migrations/archive/2017/20171030_jackolanterns.js new file mode 100644 index 0000000000..1b07ed1fb2 --- /dev/null +++ b/migrations/archive/2017/20171030_jackolanterns.js @@ -0,0 +1,116 @@ +let migrationName = '20171030_jackolanterns.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award the Jack-O'-Lantern ladder: + * Ghost Jack-O-Lantern Mount to owners of Ghost Jack-O-Lantern Pet + * Ghost Jack-O-Lantern Pet to owners of Jack-O-Lantern Mount + * Jack-O-Lantern Mount to owners of Jack-O-Lantern Pet + * Jack-O-Lantern Pet to everyone else + */ + +let monk = require('monk'); +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbUsers = monk(connectionString).get('users', { castIds: false }); + +function processUsers (lastId) { + // specify a query to limit the affected users (empty for all users): + let query = { + migration: {$ne: migrationName}, + }; + + if (lastId) { + query._id = { + $gt: lastId, + }; + } + + dbUsers.find(query, { + sort: {_id: 1}, + limit: 250, + fields: [ + 'items.pets', + 'items.mounts', + ], // specify fields we are interested in to limit retrieved data (empty if we're not reading data): + }) + .then(updateUsers) + .catch(function (err) { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); +} + +let progressCount = 1000; +let count = 0; + +function updateUsers (users) { + if (!users || users.length === 0) { + console.warn('All appropriate users found and modified.'); + displayData(); + return; + } + + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; + + return Promise.all(userPromises) + .then(function () { + processUsers(lastUser._id); + }); +} + +function updateUser (user) { + count++; + + let set = {}; + let inc = { + 'items.food.Candy_Skeleton': 1, + 'items.food.Candy_Base': 1, + 'items.food.Candy_CottonCandyBlue': 1, + 'items.food.Candy_CottonCandyPink': 1, + 'items.food.Candy_Shade': 1, + 'items.food.Candy_White': 1, + 'items.food.Candy_Golden': 1, + 'items.food.Candy_Zombie': 1, + 'items.food.Candy_Desert': 1, + 'items.food.Candy_Red': 1, + }; + + if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Ghost']) { + set = {migration: migrationName, 'items.mounts.JackOLantern-Ghost': true}; + } else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Base']) { + set = {migration: migrationName, 'items.pets.JackOLantern-Ghost': 5}; + } else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Base']) { + set = {migration: migrationName, 'items.mounts.JackOLantern-Base': true}; + } else { + set = {migration: migrationName, 'items.pets.JackOLantern-Base': 5}; + } + + dbUsers.update({_id: user._id}, {$set: set, $inc: inc}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +} + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + +module.exports = processUsers; diff --git a/migrations/20171117_turkey_ladder.js b/migrations/archive/2017/20171117_turkey_ladder.js similarity index 57% rename from migrations/20171117_turkey_ladder.js rename to migrations/archive/2017/20171117_turkey_ladder.js index 814109af5f..f4414f58ac 100644 --- a/migrations/20171117_turkey_ladder.js +++ b/migrations/archive/2017/20171117_turkey_ladder.js @@ -1,6 +1,6 @@ -var migrationName = '20171117_turkey_ladder.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done +let migrationName = '20171117_turkey_ladder.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done /* * Award the Turkey Day ladder: @@ -11,21 +11,21 @@ var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done * Grant Base Turkey pet to those who have none of the above yet */ -var monk = require('monk'); -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbUsers = monk(connectionString).get('users', { castIds: false }); +let monk = require('monk'); +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbUsers = monk(connectionString).get('users', { castIds: false }); -function processUsers(lastId) { +function processUsers (lastId) { // specify a query to limit the affected users (empty for all users): - var query = { - 'migration':{$ne:migrationName}, - 'auth.timestamps.loggedin':{$gt:new Date('2017-11-01')}, + let query = { + migration: {$ne: migrationName}, + 'auth.timestamps.loggedin': {$gt: new Date('2017-11-01')}, }; if (lastId) { query._id = { - $gt: lastId - } + $gt: lastId, + }; } dbUsers.find(query, { @@ -34,17 +34,17 @@ function processUsers(lastId) { fields: [ 'items.pets', 'items.mounts', - ] // specify fields we are interested in to limit retrieved data (empty if we're not reading data): + ], // specify fields we are interested in to limit retrieved data (empty if we're not reading data): }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); + .then(updateUsers) + .catch(function (err) { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); } -var progressCount = 1000; -var count = 0; +let progressCount = 1000; +let count = 0; function updateUsers (users) { if (!users || users.length === 0) { @@ -53,19 +53,19 @@ function updateUsers (users) { return; } - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); + .then(function () { + processUsers(lastUser._id); + }); } function updateUser (user) { count++; - var set = {}; + let set = {}; if (user && user.items && user.items.mounts && user.items.mounts['Turkey-Gilded']) { set = { @@ -92,13 +92,13 @@ function updateUser (user) { }, ]; } else if (user && user.items && user.items.pets && user.items.pets['Turkey-Gilded']) { - set = {'migration':migrationName, 'items.mounts.Turkey-Gilded':true}; + set = {migration: migrationName, 'items.mounts.Turkey-Gilded': true}; } else if (user && user.items && user.items.mounts && user.items.mounts['Turkey-Base']) { - set = {'migration':migrationName, 'items.pets.Turkey-Gilded':5}; + set = {migration: migrationName, 'items.pets.Turkey-Gilded': 5}; } else if (user && user.items && user.items.pets && user.items.pets['Turkey-Base']) { - set = {'migration':migrationName, 'items.mounts.Turkey-Base':true}; + set = {migration: migrationName, 'items.mounts.Turkey-Base': true}; } else { - set = {'migration':migrationName, 'items.pets.Turkey-Base':5}; + set = {migration: migrationName, 'items.pets.Turkey-Base': 5}; } dbUsers.update({_id: user._id}, {$set: set}); @@ -106,21 +106,26 @@ function updateUser (user) { dbUsers.update({_id: user._id}, {$push: {pinnedItems: {$each: push}}}); } - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); } -function displayData() { - console.warn('\n' + count + ' users processed\n'); +function displayData () { + console.warn(`\n${ count } users processed\n`); return exiting(0); } -function exiting(code, msg) { +function exiting (code, msg) { code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } + if (code && !msg) { + msg = 'ERROR!'; + } if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } + if (code) { + console.error(msg); + } else { + console.log(msg); + } } process.exit(code); } diff --git a/migrations/archive/2017/20171230_nye_hats.js b/migrations/archive/2017/20171230_nye_hats.js new file mode 100644 index 0000000000..12c4287dc5 --- /dev/null +++ b/migrations/archive/2017/20171230_nye_hats.js @@ -0,0 +1,108 @@ +let migrationName = '20171230_nye_hats.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done + +/* + * Award New Year's Eve party hats to users in sequence + */ + +let monk = require('monk'); +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbUsers = monk(connectionString).get('users', { castIds: false }); + +function processUsers (lastId) { + // specify a query to limit the affected users (empty for all users): + let query = { + migration: {$ne: migrationName}, + 'auth.timestamps.loggedin': {$gt: new Date('2017-11-30')}, + }; + + if (lastId) { + query._id = { + $gt: lastId, + }; + } + + dbUsers.find(query, { + sort: {_id: 1}, + limit: 250, + fields: [ + 'items.gear.owned', + ], // specify fields we are interested in to limit retrieved data (empty if we're not reading data): + }) + .then(updateUsers) + .catch(function (err) { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); +} + +let progressCount = 1000; +let count = 0; + +function updateUsers (users) { + if (!users || users.length === 0) { + console.warn('All appropriate users found and modified.'); + displayData(); + return; + } + + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; + + return Promise.all(userPromises) + .then(function () { + processUsers(lastUser._id); + }); +} + +function updateUser (user) { + count++; + + let set = {}; + let push = {}; + + if (typeof user.items.gear.owned.head_special_nye2016 !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.head_special_nye2017': false}; + push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_nye2017', _id: monk.id()}}; + } else if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.head_special_nye2016': false}; + push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_nye2016', _id: monk.id()}}; + } else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.head_special_nye2015': false}; + push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_nye2015', _id: monk.id()}}; + } else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') { + set = {migration: migrationName, 'items.gear.owned.head_special_nye2014': false}; + push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_nye2014', _id: monk.id()}}; + } else { + set = {migration: migrationName, 'items.gear.owned.head_special_nye': false}; + push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_nye', _id: monk.id()}}; + } + + dbUsers.update({_id: user._id}, {$set: set, $push: push}); + + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); +} + +function displayData () { + console.warn(`\n${ count } users processed\n`); + return exiting(0); +} + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + +module.exports = processUsers; diff --git a/migrations/20180110_nextPaymentProcessing.js b/migrations/archive/2018/20180110_nextPaymentProcessing.js similarity index 50% rename from migrations/20180110_nextPaymentProcessing.js rename to migrations/archive/2018/20180110_nextPaymentProcessing.js index 293dcb354c..c56cca690f 100644 --- a/migrations/20180110_nextPaymentProcessing.js +++ b/migrations/archive/2018/20180110_nextPaymentProcessing.js @@ -2,36 +2,36 @@ * Convert purchased.plan.nextPaymentProcessing from a double to a date field for Apple subscribers */ -var monk = require('monk'); -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbUsers = monk(connectionString).get('users', { castIds: false }); +let monk = require('monk'); +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbUsers = monk(connectionString).get('users', { castIds: false }); -function processUsers(lastId) { +function processUsers (lastId) { // specify a query to limit the affected users (empty for all users): - var query = { - 'purchased.plan.paymentMethod': "Apple", + let query = { + 'purchased.plan.paymentMethod': 'Apple', 'purchased.plan.nextPaymentProcessing': {$type: 'double'}, }; if (lastId) { query._id = { - $gt: lastId - } + $gt: lastId, + }; } dbUsers.find(query, { sort: {_id: 1}, limit: 250, }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); + .then(updateUsers) + .catch(function (err) { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); } -var progressCount = 100; -var count = 0; +let progressCount = 100; +let count = 0; function updateUsers (users) { if (!users || users.length === 0) { @@ -40,38 +40,43 @@ function updateUsers (users) { return; } - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); + .then(function () { + processUsers(lastUser._id); + }); } function updateUser (user) { count++; - var set = { + let set = { 'purchased.plan.nextPaymentProcessing': new Date(user.purchased.plan.nextPaymentProcessing), }; dbUsers.update({_id: user._id}, {$set: set}); - if (count % progressCount == 0) console.warn(count + ' ' + user._id); + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); } -function displayData() { - console.warn('\n' + count + ' users processed\n'); +function displayData () { + console.warn(`\n${ count } users processed\n`); return exiting(0); } -function exiting(code, msg) { +function exiting (code, msg) { code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } + if (code && !msg) { + msg = 'ERROR!'; + } if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } + if (code) { + console.error(msg); + } else { + console.log(msg); + } } process.exit(code); } diff --git a/migrations/20180125_clean_new_notifications.js b/migrations/archive/2018/20180125_clean_new_notifications.js similarity index 100% rename from migrations/20180125_clean_new_notifications.js rename to migrations/archive/2018/20180125_clean_new_notifications.js diff --git a/migrations/20180125_notifications.js b/migrations/archive/2018/20180125_notifications.js similarity index 100% rename from migrations/20180125_notifications.js rename to migrations/archive/2018/20180125_notifications.js diff --git a/migrations/20180130_habit_birthday.js b/migrations/archive/2018/20180130_habit_birthday.js similarity index 51% rename from migrations/20180130_habit_birthday.js rename to migrations/archive/2018/20180130_habit_birthday.js index 05a3dc1c02..f28a42eaa8 100644 --- a/migrations/20180130_habit_birthday.js +++ b/migrations/archive/2018/20180130_habit_birthday.js @@ -1,44 +1,44 @@ -var migrationName = '20180130_habit_birthday.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done +let migrationName = '20180130_habit_birthday.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done /* * Award party robes: most recent user doesn't have of 2014-2018. Also cake! */ -var monk = require('monk'); -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbUsers = monk(connectionString).get('users', { castIds: false }); +let monk = require('monk'); +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbUsers = monk(connectionString).get('users', { castIds: false }); -function processUsers(lastId) { +function processUsers (lastId) { // specify a query to limit the affected users (empty for all users): - var query = { - 'migration':{$ne:migrationName}, - 'auth.timestamps.loggedin':{$gt:new Date('2018-01-01')}, // remove after first run to cover remaining users + let query = { + migration: {$ne: migrationName}, + 'auth.timestamps.loggedin': {$gt: new Date('2018-01-01')}, // remove after first run to cover remaining users }; if (lastId) { query._id = { - $gt: lastId - } + $gt: lastId, + }; } dbUsers.find(query, { sort: {_id: 1}, limit: 250, fields: [ // specify fields we are interested in to limit retrieved data (empty if we're not reading data) - 'items.gear.owned' + 'items.gear.owned', ], }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); + .then(updateUsers) + .catch(function (err) { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); } -var progressCount = 1000; -var count = 0; +let progressCount = 1000; +let count = 0; function updateUsers (users) { if (!users || users.length === 0) { @@ -47,69 +47,74 @@ function updateUsers (users) { return; } - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); + .then(function () { + processUsers(lastUser._id); + }); } function updateUser (user) { count++; - var push; - var set = {'migration':migrationName}; + let push; + let set = {migration: migrationName}; if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2017')) { set['items.gear.owned.armor_special_birthday2018'] = false; - push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2018', '_id': monk.id()}}; + push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2018', _id: monk.id()}}; } else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2016')) { set['items.gear.owned.armor_special_birthday2017'] = false; - push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2017', '_id': monk.id()}}; + push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2017', _id: monk.id()}}; } else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2015')) { set['items.gear.owned.armor_special_birthday2016'] = false; - push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2016', '_id': monk.id()}}; + push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2016', _id: monk.id()}}; } else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday')) { set['items.gear.owned.armor_special_birthday2015'] = false; - push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2015', '_id': monk.id()}}; + push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2015', _id: monk.id()}}; } else { set['items.gear.owned.armor_special_birthday'] = false; - push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday', '_id': monk.id()}}; + push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday', _id: monk.id()}}; } - var inc = { - 'items.food.Cake_Skeleton':1, - 'items.food.Cake_Base':1, - 'items.food.Cake_CottonCandyBlue':1, - 'items.food.Cake_CottonCandyPink':1, - 'items.food.Cake_Shade':1, - 'items.food.Cake_White':1, - 'items.food.Cake_Golden':1, - 'items.food.Cake_Zombie':1, - 'items.food.Cake_Desert':1, - 'items.food.Cake_Red':1, - 'achievements.habitBirthdays':1 + let inc = { + 'items.food.Cake_Skeleton': 1, + 'items.food.Cake_Base': 1, + 'items.food.Cake_CottonCandyBlue': 1, + 'items.food.Cake_CottonCandyPink': 1, + 'items.food.Cake_Shade': 1, + 'items.food.Cake_White': 1, + 'items.food.Cake_Golden': 1, + 'items.food.Cake_Zombie': 1, + 'items.food.Cake_Desert': 1, + 'items.food.Cake_Red': 1, + 'achievements.habitBirthdays': 1, }; dbUsers.update({_id: user._id}, {$set: set, $inc: inc, $push: push}); - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); } -function displayData() { - console.warn('\n' + count + ' users processed\n'); +function displayData () { + console.warn(`\n${ count } users processed\n`); return exiting(0); } -function exiting(code, msg) { +function exiting (code, msg) { code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } + if (code && !msg) { + msg = 'ERROR!'; + } if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } + if (code) { + console.error(msg); + } else { + console.log(msg); + } } process.exit(code); } diff --git a/migrations/archive/README.md b/migrations/archive/README.md new file mode 100644 index 0000000000..9330a6dbf8 --- /dev/null +++ b/migrations/archive/README.md @@ -0,0 +1,4 @@ +If you need to use a migration from this folder, move it to /migrations. + +Note that /migrations files (excluding /archive) are linted, so to pass test you'll have to make sure +that the file is written correctly. \ No newline at end of file diff --git a/migrations/archive/api_v3/challenges.js b/migrations/archive/api_v3/challenges.js new file mode 100644 index 0000000000..80315db9f1 --- /dev/null +++ b/migrations/archive/api_v3/challenges.js @@ -0,0 +1,218 @@ +// Migrate challenges collection to new schema (except for members) + +// The console-stamp module must be installed (not included in package.json) + +// It requires two environment variables: MONGODB_OLD and MONGODB_NEW + +// Due to some big user profiles it needs more RAM than is allowed by default by v8 (arounf 1.7GB). +// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM + +// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. +// We've now upgraded to lodash v4 but the code used in this migration has not been +// adapted to work with it. Before this migration is used again any lodash method should +// be checked for compatibility against the v4 changelog and changed if necessary. +// https://github.com/lodash/lodash/wiki/Changelog#v400 +console.log('Starting migrations/api_v3/challenges.js.'); + +require('babel-register'); +require('babel-polyfill'); + +let Bluebird = require('bluebird'); +let MongoDB = require('mongodb'); +let nconf = require('nconf'); +let mongoose = require('mongoose'); +let _ = require('lodash'); +let uuid = require('uuid'); +let consoleStamp = require('console-stamp'); +let fs = require('fs'); + +// Add timestamps to console messages +consoleStamp(console); + +// Initialize configuration +require('../../website/server/libs/api-v3/setupNconf')(); + +let MONGODB_OLD = nconf.get('MONGODB_OLD'); +let MONGODB_NEW = nconf.get('MONGODB_NEW'); + +let MongoClient = MongoDB.MongoClient; + +mongoose.Promise = Bluebird; // otherwise mongoose models won't work + +// Load new models +let NewChallenge = require('../../website/server/models/challenge').model; +let Tasks = require('../../website/server/models/task'); + +// To be defined later when MongoClient connects +let mongoDbOldInstance; +let oldChallengeCollection; + +let mongoDbNewInstance; +let newChallengeCollection; +let newTaskCollection; + +let BATCH_SIZE = 1000; + +let processedChallenges = 0; +let totoalProcessedTasks = 0; + +let newTasksIds = {}; // a map of old id -> [new id, challengeId] + +// Only process challenges that fall in a interval ie -> up to 0000-4000-0000-0000 +let AFTER_CHALLENGE_ID = nconf.get('AFTER_CHALLENGE_ID'); +let BEFORE_CHALLENGE_ID = nconf.get('BEFORE_CHALLENGE_ID'); + +function processChallenges (afterId) { + let processedTasks = 0; + let lastChallenge = null; + let oldChallenges; + + let query = {}; + + if (BEFORE_CHALLENGE_ID) { + query._id = {$lte: BEFORE_CHALLENGE_ID}; + } + + if ((afterId || AFTER_CHALLENGE_ID) && !query._id) { + query._id = {}; + } + + if (afterId) { + query._id.$gt = afterId; + } else if (AFTER_CHALLENGE_ID) { + query._id.$gt = AFTER_CHALLENGE_ID; + } + + let batchInsertTasks = newTaskCollection.initializeUnorderedBulkOp(); + let batchInsertChallenges = newChallengeCollection.initializeUnorderedBulkOp(); + + console.log(`Executing challenges query.\nMatching challenges after ${afterId ? afterId : AFTER_CHALLENGE_ID} and before ${BEFORE_CHALLENGE_ID} (included).`); + + return oldChallengeCollection + .find(query) + .sort({_id: 1}) + .limit(BATCH_SIZE) + .toArray() + .then(function (oldChallengesR) { + oldChallenges = oldChallengesR; + + console.log(`Processing ${oldChallenges.length} challenges. Already processed ${processedChallenges} challenges and ${totoalProcessedTasks} tasks.`); + + if (oldChallenges.length === BATCH_SIZE) { + lastChallenge = oldChallenges[oldChallenges.length - 1]._id; + } + + oldChallenges.forEach(function (oldChallenge) { + let oldTasks = oldChallenge.habits.concat(oldChallenge.dailys).concat(oldChallenge.rewards).concat(oldChallenge.todos); + delete oldChallenge.habits; + delete oldChallenge.dailys; + delete oldChallenge.rewards; + delete oldChallenge.todos; + + let createdAt = oldChallenge.timestamp; + + oldChallenge.memberCount = oldChallenge.members.length; + if (oldChallenge.prize <= 0) oldChallenge.prize = 0; + if (!oldChallenge.name) oldChallenge.name = 'challenge name'; + if (!oldChallenge.shortName) oldChallenge.name = 'challenge-name'; + + if (!oldChallenge.group) throw new Error('challenge.group is required'); + if (!oldChallenge.leader) throw new Error('challenge.leader is required'); + + + if (oldChallenge.leader === '9') { + oldChallenge.leader = '00000000-0000-4000-9000-000000000000'; + } + + if (oldChallenge.group === 'habitrpg') { + oldChallenge.group = '00000000-0000-4000-A000-000000000000'; + } + + delete oldChallenge.id; + + let newChallenge = new NewChallenge(oldChallenge); + + newChallenge.createdAt = createdAt; + + oldTasks.forEach(function (oldTask) { + oldTask._id = uuid.v4(); + oldTask._legacyId = oldTask.id; // store the old task id + delete oldTask.id; + + oldTask.challenge = oldTask.challenge || {}; + oldTask.challenge.id = newChallenge._id; + + if (newTasksIds[`${oldTask._legacyId }-${ newChallenge._id}`]) { + throw new Error('duplicate :('); + } else { + newTasksIds[`${oldTask._legacyId }-${ newChallenge._id}`] = oldTask._id; + } + + oldTask.tags = _.map(oldTask.tags || {}, function (tagPresent, tagId) { + return tagPresent && tagId; + }).filter(function (tag) { + return tag !== false; + }); + + if (!oldTask.text) oldTask.text = 'task text'; // required + + oldTask.createdAt = oldTask.dateCreated; + + newChallenge.tasksOrder[`${oldTask.type}s`].push(oldTask._id); + if (oldTask.completed) oldTask.completed = false; + + let newTask = new Tasks[oldTask.type](oldTask); + + batchInsertTasks.insert(newTask.toObject()); + processedTasks++; + }); + + batchInsertChallenges.insert(newChallenge.toObject()); + }); + + console.log(`Saving ${oldChallenges.length} challenges and ${processedTasks} tasks.`); + + return Bluebird.all([ + batchInsertChallenges.execute(), + batchInsertTasks.execute(), + ]); + }) + .then(function () { + totoalProcessedTasks += processedTasks; + processedChallenges += oldChallenges.length; + + console.log(`Saved ${oldChallenges.length} challenges and their tasks.`); + + if (lastChallenge) { + return processChallenges(lastChallenge); + } else { + console.log('Writing newTasksIds.json...'); + fs.writeFileSync('newTasksIds.json', JSON.stringify(newTasksIds, null, 4), 'utf8'); + return console.log('Done!'); + } + }); +} + +// Connect to the databases +Bluebird.all([ + MongoClient.connect(MONGODB_OLD), + MongoClient.connect(MONGODB_NEW), +]) + .then(function (result) { + let oldInstance = result[0]; + let newInstance = result[1]; + + mongoDbOldInstance = oldInstance; + oldChallengeCollection = mongoDbOldInstance.collection('challenges'); + + mongoDbNewInstance = newInstance; + newChallengeCollection = mongoDbNewInstance.collection('challenges'); + newTaskCollection = mongoDbNewInstance.collection('tasks'); + + console.log(`Connected with MongoClient to ${MONGODB_OLD} and ${MONGODB_NEW}.`); + + return processChallenges(); + }) + .catch(function (err) { + console.error(err.stack || err); + }); diff --git a/migrations/archive/api_v3/challengesMembers.js b/migrations/archive/api_v3/challengesMembers.js new file mode 100644 index 0000000000..2691896b18 --- /dev/null +++ b/migrations/archive/api_v3/challengesMembers.js @@ -0,0 +1,149 @@ +// Migrate challenges members +// Run AFTER users migration + +// The console-stamp module must be installed (not included in package.json) + +// It requires two environment variables: MONGODB_OLD and MONGODB_NEW + +// Due to some big user profiles it needs more RAM than is allowed by default by v8 (arounf 1.7GB). +// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM +console.log('Starting migrations/api_v3/challengesMembers.js.'); + +// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. +// We've now upgraded to lodash v4 but the code used in this migration has not been +// adapted to work with it. Before this migration is used again any lodash method should +// be checked for compatibility against the v4 changelog and changed if necessary. +// https://github.com/lodash/lodash/wiki/Changelog#v400 + +require('babel-register'); +require('babel-polyfill'); + +let Bluebird = require('bluebird'); +let MongoDB = require('mongodb'); +let nconf = require('nconf'); +let mongoose = require('mongoose'); +let _ = require('lodash'); +let uuid = require('uuid'); +let consoleStamp = require('console-stamp'); + +// Add timestamps to console messages +consoleStamp(console); + +// Initialize configuration +require('../../website/server/libs/api-v3/setupNconf')(); + +let MONGODB_OLD = nconf.get('MONGODB_OLD'); +let MONGODB_NEW = nconf.get('MONGODB_NEW'); + +let MongoClient = MongoDB.MongoClient; + +mongoose.Promise = Bluebird; // otherwise mongoose models won't work + +// To be defined later when MongoClient connects +let mongoDbOldInstance; +let oldChallengeCollection; + +let mongoDbNewInstance; +let newUserCollection; + +let BATCH_SIZE = 1000; + +let processedChallenges = 0; + +// Only process challenges that fall in a interval ie -> up to 0000-4000-0000-0000 +let AFTER_CHALLENGE_ID = nconf.get('AFTER_CHALLENGE_ID'); +let BEFORE_CHALLENGE_ID = nconf.get('BEFORE_CHALLENGE_ID'); + +function processChallenges (afterId) { + let processedTasks = 0; + let lastChallenge = null; + let oldChallenges; + + let query = {}; + + if (BEFORE_CHALLENGE_ID) { + query._id = {$lte: BEFORE_CHALLENGE_ID}; + } + + if ((afterId || AFTER_CHALLENGE_ID) && !query._id) { + query._id = {}; + } + + if (afterId) { + query._id.$gt = afterId; + } else if (AFTER_CHALLENGE_ID) { + query._id.$gt = AFTER_CHALLENGE_ID; + } + + console.log(`Executing challenges query.\nMatching challenges after ${afterId ? afterId : AFTER_CHALLENGE_ID} and before ${BEFORE_CHALLENGE_ID} (included).`); + + return oldChallengeCollection + .find(query) + .sort({_id: 1}) + .limit(BATCH_SIZE) + .toArray() + .then(function (oldChallengesR) { + oldChallenges = oldChallengesR; + + let promises = []; + + console.log(`Processing ${oldChallenges.length} challenges. Already processed ${processedChallenges} challenges.`); + + if (oldChallenges.length === BATCH_SIZE) { + lastChallenge = oldChallenges[oldChallenges.length - 1]._id; + } + + oldChallenges.forEach(function (oldChallenge) { + // Tyler Renelle + oldChallenge.members.forEach(function (id, index) { + if (id === '9') { + oldChallenge.members[index] = '00000000-0000-4000-9000-000000000000'; + } + }); + + promises.push(newUserCollection.updateMany({ + _id: {$in: oldChallenge.members || []}, + }, { + $push: {challenges: oldChallenge._id}, + }, {multi: true})); + }); + + console.log(`Migrating members of ${oldChallenges.length} challenges.`); + + return Bluebird.all(promises); + }) + .then(function () { + processedChallenges += oldChallenges.length; + + console.log(`Migrated members of ${oldChallenges.length} challenges.`); + + if (lastChallenge) { + return processChallenges(lastChallenge); + } else { + return console.log('Done!'); + } + }); +} + +// Connect to the databases +Bluebird.all([ + MongoClient.connect(MONGODB_OLD), + MongoClient.connect(MONGODB_NEW), +]) + .then(function (result) { + let oldInstance = result[0]; + let newInstance = result[1]; + + mongoDbOldInstance = oldInstance; + oldChallengeCollection = mongoDbOldInstance.collection('challenges'); + + mongoDbNewInstance = newInstance; + newUserCollection = mongoDbNewInstance.collection('users'); + + console.log(`Connected with MongoClient to ${MONGODB_OLD} and ${MONGODB_NEW}.`); + + return processChallenges(); + }) + .catch(function (err) { + console.error(err.stack || err); + }); diff --git a/migrations/archive/api_v3/coupons.js b/migrations/archive/api_v3/coupons.js new file mode 100644 index 0000000000..22da730515 --- /dev/null +++ b/migrations/archive/api_v3/coupons.js @@ -0,0 +1,142 @@ +// Migrate coupons collection to new schema + +// The console-stamp module must be installed (not included in package.json) + +// It requires two environment variables: MONGODB_OLD and MONGODB_NEW + +// Due to some big user profiles it needs more RAM than is allowed by default by v8 (arounf 1.7GB). +// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM +console.log('Starting migrations/api_v3/coupons.js.'); + +// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. +// We've now upgraded to lodash v4 but the code used in this migration has not been +// adapted to work with it. Before this migration is used again any lodash method should +// be checked for compatibility against the v4 changelog and changed if necessary. +// https://github.com/lodash/lodash/wiki/Changelog#v400 + +require('babel-register'); +require('babel-polyfill'); + +let Bluebird = require('bluebird'); +let MongoDB = require('mongodb'); +let nconf = require('nconf'); +let mongoose = require('mongoose'); +let _ = require('lodash'); +let uuid = require('uuid'); +let consoleStamp = require('console-stamp'); + +// Add timestamps to console messages +consoleStamp(console); + +// Initialize configuration +require('../../website/server/libs/api-v3/setupNconf')(); + +let MONGODB_OLD = nconf.get('MONGODB_OLD'); +let MONGODB_NEW = nconf.get('MONGODB_NEW'); + +let MongoClient = MongoDB.MongoClient; + +mongoose.Promise = Bluebird; // otherwise mongoose models won't work + +// Load new models +let Coupon = require('../../website/server/models/coupon').model; + +// To be defined later when MongoClient connects +let mongoDbOldInstance; +let oldCouponCollection; + +let mongoDbNewInstance; +let newCouponCollection; + +let BATCH_SIZE = 1000; + +let processedCoupons = 0; + +// Only process coupons that fall in a interval ie -> up to 0000-4000-0000-0000 +let AFTER_COUPON_ID = nconf.get('AFTER_COUPON_ID'); +let BEFORE_COUPON_ID = nconf.get('BEFORE_COUPON_ID'); + +function processCoupons (afterId) { + let processedTasks = 0; + let lastCoupon = null; + let oldCoupons; + + let query = {}; + + if (BEFORE_COUPON_ID) { + query._id = {$lte: BEFORE_COUPON_ID}; + } + + if ((afterId || AFTER_COUPON_ID) && !query._id) { + query._id = {}; + } + + if (afterId) { + query._id.$gt = afterId; + } else if (AFTER_COUPON_ID) { + query._id.$gt = AFTER_COUPON_ID; + } + + let batchInsertCoupons = newCouponCollection.initializeUnorderedBulkOp(); + + console.log(`Executing coupons query.\nMatching coupons after ${afterId ? afterId : AFTER_COUPON_ID} and before ${BEFORE_COUPON_ID} (included).`); + + return oldCouponCollection + .find(query) + .sort({_id: 1}) + .limit(BATCH_SIZE) + .toArray() + .then(function (oldCouponsR) { + oldCoupons = oldCouponsR; + + console.log(`Processing ${oldCoupons.length} coupons. Already processed ${processedCoupons} coupons.`); + + if (oldCoupons.length === BATCH_SIZE) { + lastCoupon = oldCoupons[oldCoupons.length - 1]._id; + } + + oldCoupons.forEach(function (oldCoupon) { + let newCoupon = new Coupon(oldCoupon); + + batchInsertCoupons.insert(newCoupon.toObject()); + }); + + console.log(`Saving ${oldCoupons.length} coupons.`); + + return batchInsertCoupons.execute(); + }) + .then(function () { + processedCoupons += oldCoupons.length; + + console.log(`Saved ${oldCoupons.length} coupons.`); + + if (lastCoupon) { + return processCoupons(lastCoupon); + } else { + return console.log('Done!'); + } + }); +} + +// Connect to the databases +Bluebird.all([ + MongoClient.connect(MONGODB_OLD), + MongoClient.connect(MONGODB_NEW), +]) + .then(function (result) { + let oldInstance = result[0]; + let newInstance = result[1]; + + mongoDbOldInstance = oldInstance; + oldCouponCollection = mongoDbOldInstance.collection('coupons'); + + mongoDbNewInstance = newInstance; + newCouponCollection = mongoDbNewInstance.collection('coupons'); + + console.log(`Connected with MongoClient to ${MONGODB_OLD} and ${MONGODB_NEW}.`); + + return processCoupons(); + }) + .catch(function (err) { + console.error(err.stack || err); + }); diff --git a/migrations/archive/api_v3/emailUnsubscriptions.js b/migrations/archive/api_v3/emailUnsubscriptions.js new file mode 100644 index 0000000000..21eae2a13c --- /dev/null +++ b/migrations/archive/api_v3/emailUnsubscriptions.js @@ -0,0 +1,143 @@ +// Migrate unsubscriptions collection to new schema + +// The console-stamp module must be installed (not included in package.json) + +// It requires two environment variables: MONGODB_OLD and MONGODB_NEW + +// Due to some big user profiles it needs more RAM than is allowed by default by v8 (arounf 1.7GB). +// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM +console.log('Starting migrations/api_v3/unsubscriptions.js.'); + +// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. +// We've now upgraded to lodash v4 but the code used in this migration has not been +// adapted to work with it. Before this migration is used again any lodash method should +// be checked for compatibility against the v4 changelog and changed if necessary. +// https://github.com/lodash/lodash/wiki/Changelog#v400 + +require('babel-register'); +require('babel-polyfill'); + +let Bluebird = require('bluebird'); +let MongoDB = require('mongodb'); +let nconf = require('nconf'); +let mongoose = require('mongoose'); +let _ = require('lodash'); +let uuid = require('uuid'); +let consoleStamp = require('console-stamp'); + +// Add timestamps to console messages +consoleStamp(console); + +// Initialize configuration +require('../../website/server/libs/api-v3/setupNconf')(); + +let MONGODB_OLD = nconf.get('MONGODB_OLD'); +let MONGODB_NEW = nconf.get('MONGODB_NEW'); + +let MongoClient = MongoDB.MongoClient; + +mongoose.Promise = Bluebird; // otherwise mongoose models won't work + +// Load new models +let EmailUnsubscription = require('../../website/server/models/emailUnsubscription').model; + +// To be defined later when MongoClient connects +let mongoDbOldInstance; +let oldUnsubscriptionCollection; + +let mongoDbNewInstance; +let newUnsubscriptionCollection; + +let BATCH_SIZE = 1000; + +let processedUnsubscriptions = 0; + +// Only process unsubscriptions that fall in a interval ie -> up to 0000-4000-0000-0000 +let AFTER_UNSUBSCRIPTION_ID = nconf.get('AFTER_UNSUBSCRIPTION_ID'); +let BEFORE_UNSUBSCRIPTION_ID = nconf.get('BEFORE_UNSUBSCRIPTION_ID'); + +function processUnsubscriptions (afterId) { + let processedTasks = 0; + let lastUnsubscription = null; + let oldUnsubscriptions; + + let query = {}; + + if (BEFORE_UNSUBSCRIPTION_ID) { + query._id = {$lte: BEFORE_UNSUBSCRIPTION_ID}; + } + + if ((afterId || AFTER_UNSUBSCRIPTION_ID) && !query._id) { + query._id = {}; + } + + if (afterId) { + query._id.$gt = afterId; + } else if (AFTER_UNSUBSCRIPTION_ID) { + query._id.$gt = AFTER_UNSUBSCRIPTION_ID; + } + + let batchInsertUnsubscriptions = newUnsubscriptionCollection.initializeUnorderedBulkOp(); + + console.log(`Executing unsubscriptions query.\nMatching unsubscriptions after ${afterId ? afterId : AFTER_UNSUBSCRIPTION_ID} and before ${BEFORE_UNSUBSCRIPTION_ID} (included).`); + + return oldUnsubscriptionCollection + .find(query) + .sort({_id: 1}) + .limit(BATCH_SIZE) + .toArray() + .then(function (oldUnsubscriptionsR) { + oldUnsubscriptions = oldUnsubscriptionsR; + + console.log(`Processing ${oldUnsubscriptions.length} unsubscriptions. Already processed ${processedUnsubscriptions} unsubscriptions.`); + + if (oldUnsubscriptions.length === BATCH_SIZE) { + lastUnsubscription = oldUnsubscriptions[oldUnsubscriptions.length - 1]._id; + } + + oldUnsubscriptions.forEach(function (oldUnsubscription) { + oldUnsubscription.email = oldUnsubscription.email.toLowerCase(); + let newUnsubscription = new EmailUnsubscription(oldUnsubscription); + + batchInsertUnsubscriptions.insert(newUnsubscription.toObject()); + }); + + console.log(`Saving ${oldUnsubscriptions.length} unsubscriptions.`); + + return batchInsertUnsubscriptions.execute(); + }) + .then(function () { + processedUnsubscriptions += oldUnsubscriptions.length; + + console.log(`Saved ${oldUnsubscriptions.length} unsubscriptions.`); + + if (lastUnsubscription) { + return processUnsubscriptions(lastUnsubscription); + } else { + return console.log('Done!'); + } + }); +} + +// Connect to the databases +Bluebird.all([ + MongoClient.connect(MONGODB_OLD), + MongoClient.connect(MONGODB_NEW), +]) + .then(function (result) { + let oldInstance = result[0]; + let newInstance = result[1]; + + mongoDbOldInstance = oldInstance; + oldUnsubscriptionCollection = mongoDbOldInstance.collection('emailunsubscriptions'); + + mongoDbNewInstance = newInstance; + newUnsubscriptionCollection = mongoDbNewInstance.collection('emailunsubscriptions'); + + console.log(`Connected with MongoClient to ${MONGODB_OLD} and ${MONGODB_NEW}.`); + + return processUnsubscriptions(); + }) + .catch(function (err) { + console.error(err.stack || err); + }); diff --git a/migrations/archive/api_v3/groups.js b/migrations/archive/api_v3/groups.js new file mode 100644 index 0000000000..1d1d7e3b0c --- /dev/null +++ b/migrations/archive/api_v3/groups.js @@ -0,0 +1,217 @@ +/* + members are not stored anymore + invites are not stored anymore + + tavern id and leader must be updated +*/ + +// Migrate groups collection to new schema +// Run AFTER users migration + +// The console-stamp module must be installed (not included in package.json) + +// It requires two environment variables: MONGODB_OLD and MONGODB_NEW + +// Due to some big user profiles it needs more RAM than is allowed by default by v8 (arounf 1.7GB). +// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM +console.log('Starting migrations/api_v3/groups.js.'); + +// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. +// We've now upgraded to lodash v4 but the code used in this migration has not been +// adapted to work with it. Before this migration is used again any lodash method should +// be checked for compatibility against the v4 changelog and changed if necessary. +// https://github.com/lodash/lodash/wiki/Changelog#v400 + +require('babel-register'); +require('babel-polyfill'); + +let Bluebird = require('bluebird'); +let MongoDB = require('mongodb'); +let nconf = require('nconf'); +let mongoose = require('mongoose'); +let _ = require('lodash'); +let uuid = require('uuid'); +let consoleStamp = require('console-stamp'); + +// Add timestamps to console messages +consoleStamp(console); + +// Initialize configuration +require('../../website/server/libs/api-v3/setupNconf')(); + +let MONGODB_OLD = nconf.get('MONGODB_OLD'); +let MONGODB_NEW = nconf.get('MONGODB_NEW'); + +let MongoClient = MongoDB.MongoClient; + +mongoose.Promise = Bluebird; // otherwise mongoose models won't work + +// Load new models +let NewGroup = require('../../website/server/models/group').model; + +let TAVERN_ID = require('../../website/server/models/group').TAVERN_ID; + +// To be defined later when MongoClient connects +let mongoDbOldInstance; +let oldGroupCollection; + +let mongoDbNewInstance; +let newGroupCollection; +let newUserCollection; + +let BATCH_SIZE = 1000; + +let processedGroups = 0; + +// Only process groups that fall in a interval ie -> up to 0000-4000-0000-0000 +let AFTER_GROUP_ID = nconf.get('AFTER_GROUP_ID'); +let BEFORE_GROUP_ID = nconf.get('BEFORE_GROUP_ID'); + +function processGroups (afterId) { + let processedTasks = 0; + let lastGroup = null; + let oldGroups; + + let query = {}; + + if (BEFORE_GROUP_ID) { + query._id = {$lte: BEFORE_GROUP_ID}; + } + + if ((afterId || AFTER_GROUP_ID) && !query._id) { + query._id = {}; + } + + if (afterId) { + query._id.$gt = afterId; + } else if (AFTER_GROUP_ID) { + query._id.$gt = AFTER_GROUP_ID; + } + + let batchInsertGroups = newGroupCollection.initializeUnorderedBulkOp(); + + console.log(`Executing groups query.\nMatching groups after ${afterId ? afterId : AFTER_GROUP_ID} and before ${BEFORE_GROUP_ID} (included).`); + + return oldGroupCollection + .find(query) + .sort({_id: 1}) + .limit(BATCH_SIZE) + .toArray() + .then(function (oldGroupsR) { + oldGroups = oldGroupsR; + + let promises = []; + + console.log(`Processing ${oldGroups.length} groups. Already processed ${processedGroups} groups.`); + + if (oldGroups.length === BATCH_SIZE) { + lastGroup = oldGroups[oldGroups.length - 1]._id; + } + + oldGroups.forEach(function (oldGroup) { + if ((!oldGroup.privacy || oldGroup.privacy === 'private') && (!oldGroup.members || oldGroup.members.length === 0)) return; // delete empty private groups TODO must also delete challenges or this won't work + + oldGroup.members = oldGroup.members || []; + oldGroup.memberCount = oldGroup.members ? oldGroup.members.length : 0; + oldGroup.challengeCount = oldGroup.challenges ? oldGroup.challenges.length : 0; + + if (oldGroup.balance <= 0) oldGroup.balance = 0; + if (!oldGroup.name) oldGroup.name = 'group name'; + if (!oldGroup.leaderOnly) oldGroup.leaderOnly = {}; + if (!oldGroup.leaderOnly.challenges) oldGroup.leaderOnly.challenges = false; + + // Tavern + if (oldGroup._id === 'habitrpg') { + oldGroup._id = TAVERN_ID; + oldGroup.leader = '7bde7864-ebc5-4ee2-a4b7-1070d464cdb0'; // Siena Leslie + } + + if (!oldGroup.type) { + // throw new Error('group.type is required'); + oldGroup.type = 'guild'; + } + + if (!oldGroup.leader) { + if (oldGroup.members && oldGroup.members.length > 0) { + oldGroup.leader = oldGroup.members[0]; + } else { + throw new Error('group.leader is required and no member available!'); + } + } + + if (!oldGroup.privacy) { + // throw new Error('group.privacy is required'); + oldGroup.privacy = 'private'; + } + + let updateMembers = {}; + + if (oldGroup.type === 'guild') { + updateMembers.$push = {guilds: oldGroup._id}; + } else if (oldGroup.type === 'party') { + updateMembers.$set = {'party._id': oldGroup._id}; + } + + if (oldGroup.members) { + // Tyler Renelle + oldGroup.members.forEach(function (id, index) { + if (id === '9') { + oldGroup.members[index] = '00000000-0000-4000-9000-000000000000'; + } + }); + + promises.push(newUserCollection.updateMany({ + _id: {$in: oldGroup.members}, + }, updateMembers, {multi: true})); + } + + let newGroup = new NewGroup(oldGroup); + + batchInsertGroups.insert(newGroup.toObject()); + }); + + console.log(`Saving ${oldGroups.length} groups and migrating members to users collection.`); + + promises.push(batchInsertGroups.execute()); + return Bluebird.all(promises); + }) + .then(function () { + processedGroups += oldGroups.length; + + console.log(`Saved ${oldGroups.length} groups and migrated their members to the user collection.`); + + if (lastGroup) { + return processGroups(lastGroup); + } else { + return console.log('Done!'); + } + }); +} + +// Connect to the databases +Bluebird.all([ + MongoClient.connect(MONGODB_OLD), + MongoClient.connect(MONGODB_NEW), +]) + .then(function (result) { + let oldInstance = result[0]; + let newInstance = result[1]; + + mongoDbOldInstance = oldInstance; + oldGroupCollection = mongoDbOldInstance.collection('groups'); + + mongoDbNewInstance = newInstance; + newGroupCollection = mongoDbNewInstance.collection('groups'); + newUserCollection = mongoDbNewInstance.collection('users'); + + console.log(`Connected with MongoClient to ${MONGODB_OLD} and ${MONGODB_NEW}.`); + + // First delete the tavern group created by having required the group model + return newGroupCollection.deleteOne({_id: TAVERN_ID}); + }) + .then(function () { + return processGroups(); + }) + .catch(function (err) { + console.error(err.stack || err); + }); diff --git a/migrations/api_v3/indexes.js b/migrations/archive/api_v3/indexes.js similarity index 100% rename from migrations/api_v3/indexes.js rename to migrations/archive/api_v3/indexes.js diff --git a/migrations/archive/api_v3/users.js b/migrations/archive/api_v3/users.js new file mode 100644 index 0000000000..1560c9d3dd --- /dev/null +++ b/migrations/archive/api_v3/users.js @@ -0,0 +1,268 @@ +// Migrate users collection to new schema +// This should run AFTER challenges migration + +// The console-stamp module must be installed (not included in package.json) + +// It requires two environment variables: MONGODB_OLD and MONGODB_NEW + +// Due to some big user profiles it needs more RAM than is allowed by default by v8 (arounf 1.7GB). +// Run the script with --max-old-space-size=4096 to allow up to 4GB of RAM +console.log('Starting migrations/api_v3/users.js.'); + +// IMPORTANT NOTE: this migration was written when we were using version 3 of lodash. +// We've now upgraded to lodash v4 but the code used in this migration has not been +// adapted to work with it. Before this migration is used again any lodash method should +// be checked for compatibility against the v4 changelog and changed if necessary. +// https://github.com/lodash/lodash/wiki/Changelog#v400 + +require('babel-register'); +require('babel-polyfill'); + +let Bluebird = require('bluebird'); +let MongoDB = require('mongodb'); +let nconf = require('nconf'); +let mongoose = require('mongoose'); +let _ = require('lodash'); +let uuid = require('uuid'); +let consoleStamp = require('console-stamp'); +let common = require('../../common'); +let moment = require('moment'); + +// Add timestamps to console messages +consoleStamp(console); + +// Initialize configuration +require('../../website/server/libs/api-v3/setupNconf')(); + +let MONGODB_OLD = nconf.get('MONGODB_OLD'); +let MONGODB_NEW = nconf.get('MONGODB_NEW'); + +let taskDefaults = common.taskDefaults; +let MongoClient = MongoDB.MongoClient; + +mongoose.Promise = Bluebird; // otherwise mongoose models won't work + +// Load new models +let NewUser = require('../../website/server/models/user').model; +let NewTasks = require('../../website/server/models/task'); + +// To be defined later when MongoClient connects +let mongoDbOldInstance; +let oldUserCollection; + +let mongoDbNewInstance; +let newUserCollection; +let newTaskCollection; + +let BATCH_SIZE = 1000; + +let processedUsers = 0; +let totoalProcessedTasks = 0; + +let challengeTaskWithMatchingId = 0; +let challengeTaskNoMatchingId = 0; + +// Load the new tasks ids for challenges tasks +let newTasksIds = require('./newTasksIds.json'); + +// Only process users that fall in a interval ie up to -> 0000-4000-0000-0000 +let AFTER_USER_ID = nconf.get('AFTER_USER_ID'); +let BEFORE_USER_ID = nconf.get('BEFORE_USER_ID'); + +function processUsers (afterId) { + let processedTasks = 0; + let lastUser = null; + let oldUsers; + + let now = new Date(); + + let query = {}; + + if (BEFORE_USER_ID) { + query._id = {$lte: BEFORE_USER_ID}; + } + + if ((afterId || AFTER_USER_ID) && !query._id) { + query._id = {}; + } + + if (afterId) { + query._id.$gt = afterId; + } else if (AFTER_USER_ID) { + query._id.$gt = AFTER_USER_ID; + } + + let batchInsertTasks = newTaskCollection.initializeUnorderedBulkOp(); + let batchInsertUsers = newUserCollection.initializeUnorderedBulkOp(); + + console.log(`Executing users query.\nMatching users after ${afterId ? afterId : AFTER_USER_ID} and before ${BEFORE_USER_ID} (included).`); + + return oldUserCollection + .find(query) + .sort({_id: 1}) + .limit(BATCH_SIZE) + .toArray() + .then(function (oldUsersR) { + oldUsers = oldUsersR; + + console.log(`Processing ${oldUsers.length} users. Already processed ${processedUsers} users and ${totoalProcessedTasks} tasks.`); + + if (oldUsers.length === BATCH_SIZE) { + lastUser = oldUsers[oldUsers.length - 1]._id; + } + + oldUsers.forEach(function (oldUser) { + let oldTasks = oldUser.habits.concat(oldUser.dailys).concat(oldUser.rewards).concat(oldUser.todos); + delete oldUser.habits; + delete oldUser.dailys; + delete oldUser.rewards; + delete oldUser.todos; + + delete oldUser.id; + + // spookDust -> spookySparkles + + if (oldUser.achievements && oldUser.achievements.spookDust) { + oldUser.achievements.spookySparkles = oldUser.achievements.spookDust; + delete oldUser.achievements.spookDust; + } + + if (oldUser.items && oldUser.items.special && oldUser.items.special.spookDust) { + oldUser.items.special.spookySparkles = oldUser.items.special.spookDust; + delete oldUser.items.special.spookDust; + } + + if (oldUser.stats && oldUser.stats.buffs && oldUser.stats.buffs.spookySparkles) { + oldUser.stats.buffs.spookySparkles = oldUser.stats.buffs.spookDust; + delete oldUser.stats.buffs.spookDust; + } + + // end spookDust -> spookySparkles + + oldUser.tags = oldUser.tags.map(function (tag) { + return { + id: tag.id, + name: tag.name || 'tag name', + challenge: tag.challenge, + }; + }); + + if (oldUser._id === '9') { // Tyler Renelle + oldUser._id = '00000000-0000-4000-9000-000000000000'; + } + + let newUser = new NewUser(oldUser); + let isSubscribed = newUser.isSubscribed(); + + oldTasks.forEach(function (oldTask) { + oldTask._id = uuid.v4(); // create a new unique uuid + oldTask.userId = newUser._id; + oldTask._legacyId = oldTask.id; // store the old task id + delete oldTask.id; + + oldTask.challenge = oldTask.challenge || {}; + if (oldTask.challenge.id) { + if (oldTask.challenge.broken) { + oldTask.challenge.taskId = oldTask._legacyId; + } else { + let newId = newTasksIds[`${oldTask._legacyId }-${ oldTask.challenge.id}`]; + + // Challenges' tasks ids changed + if (!newId && !oldTask.challenge.broken) { + challengeTaskNoMatchingId++; + oldTask.challenge.taskId = oldTask._legacyId; + oldTask.challenge.broken = 'CHALLENGE_TASK_NOT_FOUND'; + } else { + challengeTaskWithMatchingId++; + oldTask.challenge.taskId = newId; + } + } + } + + // Delete old completed todos + if (oldTask.type === 'todo' && oldTask.completed && (!oldTask.challenge.id || oldTask.challenge.broken)) { + if (moment(now).subtract(isSubscribed ? 90 : 30, 'days').toDate() > moment(oldTask.dateCompleted).toDate()) { + return; + } + } + + oldTask.createdAt = oldTask.dateCreated; + + if (!oldTask.text) oldTask.text = 'task text'; // required + oldTask.tags = _.map(oldTask.tags, function (tagPresent, tagId) { + return tagPresent && tagId; + }).filter(function (tag) { + return tag !== false; + }); + + if (oldTask.type !== 'todo' || oldTask.type === 'todo' && !oldTask.completed) { + newUser.tasksOrder[`${oldTask.type}s`].push(oldTask._id); + } + + let allTasksFields = ['_id', 'type', 'text', 'notes', 'tags', 'value', 'priority', 'attribute', 'challenge', 'reminders', 'userId', '_legacyId', 'createdAt']; + // using mongoose models is too slow + if (oldTask.type === 'habit') { + oldTask = _.pick(oldTask, allTasksFields.concat(['history', 'up', 'down'])); + } else if (oldTask.type === 'daily') { + oldTask = _.pick(oldTask, allTasksFields.concat(['completed', 'collapseChecklist', 'checklist', 'history', 'frequency', 'everyX', 'startDate', 'repeat', 'streak'])); + } else if (oldTask.type === 'todo') { + oldTask = _.pick(oldTask, allTasksFields.concat(['completed', 'collapseChecklist', 'checklist', 'date', 'dateCompleted'])); + } else if (oldTask.type === 'reward') { + oldTask = _.pick(oldTask, allTasksFields); + } else { + throw new Error('Task with no or invalid type!'); + } + + batchInsertTasks.insert(taskDefaults(oldTask)); + processedTasks++; + }); + + batchInsertUsers.insert(newUser.toObject()); + }); + + console.log(`Saving ${oldUsers.length} users and ${processedTasks} tasks.`); + + return Bluebird.all([ + batchInsertUsers.execute(), + batchInsertTasks.execute(), + ]); + }) + .then(function () { + totoalProcessedTasks += processedTasks; + processedUsers += oldUsers.length; + + console.log(`Saved ${oldUsers.length} users and their tasks.`); + console.log('Challenges\' tasks no matching id: ', challengeTaskNoMatchingId); + console.log('Challenges\' tasks with matching id: ', challengeTaskWithMatchingId); + + if (lastUser) { + return processUsers(lastUser); + } else { + return console.log('Done!'); + } + }); +} + +// Connect to the databases +Bluebird.all([ + MongoClient.connect(MONGODB_OLD), + MongoClient.connect(MONGODB_NEW), +]) + .then(function (result) { + let oldInstance = result[0]; + let newInstance = result[1]; + + mongoDbOldInstance = oldInstance; + oldUserCollection = mongoDbOldInstance.collection('users'); + + mongoDbNewInstance = newInstance; + newUserCollection = mongoDbNewInstance.collection('users'); + newTaskCollection = mongoDbNewInstance.collection('tasks'); + + console.log(`Connected with MongoClient to ${MONGODB_OLD} and ${MONGODB_NEW}.`); + + return processUsers(); + }) + .catch(function (err) { + console.error(err.stack || err); + }); diff --git a/migrations/manual_password_reset.js b/migrations/archive/manual_password_reset.js similarity index 54% rename from migrations/manual_password_reset.js rename to migrations/archive/manual_password_reset.js index 6e84673eaa..94262a17ec 100644 --- a/migrations/manual_password_reset.js +++ b/migrations/archive/manual_password_reset.js @@ -4,11 +4,11 @@ // IMPORTANT: this script isn't updated to use the new password encryption that uses bcrypt // using it will break accounts and should not be used until upgraded -var nconf = require('nconf'), +let nconf = require('nconf'), path = require('path'); nconf.argv().env().file('user', path.join(path.resolve(__dirname, '../config.json'))); -var Users = require('mongoskin').db(nconf.get("PRODUCTION_DB:URL"), nconf.get("PRODUCTION_DB").CREDS).collection('users'), +let Users = require('mongoskin').db(nconf.get('PRODUCTION_DB:URL'), nconf.get('PRODUCTION_DB').CREDS).collection('users'), async = require('async'), utils = require('../website/server/utils'), salt = utils.makeSalt(), @@ -16,23 +16,23 @@ var Users = require('mongoskin').db(nconf.get("PRODUCTION_DB:URL"), nconf.get("P hashed_password = utils.encryptPassword(newPassword, salt); async.waterfall([ - function(cb){ - Users.findItems({'auth.local.email':nconf.get("EMAIL")}, {auth:1}, cb); + function (cb) { + Users.findItems({'auth.local.email': nconf.get('EMAIL')}, {auth: 1}, cb); }, - function(users, cb){ - if (users.length<1) return cb("User not found for that email"); - if (users.length>1) return cb("Multiple users for that email"); - var user = users[0]; - console.dir({username:user.auth.local.username, password: newPassword}); - Users.update({_id:user._id}, { - $set:{ + function (users, cb) { + if (users.length < 1) return cb('User not found for that email'); + if (users.length > 1) return cb('Multiple users for that email'); + let user = users[0]; + console.dir({username: user.auth.local.username, password: newPassword}); + Users.update({_id: user._id}, { + $set: { 'auth.local.salt': salt, 'auth.local.hashed_password': hashed_password, - 'preferences.sleep': true - } - }, cb) - } -], function(err){ + 'preferences.sleep': true, + }, + }, cb); + }, +], function (err) { if (err) console.log(err); - process.exit() + process.exit(); }); \ No newline at end of file diff --git a/migrations/archive/metrics.js b/migrations/archive/metrics.js new file mode 100644 index 0000000000..866c0bfc3a --- /dev/null +++ b/migrations/archive/metrics.js @@ -0,0 +1,85 @@ +// node habitrpg ./migrations/metrics.js + +let EXPORT_EMAILS = true; +let mongo = require('mongoskin'); +let csv = require('csv'); +let _ = require('lodash'); +let moment = require('moment'); +let db = mongo.db('localhost:27017/habitrpg?auto_reconnect'); +let twoWeeksAgo = moment().subtract(14, 'days'); +let angularRewrite = moment('07/09/2013'); + +let query = {auth: {$exists: 1}}; +let fields = {lastCron: 1, 'history.exp': 1, 'auth.timestamps': 1, 'auth.local.email': 1}; +db.collection('users').find(query, fields).toArray(function (err, items) { + if (err) return console.error({err}); + let stats = {total: _.size(items), lostToDerby: 0, isActive: 0}; + let emails = []; + _.each(items, function (item) { + // if (!item.history || !item.history.exp) console.log(item._id) + + // var hasBeenActive = item.history && item.history.exp && item.history.exp.length > 7; + let hasBeenActive = item.auth.timestamps && item.auth.timestamps.created && + Math.abs(moment(item.lastCron).diff(item.auth.timestamps.created, 'd')) > 14; + + if (/* hasBeenActive && */moment(item.lastCron).isBefore(angularRewrite)) { + stats.lostToDerby++; + if (item.auth.local) + emails.push([item.auth.local.email]); + // Facebook emails. Kinda dirty, and there's only ~30 available fb emails anyway. + // } else if (item.auth.facebook && item.auth.facebook.email) { + // emails.push([item.auth.facebook.email]) + // } else if (item.auth.facebook && item.auth.facebook.emails && item.auth.facebook.emails[0] && !!item.auth.facebook.emails[0].value) { + // emails.push([item.auth.facebook.emails[0].value]) + } + if (hasBeenActive && moment(item.lastCron).isAfter(twoWeeksAgo)) { + stats.isActive++; + } + }); + stats.emails = _.size(emails); + console.log(stats); + + if (EXPORT_EMAILS) + csv().from.array(emails).to.path(`${__dirname}/emails.csv`); +}); + +/* +load('./node_modules/moment/moment.js'); +var today = +new Date, + twoWeeksAgo = +moment().subtract(14, 'days'); + + corrupt = { + $or: [ + {lastCron: {$exists:false}}, + {lastCron: 'new'} + ] + } + + un_registered = { + "auth.local": {$exists: false}, + "auth.facebook": {$exists: false} + }, + + registered = { + $or: [ + { 'auth.local': { $exists: true }}, + { 'auth.facebook': { $exists: true }} + ] + }, + + active = { + $or: [ + { 'auth.local': { $exists: true }}, + { 'auth.facebook': { $exists: true }} + ], + $where: function(){ + return this.history && this.history.exp && this.history.exp.length > 7; + }, + 'lastCron': {$gt: twoWeeksAgo} + }; + +print('corrupt: ' + db.users.count(corrupt)); +print('unregistered: ' + db.users.count(un_registered)); +print('registered: ' + db.users.count(registered)); +print('active: ' + db.users.count(active)); +*/ \ No newline at end of file diff --git a/migrations/challenges/sync-all-challenges.js b/migrations/challenges/sync-all-challenges.js index 4cb9d00088..3be7a74bb0 100644 --- a/migrations/challenges/sync-all-challenges.js +++ b/migrations/challenges/sync-all-challenges.js @@ -4,14 +4,14 @@ import { model as Challenges } from '../../website/server/models/challenge'; import { model as User } from '../../website/server/models/user'; async function syncChallengeToMembers (challenges) { - let challengSyncPromises = challenges.map(async function (challenge) { + let challengSyncPromises = challenges.map(async (challenge) => { let users = await User.find({ // _id: '', challenges: challenge._id, }).exec(); let promises = []; - users.forEach(function (user) { + users.forEach((user) => { promises.push(challenge.syncToUser(user)); promises.push(challenge.save()); promises.push(user.save()); @@ -42,6 +42,6 @@ async function syncChallenges (lastChallengeDate) { let lastChallenge = challengesFound[challengesFound.length - 1]; if (lastChallenge) syncChallenges(lastChallenge.createdAt); return syncedChallenges; -}; +} module.exports = syncChallenges; diff --git a/migrations/command-line/.eslintrc b/migrations/command-line/.eslintrc new file mode 100644 index 0000000000..49551e8040 --- /dev/null +++ b/migrations/command-line/.eslintrc @@ -0,0 +1,10 @@ +{ + "root": false, + "globals": { + "db": true, + "moment": true, + "print": true, + "printjson": true, + "_": true, + }, +} diff --git a/migrations/command-line/apology_gems.js b/migrations/command-line/apology_gems.js new file mode 100644 index 0000000000..347a63d141 --- /dev/null +++ b/migrations/command-line/apology_gems.js @@ -0,0 +1 @@ +db.users.update({_id: {$in: ['']}}, {$inc: {balance: 0.5}}, {multi: true}); \ No newline at end of file diff --git a/migrations/cancelSubscription.js b/migrations/command-line/cancelSubscription.js similarity index 77% rename from migrations/cancelSubscription.js rename to migrations/command-line/cancelSubscription.js index 38a5fe318d..f823bed40f 100644 --- a/migrations/cancelSubscription.js +++ b/migrations/command-line/cancelSubscription.js @@ -4,8 +4,8 @@ // the FAQ (http://goo.gl/1uoPGQ) they insist... db.users.update( - {_id:''}, + {_id: ''}, {$set: { - 'purchased.plan.dateTerminated': moment().add('month',1).toDate() + 'purchased.plan.dateTerminated': moment().add('month', 1).toDate(), }} ); \ No newline at end of file diff --git a/migrations/command-line/contribs_plan.js b/migrations/command-line/contribs_plan.js new file mode 100644 index 0000000000..548ff6f18f --- /dev/null +++ b/migrations/command-line/contribs_plan.js @@ -0,0 +1,23 @@ +// Give contrib.level 7+ free subscription for life +db.users.update( + + { + 'contributor.level': {$gte: 7}, + 'purchased.plan.customerId': null, + }, + + { + $set: { + 'purchased.plan': { + planId: 'basic', + customerId: 'habitrpg', + dateCreated: new Date(), + dateUpdated: new Date(), + gemsBought: 0, + }, + }, + }, + + {multi: true} + +); \ No newline at end of file diff --git a/migrations/current_period_end.js b/migrations/command-line/current_period_end.js similarity index 52% rename from migrations/current_period_end.js rename to migrations/command-line/current_period_end.js index 31e33e4a79..56c9e8b45d 100644 --- a/migrations/current_period_end.js +++ b/migrations/command-line/current_period_end.js @@ -1,5 +1,5 @@ // mongo habitrpg ./node_modules/moment/moment.js ./migrations/current_period_end.js db.users.update( - {_id:''}, - {$set:{'purchased.plan.dateTerminated':moment().add({days:7}).toDate()}} -) \ No newline at end of file + {_id: ''}, + {$set: {'purchased.plan.dateTerminated': moment().add({days: 7}).toDate()}} +); \ No newline at end of file diff --git a/migrations/duplicatedTasksFindAndRemove.js b/migrations/command-line/duplicatedTasksFindAndRemove.js similarity index 75% rename from migrations/duplicatedTasksFindAndRemove.js rename to migrations/command-line/duplicatedTasksFindAndRemove.js index a6288efc4a..2dcdb10e5c 100644 --- a/migrations/duplicatedTasksFindAndRemove.js +++ b/migrations/command-line/duplicatedTasksFindAndRemove.js @@ -39,51 +39,49 @@ // needed. Do not miss any of them! -var uuid='30fb2640-7121-4968-ace5-f385e60ea6c5'; +let uuid = '30fb2640-7121-4968-ace5-f385e60ea6c5'; db.users.aggregate([ {$match: { - '_id': uuid + _id: uuid, }}, {$project: { - '_id':0, 'todos':1 + _id: 0, todos: 1, }}, {$unwind: '$todos'}, {$group: { _id: { taskid: '$todos.id' }, - count: { $sum: 1 } + count: { $sum: 1 }, }}, {$match: { - count: { $gt: 1 } + count: { $gt: 1 }, }}, {$project: { - '_id.taskid':1, + '_id.taskid': 1, }}, {$group: { _id: { taskid: '$todos.id' }, - troublesomeIds: { $addToSet: "$_id.taskid" }, + troublesomeIds: { $addToSet: '$_id.taskid' }, }}, {$project: { - '_id':0, - troublesomeIds:1, + _id: 0, + troublesomeIds: 1, }}, -]).forEach( - function(data) { - // print( "\n" ); printjson(data); - data.troublesomeIds.forEach( function(taskid) { - print('non-unique task: ' + taskid); - db.users.update({ - '_id': uuid, - 'todos': { $elemMatch: { id: taskid } } - },{ - $set: { "todos.$.id" : 'de666' } - }); +]).forEach((data) => { + // print( "\n" ); printjson(data); + data.troublesomeIds.forEach((taskid) => { + print(`non-unique task: ${ taskid}`); + db.users.update({ + _id: uuid, + todos: { $elemMatch: { id: taskid } }, + }, { + $set: { 'todos.$.id': 'de666' }, }); - } -); + }); +}); db.users.update( - {'_id': uuid}, + {_id: uuid}, {$pull: { todos: { id: 'de666' } } }, {multi: false } ); diff --git a/migrations/facebook_to_local.js b/migrations/command-line/facebook_to_local.js similarity index 51% rename from migrations/facebook_to_local.js rename to migrations/command-line/facebook_to_local.js index 0aca5573b1..de707c4f05 100644 --- a/migrations/facebook_to_local.js +++ b/migrations/command-line/facebook_to_local.js @@ -1,10 +1,10 @@ -var oldId = "", - newId = "", - newUser = db.users.findOne({_id: newId}) +let oldId = ''; +let newId = ''; +let newUser = db.users.findOne({_id: newId}); -db.users.update({_id: oldId}, {$set:{auth: newUser.auth}}); +db.users.update({_id: oldId}, {$set: {auth: newUser.auth}}); // remove the auth on the new user (which is a template account). The account will be preened automatically later, // this allows us to keep the account around a few days in case there was a mistake -db.users.update({_id: newId}, {$unset:{auth:1}}); +db.users.update({_id: newId}, {$unset: {auth: 1}}); diff --git a/migrations/find_unique_user.js b/migrations/command-line/find_unique_user.js similarity index 74% rename from migrations/find_unique_user.js rename to migrations/command-line/find_unique_user.js index 014933ac1f..378c6c8708 100644 --- a/migrations/find_unique_user.js +++ b/migrations/command-line/find_unique_user.js @@ -5,8 +5,8 @@ * Past in the text of a unique habit here to find the user, then you can restore their UUID */ -db.users.find().forEach(function(user){ +db.users.find().forEach((user) => { user.tasks = user.habits.concat(user.dailys).concat(user.todos).concat(user.rewards); - var found = _.some(user.tasks, {text: ""}) - if (found) printjson({id:user._id, auth:user.auth}); -}) \ No newline at end of file + let found = _.some(user.tasks, {text: ''}); + if (found) printjson({id: user._id, auth: user.auth}); +}); \ No newline at end of file diff --git a/migrations/freeMonth.js b/migrations/command-line/freeMonth.js similarity index 65% rename from migrations/freeMonth.js rename to migrations/command-line/freeMonth.js index cd2c501149..2b9b19cccd 100644 --- a/migrations/freeMonth.js +++ b/migrations/command-line/freeMonth.js @@ -1,16 +1,16 @@ // mongo habitrpg ./node_modules/moment/moment.js ./migrations/freeMonth.js db.users.update( - {_id:''}, - {$set:{ - 'purchased.plan.customerId':'temporary', - 'purchased.plan.paymentMethod':'Stripe', - 'purchased.plan.planId':'basic_earned', - 'purchased.plan.dateTerminated': moment().add('month',1).toDate() + {_id: ''}, + {$set: { + 'purchased.plan.customerId': 'temporary', + 'purchased.plan.paymentMethod': 'Stripe', + 'purchased.plan.planId': 'basic_earned', + 'purchased.plan.dateTerminated': moment().add('month', 1).toDate(), }} -) -//var m = 12; -//db.users.update( +); +// var m = 12; +// db.users.update( // {_id:''}, // {$set:{'purchased.plan':{ // planId: 'basic_'+m+'mo', @@ -29,4 +29,4 @@ db.users.update( // trinkets: m/3 // } // }}} -//) \ No newline at end of file +// ) \ No newline at end of file diff --git a/migrations/command-line/habitica_day.js b/migrations/command-line/habitica_day.js new file mode 100644 index 0000000000..9e4fd3af5b --- /dev/null +++ b/migrations/command-line/habitica_day.js @@ -0,0 +1,5 @@ +db.users.update( + {}, + {$inc: {'achievements.habiticaDays': 1}}, + {multi: 1} +); diff --git a/migrations/command-line/missing_gems.js b/migrations/command-line/missing_gems.js new file mode 100644 index 0000000000..080bfbd687 --- /dev/null +++ b/migrations/command-line/missing_gems.js @@ -0,0 +1 @@ +db.users.update({_id: ''}, {$inc: {balance: 5}}); \ No newline at end of file diff --git a/migrations/mystery_items.js b/migrations/command-line/mystery_items.js similarity index 51% rename from migrations/mystery_items.js rename to migrations/command-line/mystery_items.js index 28da1c7ed6..25d8b815af 100644 --- a/migrations/mystery_items.js +++ b/migrations/command-line/mystery_items.js @@ -1,26 +1,26 @@ -var UserNotification = require('../website/server/models/userNotification').model +let UserNotification = require('../../website/server/models/userNotification').model; -var _id = ''; +let _id = ''; -var items = ['back_mystery_201801','headAccessory_mystery_201801'] +let items = ['back_mystery_201801', 'headAccessory_mystery_201801']; -var update = { +let update = { $addToSet: { - 'purchased.plan.mysteryItems':{ + 'purchased.plan.mysteryItems': { $each: items, - } + }, }, $push: { notifications: (new UserNotification({ type: 'NEW_MYSTERY_ITEMS', data: { - items: items, + items, }, })).toJSON(), }, }; -/*var update = { +/* var update = { $set:{ 'purchased.plan':{ customerId: "", @@ -37,15 +37,15 @@ var update = { if (_id) { // singular (missing items) - db.users.update({_id: _id}, update); + db.users.update({_id}, update); } else { // multiple (once @ start of event) db.users.update({ - 'purchased.plan.customerId': { $ne: null }, - $or: [ - { 'purchased.plan.dateTerminated': { $gte: new Date() } }, - { 'purchased.plan.dateTerminated': { $exists: false } }, - { 'purchased.plan.dateTerminated': { $eq: null } } - ] + 'purchased.plan.customerId': { $ne: null }, + $or: [ + { 'purchased.plan.dateTerminated': { $gte: new Date() } }, + { 'purchased.plan.dateTerminated': { $exists: false } }, + { 'purchased.plan.dateTerminated': { $eq: null } }, + ], }, update, { multi: true }); } diff --git a/migrations/contribs_plan.js b/migrations/contribs_plan.js deleted file mode 100644 index a08bd93f76..0000000000 --- a/migrations/contribs_plan.js +++ /dev/null @@ -1,23 +0,0 @@ -// Give contrib.level 7+ free subscription for life -db.users.update( - - { - 'contributor.level':{$gte:7}, - 'purchased.plan.customerId':null - }, - - { - $set: { - 'purchased.plan':{ - planId: 'basic', - customerId: 'habitrpg', - dateCreated: new Date, - dateUpdated: new Date, - gemsBought: 0 - } - } - }, - - {multi:true} - -) \ No newline at end of file diff --git a/migrations/groups/add-unlimited-subscription.js b/migrations/groups/add-unlimited-subscription.js index 4a7eb27a02..43fbe55b5d 100644 --- a/migrations/groups/add-unlimited-subscription.js +++ b/migrations/groups/add-unlimited-subscription.js @@ -1,6 +1,8 @@ -var migrationName = 'AddUnlimitedSubscription'; -var authorName = 'TheHollidayInn'; // in case script author needs to know when their ... -var authorUuid = ''; //... own data is done +/* +let migrationName = 'AddUnlimitedSubscription'; +let authorName = 'TheHollidayInn'; // in case script author needs to know when their ... +let authorUuid = ''; // ... own data is done +*/ /* * This migrations will add a free subscription to a specified group @@ -11,30 +13,30 @@ import { model as Group } from '../../website/server/models/group'; // @TODO: this should probably be a GroupManager library method async function addUnlimitedSubscription (groupId, dateTerminated) { - let group = await Group.findById(groupId); + let group = await Group.findOne({_id: groupId}); - group.purchased.plan.customerId = "group-unlimited"; - group.purchased.plan.dateCreated = new Date(); - group.purchased.plan.dateUpdated = new Date(); - group.purchased.plan.paymentMethod = "Group Unlimited"; - group.purchased.plan.planId = "group_monthly"; - group.purchased.plan.dateTerminated = null; - if (dateTerminated) { - let dateToEnd = moment(dateTerminated).toDate(); - group.purchased.plan.dateTerminated = dateToEnd; - } - // group.purchased.plan.owner = ObjectId(); - group.purchased.plan.subscriptionId = ""; + group.purchased.plan.customerId = 'group-unlimited'; + group.purchased.plan.dateCreated = new Date(); + group.purchased.plan.dateUpdated = new Date(); + group.purchased.plan.paymentMethod = 'Group Unlimited'; + group.purchased.plan.planId = 'group_monthly'; + group.purchased.plan.dateTerminated = null; + if (dateTerminated) { + let dateToEnd = moment(dateTerminated).toDate(); + group.purchased.plan.dateTerminated = dateToEnd; + } + // group.purchased.plan.owner = ObjectId(); + group.purchased.plan.subscriptionId = ''; - return group.save(); -}; + return group.save(); +} module.exports = async function addUnlimitedSubscriptionCreator () { - let groupId = process.argv[2]; + let groupId = process.argv[2]; - if (!groupId) throw Error('Group ID is required'); + if (!groupId) throw Error('Group ID is required'); - let dateTerminated = process.argv[3]; + let dateTerminated = process.argv[3]; - let result = await addUnlimitedSubscription(groupId, dateTerminated); + await addUnlimitedSubscription(groupId, dateTerminated); }; diff --git a/migrations/groups/create-group.js b/migrations/groups/create-group.js index 79a4c03e63..d1cedcc4fc 100644 --- a/migrations/groups/create-group.js +++ b/migrations/groups/create-group.js @@ -5,28 +5,27 @@ import { model as User } from '../../website/server/models/user'; // @TODO: this should probably be a GroupManager library method async function createGroup (name, privacy, type, leaderId) { - let user = await User.findById(leaderId); - - let group = new Group({ - name, - privacy, - type, - }); + let user = await User.findOne({_id: leaderId}); - group.leader = user._id; - user.guilds.push(group._id); + let group = new Group({ + name, + privacy, + type, + }); - return Bluebird.all([group.save(), user.save()]); -}; + group.leader = user._id; + user.guilds.push(group._id); + + return Bluebird.all([group.save(), user.save()]); +} module.exports = async function groupCreator () { - let name = process.argv[2]; - let privacy = process.argv[3]; - let type = process.argv[4]; - let leaderId = process.argv[5]; + let name = process.argv[2]; + let privacy = process.argv[3]; + let type = process.argv[4]; + let leaderId = process.argv[5]; - let result = await createGroup(name, privacy, type, leaderId) + await createGroup(name, privacy, type, leaderId); }; - diff --git a/migrations/groups/habitrpg-jackalopes.js b/migrations/groups/habitrpg-jackalopes.js index de7df3eb49..50c6da4673 100644 --- a/migrations/groups/habitrpg-jackalopes.js +++ b/migrations/groups/habitrpg-jackalopes.js @@ -1,4 +1,4 @@ -var migrationName = 'Jackalopes for Unlimited Subscribers'; +/* let migrationName = 'Jackalopes for Unlimited Subscribers'; */ /* * This migration will find users with unlimited subscriptions who are also eligible for Jackalope mounts, and award them @@ -7,16 +7,15 @@ import Bluebird from 'bluebird'; import { model as Group } from '../../website/server/models/group'; import { model as User } from '../../website/server/models/user'; -import * as payments from '../../website/server/libs/payments'; async function handOutJackalopes () { let promises = []; let cursor = User.find({ - 'purchased.plan.customerId':'habitrpg', + 'purchased.plan.customerId': 'habitrpg', }).cursor(); - cursor.on('data', async function(user) { - console.log('User: ' + user._id); + cursor.on('data', async (user) => { + console.log(`User: ${ user._id}`); let groupList = []; if (user.party._id) groupList.push(user.party._id); @@ -24,23 +23,23 @@ async function handOutJackalopes () { let subscribedGroup = await Group.findOne({ - '_id': {$in: groupList}, + _id: {$in: groupList}, 'purchased.plan.planId': 'group_monthly', 'purchased.plan.dateTerminated': null, }, - {'_id':1} - ); + {_id: 1} + ); if (subscribedGroup) { - User.update({'_id':user._id},{$set:{'items.mounts.Jackalope-RoyalPurple':true}}).exec(); + User.update({_id: user._id}, {$set: {'items.mounts.Jackalope-RoyalPurple': true}}).exec(); promises.push(user.save()); } }); - cursor.on('close', async function() { + cursor.on('close', async () => { console.log('done'); return await Bluebird.all(promises); }); -}; +} module.exports = handOutJackalopes; diff --git a/migrations/groups/update-groups-with-group-plans.js b/migrations/groups/update-groups-with-group-plans.js index f217d54031..921c9bbb99 100644 --- a/migrations/groups/update-groups-with-group-plans.js +++ b/migrations/groups/update-groups-with-group-plans.js @@ -1,6 +1,8 @@ -var migrationName = 'ResyncGroupPlanMembers'; -var authorName = 'TheHollidayInn'; // in case script author needs to know when their ... -var authorUuid = ''; //... own data is done +/* +let migrationName = 'ResyncGroupPlanMembers'; +let authorName = 'TheHollidayInn'; // in case script author needs to know when their ... +let authorUuid = ''; // ... own data is done +*/ /* * This migrations will iterate through all groups with a group plan a subscription and resync the free @@ -20,14 +22,14 @@ async function updateGroupsWithGroupPlans () { let promises = []; - cursor.on('data', function(group) { + cursor.on('data', (group) => { promises.push(payments.addSubscriptionToGroupUsers(group)); - promises.push(group.save()) + promises.push(group.save()); }); - cursor.on('close', async function() { + cursor.on('close', async () => { return await Bluebird.all(promises); }); -}; +} module.exports = updateGroupsWithGroupPlans; diff --git a/migrations/habitica_day.js b/migrations/habitica_day.js deleted file mode 100644 index 6e352f6f06..0000000000 --- a/migrations/habitica_day.js +++ /dev/null @@ -1,5 +0,0 @@ -db.users.update( - {}, - {$inc:{'achievements.habiticaDays':1}}, - {multi:1} -); diff --git a/migrations/metrics.js b/migrations/metrics.js deleted file mode 100644 index 523aab9113..0000000000 --- a/migrations/metrics.js +++ /dev/null @@ -1,86 +0,0 @@ -// node habitrpg ./migrations/metrics.js - -var EXPORT_EMAILS = true; -var mongo = require('mongoskin'); -var csv = require('csv'); -var _ = require('lodash'); -var moment = require('moment'); -var db = mongo.db('localhost:27017/habitrpg?auto_reconnect'); -var twoWeeksAgo = moment().subtract(14, 'days'); -var angularRewrite = moment('07/09/2013'); - -var query = {auth: {'$exists':1}}; -var fields = {lastCron:1, 'history.exp':1, 'auth.timestamps':1, 'auth.local.email':1}; -db.collection('users').find(query, fields).toArray(function(err, items) { - if (err) return console.error({err:err}); - var stats = {total: _.size(items), lostToDerby: 0, isActive: 0}; - var emails = []; - _.each(items, function(item) { - //if (!item.history || !item.history.exp) console.log(item._id) - - //var hasBeenActive = item.history && item.history.exp && item.history.exp.length > 7; - var hasBeenActive = item.auth.timestamps && item.auth.timestamps.created && - (Math.abs(moment(item.lastCron).diff(item.auth.timestamps.created, 'd')) > 14); - - if (/*hasBeenActive && */moment(item.lastCron).isBefore(angularRewrite)) { - stats.lostToDerby++; - if (item.auth.local) - emails.push([item.auth.local.email]); - // Facebook emails. Kinda dirty, and there's only ~30 available fb emails anyway. -// } else if (item.auth.facebook && item.auth.facebook.email) { -// emails.push([item.auth.facebook.email]) -// } else if (item.auth.facebook && item.auth.facebook.emails && item.auth.facebook.emails[0] && !!item.auth.facebook.emails[0].value) { -// emails.push([item.auth.facebook.emails[0].value]) - } - if (hasBeenActive && moment(item.lastCron).isAfter(twoWeeksAgo)) { - stats.isActive++; - } - - }) - stats.emails = _.size(emails); - console.log(stats); - - if (EXPORT_EMAILS) - csv().from.array(emails).to.path(__dirname+'/emails.csv') -}); - -/* -load('./node_modules/moment/moment.js'); -var today = +new Date, - twoWeeksAgo = +moment().subtract(14, 'days'); - - corrupt = { - $or: [ - {lastCron: {$exists:false}}, - {lastCron: 'new'} - ] - } - - un_registered = { - "auth.local": {$exists: false}, - "auth.facebook": {$exists: false} - }, - - registered = { - $or: [ - { 'auth.local': { $exists: true }}, - { 'auth.facebook': { $exists: true }} - ] - }, - - active = { - $or: [ - { 'auth.local': { $exists: true }}, - { 'auth.facebook': { $exists: true }} - ], - $where: function(){ - return this.history && this.history.exp && this.history.exp.length > 7; - }, - 'lastCron': {$gt: twoWeeksAgo} - }; - -print('corrupt: ' + db.users.count(corrupt)); -print('unregistered: ' + db.users.count(un_registered)); -print('registered: ' + db.users.count(registered)); -print('active: ' + db.users.count(active)); -*/ \ No newline at end of file diff --git a/migrations/migration-runner.js b/migrations/migration-runner.js index 2b030b88e6..59a2a32a27 100644 --- a/migrations/migration-runner.js +++ b/migrations/migration-runner.js @@ -1,13 +1,13 @@ -require("babel-register"); -require("babel-polyfill"); +require('babel-register'); +require('babel-polyfill'); // This file must use ES5, everything required can be in ES6 function setUpServer () { - var nconf = require('nconf'); - var mongoose = require('mongoose'); - var Bluebird = require('bluebird'); - var setupNconf = require('../website/server/libs/setupNconf'); + const nconf = require('nconf'); // eslint-disable-line global-require, no-unused-vars + const mongoose = require('mongoose'); // eslint-disable-line global-require, no-unused-vars + const Bluebird = require('bluebird'); // eslint-disable-line global-require, no-unused-vars + const setupNconf = require('../website/server/libs/setupNconf'); // eslint-disable-line global-require setupNconf(); // We require src/server and npt src/index because // 1. nconf is already setup diff --git a/migrations/missing_gems.js b/migrations/missing_gems.js deleted file mode 100644 index 78ff37ed1d..0000000000 --- a/migrations/missing_gems.js +++ /dev/null @@ -1 +0,0 @@ -db.users.update({_id:''},{$inc:{balance:5}}); \ No newline at end of file diff --git a/migrations/new_stuff.js b/migrations/new_stuff.js index 9abcbc32a1..3ee124ab6b 100644 --- a/migrations/new_stuff.js +++ b/migrations/new_stuff.js @@ -1,41 +1,41 @@ -var migrationName = 'new_stuff.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done +/* let migrationName = 'new_stuff.js'; */ +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done /* * set the newStuff flag in all user accounts so they see a Bailey message */ -var monk = require('monk'); -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbUsers = monk(connectionString).get('users', { castIds: false }); +let monk = require('monk'); +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbUsers = monk(connectionString).get('users', { castIds: false }); -function processUsers(lastId) { +function processUsers (lastId) { // specify a query to limit the affected users (empty for all users): - var query = { - 'flags.newStuff': {$ne:true}, + let query = { + 'flags.newStuff': {$ne: true}, }; if (lastId) { query._id = { - $gt: lastId - } + $gt: lastId, + }; } dbUsers.find(query, { sort: {_id: 1}, limit: 250, - fields: [] // specify fields we are interested in to limit retrieved data (empty if we're not reading data): + fields: [], // specify fields we are interested in to limit retrieved data (empty if we're not reading data): }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); + .then(updateUsers) + .catch((err) => { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); } -var progressCount = 1000; -var count = 0; +let progressCount = 1000; +let count = 0; function updateUsers (users) { if (!users || users.length === 0) { @@ -44,37 +44,42 @@ function updateUsers (users) { return; } - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); + .then(() => { + processUsers(lastUser._id); + }); } function updateUser (user) { count++; - var set = {'flags.newStuff': true}; + let set = {'flags.newStuff': true}; - dbUsers.update({_id: user._id}, {$set:set}); + dbUsers.update({_id: user._id}, {$set: set}); - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); } -function displayData() { - console.warn('\n' + count + ' users processed\n'); +function displayData () { + console.warn(`\n${ count } users processed\n`); return exiting(0); } -function exiting(code, msg) { +function exiting (code, msg) { code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } + if (code && !msg) { + msg = 'ERROR!'; + } if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } + if (code) { + console.error(msg); + } else { + console.log(msg); + } } process.exit(code); } diff --git a/migrations/restock_armoire.js b/migrations/restock_armoire.js index a26e45b693..5046998a3b 100644 --- a/migrations/restock_armoire.js +++ b/migrations/restock_armoire.js @@ -1,41 +1,41 @@ -var migrationName = 'restock_armoire.js'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done +let migrationName = 'restock_armoire.js'; +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done /* * Remove flag stating that the Enchanted Armoire is empty, for when new equipment is added */ -var monk = require('monk'); -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbUsers = monk(connectionString).get('users', { castIds: false }); +let monk = require('monk'); +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbUsers = monk(connectionString).get('users', { castIds: false }); -function processUsers(lastId) { +function processUsers (lastId) { // specify a query to limit the affected users (empty for all users): - var query = { + let query = { 'flags.armoireEmpty': true, }; if (lastId) { query._id = { - $gt: lastId - } + $gt: lastId, + }; } dbUsers.find(query, { sort: {_id: 1}, limit: 250, - fields: [] // specify fields we are interested in to limit retrieved data (empty if we're not reading data): + fields: [], // specify fields we are interested in to limit retrieved data (empty if we're not reading data): }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); + .then(updateUsers) + .catch((err) => { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); } -var progressCount = 1000; -var count = 0; +let progressCount = 1000; +let count = 0; function updateUsers (users) { if (!users || users.length === 0) { @@ -44,37 +44,42 @@ function updateUsers (users) { return; } - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); + .then(() => { + processUsers(lastUser._id); + }); } function updateUser (user) { count++; - var set = {'migration': migrationName, 'flags.armoireEmpty': false}; + let set = {migration: migrationName, 'flags.armoireEmpty': false}; - dbUsers.update({_id: user._id}, {$set:set}); + dbUsers.update({_id: user._id}, {$set: set}); - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); } -function displayData() { - console.warn('\n' + count + ' users processed\n'); +function displayData () { + console.warn(`\n${ count } users processed\n`); return exiting(0); } -function exiting(code, msg) { +function exiting (code, msg) { code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } + if (code && !msg) { + msg = 'ERROR!'; + } if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } + if (code) { + console.error(msg); + } else { + console.log(msg); + } } process.exit(code); } diff --git a/migrations/restock_armoire_for_users_that_need_it.js b/migrations/restock_armoire_for_users_that_need_it.js index 1c335ffe78..5bf0422d04 100644 --- a/migrations/restock_armoire_for_users_that_need_it.js +++ b/migrations/restock_armoire_for_users_that_need_it.js @@ -1,65 +1,65 @@ -var migrationName = 'restock_armoire_for_users_that_need_it.js'; -var authorName = 'Alys (ALittleYellowSpider)'; // in case script author needs to know when their ... -var authorUuid = '3e595299-3d8a-4a10-bfe0-88f555e4aa0c'; //... own data is done +let migrationName = 'restock_armoire_for_users_that_need_it.js'; +let authorName = 'Alys (ALittleYellowSpider)'; // in case script author needs to know when their ... +let authorUuid = '3e595299-3d8a-4a10-bfe0-88f555e4aa0c'; // ... own data is done /* * Remove flag stating that the Enchanted Armoire is empty, - * for when new equipment has been added + * for when new equipment has been added * AND the normal restock_armoire.js script has failed. * This script finds all users that logged in recently, checks if they * do NOT own all Armoire items, and only then does it mark the Armoire * as not empty. - * + * ********************************************************************* * IMPORTANT: * You must update the list of Armoire items that this list checks for. * Scroll down. You'll see it. ********************************************************************* - * + * */ -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var monk = require('monk'); -var dbUsers = monk(connectionString).get('users', { castIds: false }); +let monk = require('monk'); +let dbUsers = monk(connectionString).get('users', { castIds: false }); -function processUsers(lastId) { +function processUsers (lastId) { // specify a query to limit the affected users (empty for all users): - var query = { - 'auth.timestamps.loggedin':{$gt:new Date('2016-01-04')} + let query = { + 'auth.timestamps.loggedin': {$gt: new Date('2016-01-04')}, // '_id': authorUuid // FOR TESTING }; // specify a query to limit the affected users (empty for all users): - var fields = { - 'flags.armoireEmpty':1, - 'items.gear.owned':1 - }; + /* let fields = { + 'flags.armoireEmpty': 1, + 'items.gear.owned': 1, + };*/ if (lastId) { query._id = { - $gt: lastId - } + $gt: lastId, + }; } dbUsers.find(query, { sort: {_id: 1}, limit: 250, fields: { - 'flags.armoireEmpty':1, - 'items.gear.owned':1 - } // specify fields we are interested in to limit retrieved data (empty if we're not reading data): + 'flags.armoireEmpty': 1, + 'items.gear.owned': 1, + }, // specify fields we are interested in to limit retrieved data (empty if we're not reading data): }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); + .then(updateUsers) + .catch((err) => { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); } -var progressCount = 1000; -var count = 0; +let progressCount = 1000; +let count = 0; function updateUsers (users) { if (!users || users.length === 0) { @@ -68,19 +68,19 @@ function updateUsers (users) { return; } - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); + .then(() => { + processUsers(lastUser._id); + }); } function updateUser (user) { count++; - var set = {'migration':migrationName, 'flags.armoireEmpty':false}; + let set = {migration: migrationName, 'flags.armoireEmpty': false}; if (user.flags.armoireEmpty) { @@ -98,21 +98,26 @@ function updateUser (user) { // console.log("DON'T CHANGE: " + user._id); // FOR TESTING } - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); } -function displayData() { - console.warn('\n' + count + ' users processed\n'); +function displayData () { + console.warn(`\n${ count } users processed\n`); return exiting(0); } -function exiting(code, msg) { +function exiting (code, msg) { code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } + if (code && !msg) { + msg = 'ERROR!'; + } if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } + if (code) { + console.error(msg); + } else { + console.log(msg); + } } process.exit(code); } diff --git a/migrations/restore-profile-data.js b/migrations/restore-profile-data.js index 78884738e1..750a991efb 100644 --- a/migrations/restore-profile-data.js +++ b/migrations/restore-profile-data.js @@ -1,23 +1,22 @@ -var migrationName = 'restore_profile_data.js'; -var authorName = 'ThehollidayInn'; // in case script author needs to know when their ... -var authorUuid = ''; //... own data is done +/* let migrationName = 'restore_profile_data.js'; */ +let authorName = 'ThehollidayInn'; // in case script author needs to know when their ... +let authorUuid = ''; // ... own data is done /* * Check if users have empty profile data in new database and update it with old database info */ -var monk = require('monk'); -var connectionString = ''; // FOR TEST DATABASE -var dbUsers = monk(connectionString).get('users', { castIds: false }); +let monk = require('monk'); +let connectionString = ''; // FOR TEST DATABASE +let dbUsers = monk(connectionString).get('users', { castIds: false }); -var monk2 = require('monk'); -var oldDbConnectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var olDbUsers = monk2(oldDbConnectionString).get('users', { castIds: false }); +let monk2 = require('monk'); +let oldDbConnectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let olDbUsers = monk2(oldDbConnectionString).get('users', { castIds: false }); -function processUsers(lastId) -{ +function processUsers (lastId) { // specify a query to limit the affected users (empty for all users): - var query = { + let query = { // 'profile.name': 'profile name not found', 'profile.blurb': null, // 'auth.timestamps.loggedin': {$gt: new Date('11/30/2016')}, @@ -25,24 +24,24 @@ function processUsers(lastId) if (lastId) { query._id = { - $gt: lastId - } + $gt: lastId, + }; } dbUsers.find(query, { sort: {_id: 1}, limit: 250, - fields: ['_id', 'profile', 'auth.timestamps.loggedin'] // specify fields we are interested in to limit retrieved data (empty if we're not reading data): + fields: ['_id', 'profile', 'auth.timestamps.loggedin'], // specify fields we are interested in to limit retrieved data (empty if we're not reading data): }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); + .then(updateUsers) + .catch((err) => { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); } -var progressCount = 1000; -var count = 0; +let progressCount = 1000; +let count = 0; function updateUsers (users) { if (!users || users.length === 0) { @@ -51,13 +50,13 @@ function updateUsers (users) { return; } - var userPaymentPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; + let userPaymentPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; return Promise.all(userPaymentPromises) - .then(function () { - processUsers(lastUser._id); - }); + .then(() => { + return processUsers(lastUser._id); + }); } function updateUser (user) { @@ -68,11 +67,11 @@ function updateUser (user) { .then((oldUserData) => { if (!oldUserData) return; // specify user data to change: - var set = {}; + let set = {}; if (oldUserData.profile.name === 'profile name not found') return; - var userNeedsProfileName = !user.profile.name || user.profile.name === 'profile name not found'; + let userNeedsProfileName = !user.profile.name || user.profile.name === 'profile name not found'; if (userNeedsProfileName && oldUserData.profile.name) { set['profile.name'] = oldUserData.profile.name; } @@ -86,30 +85,35 @@ function updateUser (user) { } if (Object.keys(set).length !== 0 && set.constructor === Object) { - console.log(set) - return dbUsers.update({_id: user._id}, {$set:set}); + console.log(set); + return dbUsers.update({_id: user._id}, {$set: set}); } }); } - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); } -function displayData() { - console.warn('\n' + count + ' users processed\n'); +function displayData () { + console.warn(`\n${ count } users processed\n`); return exiting(0); } -function exiting(code, msg) { +function exiting (code, msg) { code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } + if (code && !msg) { + msg = 'ERROR!'; + } if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } + if (code) { + console.error(msg); + } else { + console.log(msg); + } } process.exit(code); } -processUsers() +processUsers(); diff --git a/migrations/s3-upload.js b/migrations/s3-upload.js index dfb07f1eca..7538e83f6d 100644 --- a/migrations/s3-upload.js +++ b/migrations/s3-upload.js @@ -4,7 +4,7 @@ let last = require('lodash/last'); let AWS = require('aws-sdk'); let config = require('../config'); -const S3_DIRECTORY = 'mobileApp/images'; //config.S3.SPRITES_DIRECTORY; +const S3_DIRECTORY = 'mobileApp/images'; // config.S3.SPRITES_DIRECTORY; AWS.config.update({ accessKeyId: config.S3.accessKeyId, @@ -53,26 +53,26 @@ function getFileFromUrl (url) { let commit = '78f94e365c72cc58f66857d5941105638db7d35c'; commit = 'df0dbaba636c9ce424cc7040f7bd7fc1aa311015'; -let gihuburl = `https://api.github.com/repos/HabitRPG/habitica/commits/${commit}` +let gihuburl = `https://api.github.com/repos/HabitRPG/habitica/commits/${commit}`; let currentIndex = 0; -function uploadToS3(start, end, filesUrls) { +function uploadToS3 (start, end, filesUrls) { let urls = filesUrls.slice(start, end); if (urls.length === 0) { - console.log("done"); + console.log('done'); return; } let promises = urls.map(fullUrl => { return getFileFromUrl(fullUrl) - .then((buffer) => { - return uploadFile(buffer, getFileName(fullUrl)); - }); + .then((buffer) => { + return uploadFile(buffer, getFileName(fullUrl)); + }); }); - console.log(promises.length) + console.log(promises.length); return Bluebird.all(promises) .then(() => { @@ -92,7 +92,7 @@ request.get(gihuburl) let filesUrls = ['']; filesUrls = files.map(file => { return file.raw_url; - }) + }); uploadToS3(currentIndex, currentIndex + 50, filesUrls); }); diff --git a/migrations/takeThis.js b/migrations/takeThis.js index 17336e394d..13e4283b9b 100644 --- a/migrations/takeThis.js +++ b/migrations/takeThis.js @@ -1,26 +1,26 @@ -var migrationName = '20180102_takeThis.js'; // Update per month -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done +let migrationName = '20180102_takeThis.js'; // Update per month +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done /* * Award Take This ladder items to participants in this month's challenge */ -var monk = require('monk'); -var connectionString = 'mongodb://sabrecat:z8e8jyRA8CTofMQ@ds013393-a0.mlab.com:13393/habitica?auto_reconnect=true'; -var dbUsers = monk(connectionString).get('users', { castIds: false }); +let monk = require('monk'); +let connectionString = 'mongodb://sabrecat:z8e8jyRA8CTofMQ@ds013393-a0.mlab.com:13393/habitica?auto_reconnect=true'; +let dbUsers = monk(connectionString).get('users', { castIds: false }); -function processUsers(lastId) { +function processUsers (lastId) { // specify a query to limit the affected users (empty for all users): - var query = { - 'migration':{$ne:migrationName}, - 'challenges':{$in:['5f70ce5b-2d82-4114-8e44-ca65615aae62']} // Update per month + let query = { + migration: {$ne: migrationName}, + challenges: {$in: ['5f70ce5b-2d82-4114-8e44-ca65615aae62']}, // Update per month }; if (lastId) { query._id = { - $gt: lastId - } + $gt: lastId, + }; } dbUsers.find(query, { @@ -28,17 +28,17 @@ function processUsers(lastId) { limit: 250, fields: [ 'items.gear.owned', - ] // specify fields we are interested in to limit retrieved data (empty if we're not reading data): + ], // specify fields we are interested in to limit retrieved data (empty if we're not reading data): }) - .then(updateUsers) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); + .then(updateUsers) + .catch((err) => { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); } -var progressCount = 1000; -var count = 0; +let progressCount = 1000; +let count = 0; function updateUsers (users) { if (!users || users.length === 0) { @@ -47,40 +47,41 @@ function updateUsers (users) { return; } - var userPromises = users.map(updateUser); - var lastUser = users[users.length - 1]; + let userPromises = users.map(updateUser); + let lastUser = users[users.length - 1]; return Promise.all(userPromises) - .then(function () { - processUsers(lastUser._id); - }); + .then(() => { + processUsers(lastUser._id); + }); } function updateUser (user) { count++; - var set = {}; + let set = {}; + let push; if (typeof user.items.gear.owned.back_special_takeThis !== 'undefined') { - set = {'migration':migrationName}; + set = {migration: migrationName}; } else if (typeof user.items.gear.owned.body_special_takeThis !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.back_special_takeThis':false}; - var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.back_special_takeThis', '_id': monk.id()}}; + set = {migration: migrationName, 'items.gear.owned.back_special_takeThis': false}; + push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.back_special_takeThis', _id: monk.id()}}; } else if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.body_special_takeThis':false}; - var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.body_special_takeThis', '_id': monk.id()}}; + set = {migration: migrationName, 'items.gear.owned.body_special_takeThis': false}; + push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.body_special_takeThis', _id: monk.id()}}; } else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false}; - var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_takeThis', '_id': monk.id()}}; + set = {migration: migrationName, 'items.gear.owned.head_special_takeThis': false}; + push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_takeThis', _id: monk.id()}}; } else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false}; - var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_takeThis', '_id': monk.id()}}; + set = {migration: migrationName, 'items.gear.owned.armor_special_takeThis': false}; + push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_takeThis', _id: monk.id()}}; } else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') { - set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false}; - var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.weapon_special_takeThis', '_id': monk.id()}}; + set = {migration: migrationName, 'items.gear.owned.weapon_special_takeThis': false}; + push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.weapon_special_takeThis', _id: monk.id()}}; } else { - set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false}; - var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.shield_special_takeThis', '_id': monk.id()}}; + set = {migration: migrationName, 'items.gear.owned.shield_special_takeThis': false}; + push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.shield_special_takeThis', _id: monk.id()}}; } if (push) { @@ -89,21 +90,26 @@ function updateUser (user) { dbUsers.update({_id: user._id}, {$set: set}); } - if (count % progressCount == 0) console.warn(count + ' ' + user._id); - if (user._id == authorUuid) console.warn(authorName + ' processed'); + if (count % progressCount === 0) console.warn(`${count } ${ user._id}`); + if (user._id === authorUuid) console.warn(`${authorName } processed`); } -function displayData() { - console.warn('\n' + count + ' users processed\n'); +function displayData () { + console.warn(`\n${ count } users processed\n`); return exiting(0); } -function exiting(code, msg) { +function exiting (code, msg) { code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } + if (code && !msg) { + msg = 'ERROR!'; + } if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } + if (code) { + console.error(msg); + } else { + console.log(msg); + } } process.exit(code); } diff --git a/migrations/tasks/tasks-set-everyX.js b/migrations/tasks/tasks-set-everyX.js index c0a4acc7fb..f3bd5408d6 100644 --- a/migrations/tasks/tasks-set-everyX.js +++ b/migrations/tasks/tasks-set-everyX.js @@ -1,32 +1,32 @@ -var migrationName = 'tasks-set-everyX'; -var authorName = 'Sabe'; // in case script author needs to know when their ... -var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done +/* let migrationName = 'tasks-set-everyX'; */ +let authorName = 'Sabe'; // in case script author needs to know when their ... +let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done /* * Iterates over all tasks and sets invalid everyX values (less than 0 or more than 9999 or not an int) field to 0 */ -var monk = require('monk'); -var connectionString = 'mongodb://sabrecat:z8e8jyRA8CTofMQ@ds013393-a0.mlab.com:13393/habitica?auto_reconnect=true'; -var dbTasks = monk(connectionString).get('tasks', { castIds: false }); +let monk = require('monk'); +let connectionString = 'mongodb://sabrecat:z8e8jyRA8CTofMQ@ds013393-a0.mlab.com:13393/habitica?auto_reconnect=true'; +let dbTasks = monk(connectionString).get('tasks', { castIds: false }); -function processTasks(lastId) { +function processTasks (lastId) { // specify a query to limit the affected tasks (empty for all tasks): - var query = { - type: "daily", + let query = { + type: 'daily', everyX: { $not: { $gte: 0, $lte: 9999, - $type: "int", - } + $type: 'int', + }, }, }; if (lastId) { query._id = { - $gt: lastId - } + $gt: lastId, + }; } dbTasks.find(query, { @@ -34,15 +34,15 @@ function processTasks(lastId) { limit: 250, fields: [], }) - .then(updateTasks) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); + .then(updateTasks) + .catch((err) => { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); } -var progressCount = 1000; -var count = 0; +let progressCount = 1000; +let count = 0; function updateTasks (tasks) { if (!tasks || tasks.length === 0) { @@ -51,36 +51,41 @@ function updateTasks (tasks) { return; } - var taskPromises = tasks.map(updatetask); - var lasttask = tasks[tasks.length - 1]; + let taskPromises = tasks.map(updatetask); + let lasttask = tasks[tasks.length - 1]; return Promise.all(taskPromises) - .then(function () { - processTasks(lasttask._id); - }); + .then(() => { + return processTasks(lasttask._id); + }); } function updatetask (task) { count++; - var set = {'everyX': 0}; + let set = {everyX: 0}; - dbTasks.update({_id: task._id}, {$set:set}); + dbTasks.update({_id: task._id}, {$set: set}); - if (count % progressCount == 0) console.warn(count + ' ' + task._id); - if (task._id == authorUuid) console.warn(authorName + ' processed'); + if (count % progressCount === 0) console.warn(`${count } ${ task._id}`); + if (task._id === authorUuid) console.warn(`${authorName } processed`); } -function displayData() { - console.warn('\n' + count + ' tasks processed\n'); +function displayData () { + console.warn(`\n${ count } tasks processed\n`); return exiting(0); } -function exiting(code, msg) { +function exiting (code, msg) { code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } + if (code && !msg) { + msg = 'ERROR!'; + } if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } + if (code) { + console.error(msg); + } else { + console.log(msg); + } } process.exit(code); } diff --git a/migrations/tasks/tasks-set-yesterdailies.js b/migrations/tasks/tasks-set-yesterdailies.js index 8ce87b1046..253d871612 100644 --- a/migrations/tasks/tasks-set-yesterdailies.js +++ b/migrations/tasks/tasks-set-yesterdailies.js @@ -1,83 +1,88 @@ -var migrationName = 'tasks-set-yesterdaily'; -var authorName = 'TheHollidayInn'; // in case script author needs to know when their ... -var authorUuid = ''; //... own data is done - -/* - * Iterates over all tasks and sets the yseterDaily field to True - */ - -import monk from 'monk'; - -var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE -var dbTasks = monk(connectionString).get('tasks', { castIds: false }); - -function processTasks(lastId) { - // specify a query to limit the affected tasks (empty for all tasks): - var query = { - yesterDaily: false, - }; - - if (lastId) { - query._id = { - $gt: lastId - } - } - - dbTasks.find(query, { - sort: {_id: 1}, - limit: 250, - fields: [ // specify fields we are interested in to limit retrieved data (empty if we're not reading data): - ], - }) - .then(updateTasks) - .catch(function (err) { - console.log(err); - return exiting(1, 'ERROR! ' + err); - }); -} - -var progressCount = 1000; -var count = 0; - -function updateTasks (tasks) { - if (!tasks || tasks.length === 0) { - console.warn('All appropriate tasks found and modified.'); - displayData(); - return; - } - - var taskPromises = tasks.map(updatetask); - var lasttask = tasks[tasks.length - 1]; - - return Promise.all(taskPromises) - .then(function () { - processtasks(lasttask._id); - }); -} - -function updatetask (task) { - count++; - var set = {'yesterDaily': true}; - - dbTasks.update({_id: task._id}, {$set:set}); - - if (count % progressCount == 0) console.warn(count + ' ' + task._id); - if (task._id == authorUuid) console.warn(authorName + ' processed'); -} - -function displayData() { - console.warn('\n' + count + ' tasks processed\n'); - return exiting(0); -} - -function exiting(code, msg) { - code = code || 0; // 0 = success - if (code && !msg) { msg = 'ERROR!'; } - if (msg) { - if (code) { console.error(msg); } - else { console.log( msg); } - } - process.exit(code); -} - -module.exports = processtasks; +/* let migrationName = 'tasks-set-yesterdaily'; */ +let authorName = 'TheHollidayInn'; // in case script author needs to know when their ... +let authorUuid = ''; // ... own data is done + +/* + * Iterates over all tasks and sets the yseterDaily field to True + */ + +import monk from 'monk'; + +let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE +let dbTasks = monk(connectionString).get('tasks', { castIds: false }); + +let progressCount = 1000; +let count = 0; + +function exiting (code, msg) { + code = code || 0; // 0 = success + if (code && !msg) { + msg = 'ERROR!'; + } + if (msg) { + if (code) { + console.error(msg); + } else { + console.log(msg); + } + } + process.exit(code); +} + +function displayData () { + console.warn(`\n${ count } tasks processed\n`); + return exiting(0); +} + +function updatetask (task) { + count++; + let set = {yesterDaily: true}; + + dbTasks.update({_id: task._id}, {$set: set}); + + if (count % progressCount === 0) console.warn(`${count } ${ task._id}`); + if (task._id === authorUuid) console.warn(`${authorName } processed`); +} + +function updateTasks (tasks) { + if (!tasks || tasks.length === 0) { + console.warn('All appropriate tasks found and modified.'); + displayData(); + return; + } + + let taskPromises = tasks.map(updatetask); + let lasttask = tasks[tasks.length - 1]; + + return Promise.all(taskPromises) + .then(() => { + return processTasks(lasttask._id); // eslint-disable-line no-use-before-define + }); +} + +function processTasks (lastId) { + // specify a query to limit the affected tasks (empty for all tasks): + let query = { + yesterDaily: false, + }; + + if (lastId) { + query._id = { + $gt: lastId, + }; + } + + dbTasks.find(query, { + sort: {_id: 1}, + limit: 250, + fields: [ // specify fields we are interested in to limit retrieved data (empty if we're not reading data): + ], + }) + .then(updateTasks) + .catch((err) => { + console.log(err); + return exiting(1, `ERROR! ${ err}`); + }); +} + +module.exports = processTasks; diff --git a/migrations/users/account-transfer.js b/migrations/users/account-transfer.js index 7d57dac08b..42a9602aa1 100644 --- a/migrations/users/account-transfer.js +++ b/migrations/users/account-transfer.js @@ -1,6 +1,8 @@ -var migrationName = 'AccountTransfer'; -var authorName = 'TheHollidayInn'; // in case script author needs to know when their ... -var authorUuid = ''; //... own data is done +/* +let migrationName = 'AccountTransfer'; +let authorName = 'TheHollidayInn'; // in case script author needs to know when their ... +let authorUuid = ''; // ... own data is done +*/ /* * This migraition will copy user data from prod to test @@ -10,10 +12,6 @@ const monk = require('monk'); const connectionString = ''; const Users = monk(connectionString).get('users', { castIds: false }); -import uniq from 'lodash/uniq'; -import Bluebird from 'bluebird'; - - module.exports = async function accountTransfer () { const fromAccountId = ''; const toAccountId = ''; @@ -32,7 +30,7 @@ module.exports = async function accountTransfer () { 'purchased.background': newBackgrounds, }, }) - .then((result) => { - console.log(result); - }); + .then((result) => { + console.log(result); + }); }; diff --git a/migrations/users/achievement-restore.js b/migrations/users/achievement-restore.js index bd7e91a327..e87300bc18 100644 --- a/migrations/users/achievement-restore.js +++ b/migrations/users/achievement-restore.js @@ -1,17 +1,17 @@ +/* const migrationName = 'AchievementRestore'; const authorName = 'TheHollidayInn'; // in case script author needs to know when their ... -const authorUuid = ''; //... own data is done +const authorUuid = ''; // ... own data is done +*/ /* * This migraition will copy user data from prod to test */ -import Bluebird from 'bluebird'; const monk = require('monk'); const connectionString = 'mongodb://localhost/new-habit'; const Users = monk(connectionString).get('users', { castIds: false }); -const monkOld = require('monk'); const oldConnectionSting = 'mongodb://localhost/old-habit'; const UsersOld = monk(oldConnectionSting).get('users', { castIds: false }); @@ -52,21 +52,19 @@ function getAchievementUpdate (newUser, oldUser) { achievementsUpdate.rebirthLevel = oldAchievements.rebirthLevel; } - //All others + // All others const indexsToIgnore = ['ultimateGearSets', 'challenges', 'quests', 'rebirthLevel']; for (let index in oldAchievements) { - if (indexsToIgnore.indexOf(index) !== -1) continue; + if (indexsToIgnore.indexOf(index) !== -1) continue; // eslint-disable-line no-continue if (!achievementsUpdate[index]) { achievementsUpdate[index] = oldAchievements[index]; - continue; + continue; // eslint-disable-line no-continue } if (Number.isInteger(oldAchievements[index])) { achievementsUpdate[index] += oldAchievements[index]; - } else { - if (oldAchievements[index] === true) achievementsUpdate[index] = true; - } + } else if (oldAchievements[index] === true) achievementsUpdate[index] = true; } return achievementsUpdate; @@ -77,6 +75,7 @@ module.exports = async function achievementRestore () { ]; for (let index in userIds) { + /* eslint-disable no-await-in-loop */ const userId = userIds[index]; const oldUser = await UsersOld.findOne({_id: userId}, 'achievements'); const newUser = await Users.findOne({_id: userId}, 'achievements'); @@ -85,9 +84,10 @@ module.exports = async function achievementRestore () { {_id: userId}, { $set: { - 'achievements': achievementUpdate, + achievements: achievementUpdate, }, }); console.log(`Updated ${userId}`); + /* eslint-enable no-await-in-loop */ } }; diff --git a/migrations/users/users-to-test.js b/migrations/users/users-to-test.js index 64b6c38e07..cf9f4cc92f 100644 --- a/migrations/users/users-to-test.js +++ b/migrations/users/users-to-test.js @@ -1,32 +1,35 @@ -var migrationName = 'UserFromProdToTest'; -var authorName = 'TheHollidayInn'; // in case script author needs to know when their ... -var authorUuid = ''; //... own data is done +/* +let migrationName = 'UserFromProdToTest'; +let authorName = 'TheHollidayInn'; // in case script author needs to know when their ... +let authorUuid = ''; // ... own data is done +*/ /* * This migraition will copy user data from prod to test */ -var monk = require('monk'); -var testConnectionSting = ''; // FOR TEST DATABASE -var usersTest = monk(testConnectionSting).get('users', { castIds: false }); -var groupsTest = monk(testConnectionSting).get('groups', { castIds: false }); -var challengesTest = monk(testConnectionSting).get('challenges', { castIds: false }); -var tasksTest = monk(testConnectionSting).get('tasks', { castIds: false }); +let monk = require('monk'); +let testConnectionSting = ''; // FOR TEST DATABASE +let usersTest = monk(testConnectionSting).get('users', { castIds: false }); +let groupsTest = monk(testConnectionSting).get('groups', { castIds: false }); +let challengesTest = monk(testConnectionSting).get('challenges', { castIds: false }); +let tasksTest = monk(testConnectionSting).get('tasks', { castIds: false }); -var monk2 = require('monk'); -var liveConnectString = ''; // FOR TEST DATABASE -var userLive = monk2(liveConnectString).get('users', { castIds: false }); -var groupsLive = monk2(liveConnectString).get('groups', { castIds: false }); -var challengesLive = monk2(liveConnectString).get('challenges', { castIds: false }); -var tasksLive = monk2(liveConnectString).get('tasks', { castIds: false }); +let monk2 = require('monk'); +let liveConnectString = ''; // FOR TEST DATABASE +let userLive = monk2(liveConnectString).get('users', { castIds: false }); +let groupsLive = monk2(liveConnectString).get('groups', { castIds: false }); +let challengesLive = monk2(liveConnectString).get('challenges', { castIds: false }); +let tasksLive = monk2(liveConnectString).get('tasks', { castIds: false }); import uniq from 'lodash/uniq'; -import Bluebird from 'bluebird'; // Variabls for updating +/* let userIds = [ '206039c6-24e4-4b9f-8a31-61cbb9aa3f66', ]; +*/ let groupIds = []; let challengeIds = []; @@ -34,10 +37,10 @@ let tasksIds = []; async function processUsers () { let userPromises = []; - //{_id: {$in: userIds}} + // {_id: {$in: userIds}} return userLive.find({guilds: 'b0764d64-8276-45a1-afa5-5ca9a5c64ca0'}) - .each((user, {close, pause, resume}) => { + .each((user) => { if (user.guilds.length > 0) groupIds = groupIds.concat(user.guilds); if (user.party._id) groupIds.push(user.party._id); if (user.challenges.length > 0) challengeIds = challengeIds.concat(user.challenges); @@ -46,13 +49,13 @@ async function processUsers () { if (user.tasksOrder.dailys.length > 0) tasksIds = tasksIds.concat(user.tasksOrder.dailys); if (user.tasksOrder.habits.length > 0) tasksIds = tasksIds.concat(user.tasksOrder.habits); - let userPromise = usersTest.update({'_id': user._id}, user, {upsert:true}); + let userPromise = usersTest.update({_id: user._id}, user, {upsert: true}); userPromises.push(userPromise); }).then(() => { - return Bluebird.all(userPromises); + return Promise.all(userPromises); }) .then(() => { - console.log("Done User"); + console.log('Done User'); }); } @@ -60,14 +63,14 @@ function processGroups () { let promises = []; let groupsToQuery = uniq(groupIds); return groupsLive.find({_id: {$in: groupsToQuery}}) - .each((group, {close, pause, resume}) => { - let promise = groupsTest.update({_id: group._id}, group, {upsert:true}); + .each((group) => { + let promise = groupsTest.update({_id: group._id}, group, {upsert: true}); promises.push(promise); }).then(() => { - return Bluebird.all(promises); + return Promise.all(promises); }) .then(() => { - console.log("Done Group"); + console.log('Done Group'); }); } @@ -75,14 +78,14 @@ function processChallenges () { let promises = []; let challengesToQuery = uniq(challengeIds); return challengesLive.find({_id: {$in: challengesToQuery}}) - .each((challenge, {close, pause, resume}) => { - let promise = challengesTest.update({_id: challenge._id}, challenge, {upsert:true}); + .each((challenge) => { + let promise = challengesTest.update({_id: challenge._id}, challenge, {upsert: true}); promises.push(promise); }).then(() => { - return Bluebird.all(promises); + return Promise.all(promises); }) .then(() => { - console.log("Done Challenge"); + console.log('Done Challenge'); }); } @@ -90,14 +93,14 @@ function processTasks () { let promises = []; let tasksToQuery = uniq(tasksIds); return tasksLive.find({_id: {$in: tasksToQuery}}) - .each((task, {close, pause, resume}) => { - let promise = tasksTest.update({_id: task._id}, task, {upsert:true}); + .each((task) => { + let promise = tasksTest.update({_id: task._id}, task, {upsert: true}); promises.push(promise); }).then(() => { - return Bluebird.all(promises); + return Promise.all(promises); }) .then(() => { - console.log("Done Tasks"); + console.log('Done Tasks'); }); } diff --git a/migrations/utils/connect.js b/migrations/utils/connect.js index 492b473708..1b20d523ca 100644 --- a/migrations/utils/connect.js +++ b/migrations/utils/connect.js @@ -3,7 +3,7 @@ const MongoClient = require('mongodb').MongoClient; const logger = require('./logger'); -let db; +let dbConnection; function connectToDb (dbUri) { return new Promise((resolve, reject) => { @@ -13,7 +13,7 @@ function connectToDb (dbUri) { return reject(err); } - db = database; + dbConnection = database; logger.success(`Connected to ${dbUri}`); @@ -23,7 +23,7 @@ function connectToDb (dbUri) { } function closeDb () { - if (db) db.close(); + if (dbConnection) dbConnection.close(); logger.success('Closed connection to the database'); return Promise.resolve(); @@ -32,4 +32,4 @@ function closeDb () { module.exports = { connectToDb, closeDb, -} +}; diff --git a/migrations/utils/logger.js b/migrations/utils/logger.js index a2ddc96fe2..cf6d643e76 100644 --- a/migrations/utils/logger.js +++ b/migrations/utils/logger.js @@ -2,6 +2,13 @@ const chalk = require('chalk'); +function loggerGenerator (type, color) { + return function logger () { + let args = Array.from(arguments).map(arg => chalk[color](arg)); + console[type].apply(null, args); + }; +} + const logger = { info: loggerGenerator('info', 'cyan'), success: loggerGenerator('info', 'green'), @@ -10,11 +17,4 @@ const logger = { warn: loggerGenerator('warn', 'yellow'), }; -function loggerGenerator (type, color) { - return function () { - let args = Array.from(arguments).map(arg => chalk[color](arg)); - console[type].apply(null, args); - } -} - module.exports = logger; diff --git a/migrations/utils/timer.js b/migrations/utils/timer.js index 37a75830b1..cacdfc1b8b 100644 --- a/migrations/utils/timer.js +++ b/migrations/utils/timer.js @@ -13,7 +13,7 @@ class Timer { if (!options.disableAutoStart) this.start(); } start () { - this._internalTimer = setInterval(() =>{ + this._internalTimer = setInterval(() => { this.count++; let shouldWarn = this._minutesWarningThreshold < this.count; diff --git a/package-lock.json b/package-lock.json index f953f8092e..7ef630a64f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,104 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.36.tgz", + "integrity": "sha512-sW77BFwJ48YvQp3Gzz5xtAUiXuYOL2aMJKDwiaY3OcvdqBFurtYfOpSa4QrNyDxmOGRFSYzUpabU2m9QrlWE7w==", + "requires": { + "chalk": "2.3.0", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, + "@babel/helper-function-name": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.36.tgz", + "integrity": "sha512-/SGPOyifPf20iTrMN+WdlY2MbKa7/o4j7B/4IAsdOusASp2icT+Wcdjf4tjJHaXNX8Pe9bpgVxLNxhRvcf8E5w==", + "requires": { + "@babel/helper-get-function-arity": "7.0.0-beta.36", + "@babel/template": "7.0.0-beta.36", + "@babel/types": "7.0.0-beta.36" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.36.tgz", + "integrity": "sha512-vPPcx2vsSoDbcyWr9S3nd0FM3B4hEXnt0p1oKpwa08GwK0fSRxa98MyaRGf8suk8frdQlG1P3mDrz5p/Rr3pbA==", + "requires": { + "@babel/types": "7.0.0-beta.36" + } + }, + "@babel/template": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.36.tgz", + "integrity": "sha512-mUBi90WRyZ9iVvlWLEdeo8gn/tROyJdjKNC4W5xJTSZL+9MS89rTJSqiaJKXIkxk/YRDL/g/8snrG/O0xl33uA==", + "requires": { + "@babel/code-frame": "7.0.0-beta.36", + "@babel/types": "7.0.0-beta.36", + "babylon": "7.0.0-beta.36", + "lodash": "4.17.5" + }, + "dependencies": { + "babylon": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.36.tgz", + "integrity": "sha512-rw4YdadGwajAMMRl6a5swhQ0JCOOFyaYCfJ0AsmNBD8uBD/r4J8mux7wBaqavvFKqUKQYWOzA1Speams4YDzsQ==" + } + } + }, + "@babel/traverse": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.36.tgz", + "integrity": "sha512-OTUb6iSKVR/98dGThRJ1BiyfwbuX10BVnkz89IpaerjTPRhDfMBfLsqmzxz5MiywUOW4M0Clta0o7rSxkfcuzw==", + "requires": { + "@babel/code-frame": "7.0.0-beta.36", + "@babel/helper-function-name": "7.0.0-beta.36", + "@babel/types": "7.0.0-beta.36", + "babylon": "7.0.0-beta.36", + "debug": "3.1.0", + "globals": "11.3.0", + "invariant": "2.2.2", + "lodash": "4.17.5" + }, + "dependencies": { + "babylon": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.36.tgz", + "integrity": "sha512-rw4YdadGwajAMMRl6a5swhQ0JCOOFyaYCfJ0AsmNBD8uBD/r4J8mux7wBaqavvFKqUKQYWOzA1Speams4YDzsQ==" + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "globals": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.3.0.tgz", + "integrity": "sha512-kkpcKNlmQan9Z5ZmgqKH/SMbSmjxQ7QjyNqfXVc8VJcoBV2UEg+sxQD15GQofGRh2hfpwUb70VC31DR7Rq5Hdw==" + } + } + }, + "@babel/types": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.36.tgz", + "integrity": "sha512-PyAORDO9um9tfnrddXgmWN9e6Sq9qxraQIt5ynqBOSXKA5qvK1kUr+Q3nSzKFdzorsiK+oqcUnAFvEoKxv9D+Q==", + "requires": { + "esutils": "2.0.2", + "lodash": "4.17.5", + "to-fast-properties": "2.0.0" + }, + "dependencies": { + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + } + } + }, "@slack/client": { "version": "3.16.0", "resolved": "https://registry.npmjs.org/@slack/client/-/client-3.16.0.tgz", @@ -917,14 +1015,23 @@ } }, "babel-eslint": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-7.2.3.tgz", - "integrity": "sha1-sv4tgBJkcPXBlELcdXJTqJdxCCc=", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-8.2.1.tgz", + "integrity": "sha512-RzdVOyWKQRUnLXhwLk+eKb4oyW+BykZSkpYwFhM4tnfzAG5OWfvG0w/uyzMp5XKEU0jN82+JefHr39bG2+KhRQ==", "requires": { - "babel-code-frame": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0" + "@babel/code-frame": "7.0.0-beta.36", + "@babel/traverse": "7.0.0-beta.36", + "@babel/types": "7.0.0-beta.36", + "babylon": "7.0.0-beta.36", + "eslint-scope": "3.7.1", + "eslint-visitor-keys": "1.0.0" + }, + "dependencies": { + "babylon": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.36.tgz", + "integrity": "sha512-rw4YdadGwajAMMRl6a5swhQ0JCOOFyaYCfJ0AsmNBD8uBD/r4J8mux7wBaqavvFKqUKQYWOzA1Speams4YDzsQ==" + } } }, "babel-generator": { @@ -2414,21 +2521,38 @@ } }, "chai": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", - "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", + "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", "dev": true, "requires": { "assertion-error": "1.1.0", - "deep-eql": "0.1.3", - "type-detect": "1.0.0" + "check-error": "1.0.2", + "deep-eql": "3.0.1", + "get-func-name": "2.0.0", + "pathval": "1.1.0", + "type-detect": "4.0.8" + }, + "dependencies": { + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + } } }, "chai-as-promised": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-5.3.0.tgz", - "integrity": "sha1-CdekApCKpw39vq1T5YU/x50+8hw=", - "dev": true + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "requires": { + "check-error": "1.0.2" + } }, "chai-dom": { "version": "1.7.0", @@ -2470,7 +2594,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", - "dev": true, "requires": { "ansi-styles": "3.2.0", "escape-string-regexp": "1.0.5", @@ -2481,7 +2604,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, "requires": { "color-convert": "1.9.1" } @@ -2489,14 +2611,12 @@ "has-flag": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" }, "supports-color": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, "requires": { "has-flag": "2.0.0" } @@ -2516,6 +2636,12 @@ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=" }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, "check-types": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/check-types/-/check-types-7.3.0.tgz", @@ -2862,6 +2988,12 @@ "q": "1.5.1" } }, + "coalescy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/coalescy/-/coalescy-1.0.0.tgz", + "integrity": "sha1-SwZYRrg2NhrabEtKSr9LwcrDG/E=", + "dev": true + }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -5116,38 +5248,11 @@ "es6-symbol": "3.1.1" } }, - "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.38", - "es6-iterator": "2.0.3", - "es6-set": "0.1.5", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" - } - }, "es6-promise": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" }, - "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.38", - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" - } - }, "es6-symbol": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", @@ -5210,185 +5315,126 @@ } } }, - "escope": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", - "dev": true, - "requires": { - "es6-map": "0.1.5", - "es6-weak-map": "2.0.2", - "esrecurse": "4.2.0", - "estraverse": "4.2.0" - }, - "dependencies": { - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - } - } - }, "eslint": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", - "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.17.0.tgz", + "integrity": "sha512-AyxBUCANU/o/xC0ijGMKavo5Ls3oK6xykiOITlMdjFjrKOsqLrA7Nf5cnrDgcKrHzBirclAZt63XO7YZlVUPwA==", "dev": true, "requires": { + "ajv": "5.5.2", "babel-code-frame": "6.26.0", - "chalk": "1.1.3", + "chalk": "2.3.0", "concat-stream": "1.6.0", - "debug": "2.6.9", + "cross-spawn": "5.1.0", + "debug": "3.1.0", "doctrine": "2.1.0", - "escope": "3.6.0", + "eslint-scope": "3.7.1", + "eslint-visitor-keys": "1.0.0", "espree": "3.5.3", "esquery": "1.0.0", - "estraverse": "4.2.0", "esutils": "2.0.2", "file-entry-cache": "2.0.0", + "functional-red-black-tree": "1.0.1", "glob": "7.1.2", - "globals": "9.18.0", + "globals": "11.3.0", "ignore": "3.3.7", "imurmurhash": "0.1.4", - "inquirer": "0.12.0", - "is-my-json-valid": "2.17.1", + "inquirer": "3.0.6", "is-resolvable": "1.1.0", - "js-yaml": "3.7.0", - "json-stable-stringify": "1.0.1", + "js-yaml": "3.10.0", + "json-stable-stringify-without-jsonify": "1.0.1", "levn": "0.3.0", "lodash": "4.17.5", + "minimatch": "3.0.4", "mkdirp": "0.5.1", "natural-compare": "1.4.0", "optionator": "0.8.2", "path-is-inside": "1.0.2", - "pluralize": "1.2.1", - "progress": "1.1.8", + "pluralize": "7.0.0", + "progress": "2.0.0", "require-uncached": "1.0.3", - "shelljs": "0.7.8", - "strip-bom": "3.0.0", + "semver": "5.5.0", + "strip-ansi": "4.0.0", "strip-json-comments": "2.0.1", - "table": "3.8.3", - "text-table": "0.2.0", - "user-home": "2.0.0" + "table": "4.0.2", + "text-table": "0.2.0" }, "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "dev": true, - "requires": { - "restore-cursor": "1.0.1" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "1.0.5", - "object-assign": "4.1.1" - } - }, - "inquirer": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", - "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", - "dev": true, - "requires": { - "ansi-escapes": "1.4.0", - "ansi-regex": "2.1.1", - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "cli-width": "2.2.0", - "figures": "1.7.0", - "lodash": "4.17.5", - "readline2": "1.0.1", - "run-async": "0.1.0", - "rx-lite": "3.1.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "through": "2.3.8" - } - }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "dev": true, - "requires": { - "exit-hook": "1.1.1", - "onetime": "1.1.0" - } - }, - "run-async": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", - "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", - "dev": true, - "requires": { - "once": "1.4.0" - } - }, - "shelljs": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", - "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", - "dev": true, - "requires": { - "glob": "7.1.2", - "interpret": "1.1.0", - "rechoir": "0.6.2" - } - }, - "strip-bom": { + "ansi-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, - "user-home": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", - "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "os-homedir": "1.0.2" + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "globals": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.3.0.tgz", + "integrity": "sha512-kkpcKNlmQan9Z5ZmgqKH/SMbSmjxQ7QjyNqfXVc8VJcoBV2UEg+sxQD15GQofGRh2hfpwUb70VC31DR7Rq5Hdw==", + "dev": true + }, + "js-yaml": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", + "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", + "dev": true, + "requires": { + "argparse": "1.0.9", + "esprima": "4.0.0" + } + }, + "progress": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" } } } }, "eslint-config-habitrpg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-habitrpg/-/eslint-config-habitrpg-3.0.0.tgz", - "integrity": "sha1-fPq7yR9b084PAMCpo1Ccorl3O+0=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-habitrpg/-/eslint-config-habitrpg-4.0.0.tgz", + "integrity": "sha512-vZc/KjnNVL2BkDBQaQBF9JV16cnZyKa6djCCqH6iKhp8Uuye8Bym3eeLNEcnGkOtZfzJE61hDqiPZXQk7BiXJQ==", "dev": true, "requires": { "eslint-plugin-lodash": "2.6.1", @@ -5396,12 +5442,13 @@ } }, "eslint-friendly-formatter": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/eslint-friendly-formatter/-/eslint-friendly-formatter-2.0.7.tgz", - "integrity": "sha1-ZX+VoZr0mJY2r+uxzJ3mzrvQiO4=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-friendly-formatter/-/eslint-friendly-formatter-3.0.0.tgz", + "integrity": "sha1-J4h0Q1psRuwdlPoLH/SU4w7wQpA=", "dev": true, "requires": { "chalk": "1.1.3", + "coalescy": "1.0.0", "extend": "3.0.1", "minimist": "1.2.0", "text-table": "0.2.0" @@ -5455,9 +5502,9 @@ } }, "eslint-plugin-html": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-2.0.3.tgz", - "integrity": "sha1-fImIOrDIX6XSi2ZqFKTpBqqQuJc=", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-4.0.2.tgz", + "integrity": "sha512-CrQd0F8GWdNWnu4PFrYZl+LjUCXNVy2h0uhDMtnf/7VKc9HRcnkXSrlg0BSGfptZPSzmwnnwCaREAa9+fnQhYw==", "dev": true, "requires": { "htmlparser2": "3.9.2" @@ -5507,6 +5554,27 @@ "ramda": "0.24.1" } }, + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "requires": { + "esrecurse": "4.2.0", + "estraverse": "4.2.0" + }, + "dependencies": { + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + } + } + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==" + }, "espree": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.3.tgz", @@ -5543,7 +5611,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", - "dev": true, "requires": { "estraverse": "4.2.0", "object-assign": "4.1.1" @@ -5552,8 +5619,7 @@ "estraverse": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" } } }, @@ -5572,16 +5638,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.38" - } - }, "event-stream": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", @@ -5687,12 +5743,6 @@ "meow": "3.7.0" } }, - "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", - "dev": true - }, "expand-braces": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", @@ -7385,6 +7435,12 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -7426,6 +7482,12 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=" }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, "get-pixels": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/get-pixels/-/get-pixels-3.3.0.tgz", @@ -7555,12 +7617,6 @@ "logalot": "2.1.0" } }, - "gitbook-plugin-github": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/gitbook-plugin-github/-/gitbook-plugin-github-2.0.0.tgz", - "integrity": "sha1-UWbnY8/MQC1DKIC3pshcHFS1ao0=", - "dev": true - }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -7839,12 +7895,10 @@ } }, "gulp-babel": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/gulp-babel/-/gulp-babel-6.1.3.tgz", - "integrity": "sha512-tm15R3rt4gO59WXCuqrwf4QXJM9VIJC+0J2NPYSC6xZn+cZRD5y5RPGAiHaDxCJq7Rz5BDljlrk3cEjWADF+wQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/gulp-babel/-/gulp-babel-7.0.1.tgz", + "integrity": "sha512-UqHS3AdxZyJCRxqnAX603Dj3k/Wx6hzcgmav3QcxvsIFq3Y8ZkU7iXd0O+JwD5ivqCc6o0r1S7tCB/xxLnuSNw==", "requires": { - "babel-core": "6.26.0", - "object-assign": "4.1.1", "plugin-error": "1.0.1", "replace-ext": "0.0.1", "through2": "2.0.3", @@ -10236,6 +10290,12 @@ "jsonify": "0.0.0" } }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -10520,6 +10580,31 @@ "sinon-chai": "2.14.0" }, "dependencies": { + "chai": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", + "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", + "dev": true, + "requires": { + "assertion-error": "1.1.0", + "deep-eql": "0.1.3", + "type-detect": "1.0.0" + }, + "dependencies": { + "type-detect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", + "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", + "dev": true + } + } + }, + "chai-as-promised": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-5.3.0.tgz", + "integrity": "sha1-CdekApCKpw39vq1T5YU/x50+8hw=", + "dev": true + }, "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", @@ -12618,16 +12703,58 @@ } }, "monk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/monk/-/monk-4.1.0.tgz", - "integrity": "sha1-x+VA6GquaQ1xLD4KtwYa2LpaOio=", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/monk/-/monk-6.0.5.tgz", + "integrity": "sha512-NEygZ2fhRkPE9zxyOT/GhEYKIGClMCQ+StsTruZSlAWf1aRsgvdu8suVvOj3KWfdiOtsIMs9gg8eyyVHPNWRwg==", "dev": true, "requires": { "debug": "2.6.9", - "gitbook-plugin-github": "2.0.0", - "mongodb": "2.2.34" + "mongodb": "2.2.34", + "monk-middleware-cast-ids": "0.2.1", + "monk-middleware-fields": "0.2.0", + "monk-middleware-handle-callback": "0.2.2", + "monk-middleware-options": "0.2.1", + "monk-middleware-query": "0.2.0", + "monk-middleware-wait-for-connection": "0.2.0", + "object-assign": "4.1.1" } }, + "monk-middleware-cast-ids": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/monk-middleware-cast-ids/-/monk-middleware-cast-ids-0.2.1.tgz", + "integrity": "sha1-QMQOWmyzPM7cKJIglDJ17ohhxSk=", + "dev": true + }, + "monk-middleware-fields": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/monk-middleware-fields/-/monk-middleware-fields-0.2.0.tgz", + "integrity": "sha1-/2N6819ZSIecyyvhWpE2CRG+psE=", + "dev": true + }, + "monk-middleware-handle-callback": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/monk-middleware-handle-callback/-/monk-middleware-handle-callback-0.2.2.tgz", + "integrity": "sha512-5hBynb7asZ2uw9XVze7C3XH0zXT51yFDvYydk/5HnWWzh2NLglDSiKDcX0yLKPHzFgiq+5Z4Laq5fFVnFsmm8w==", + "dev": true + }, + "monk-middleware-options": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/monk-middleware-options/-/monk-middleware-options-0.2.1.tgz", + "integrity": "sha1-WNrhxRjUZjbr3/UG+t/Hc7tEKIY=", + "dev": true + }, + "monk-middleware-query": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/monk-middleware-query/-/monk-middleware-query-0.2.0.tgz", + "integrity": "sha1-qSbGd9SlYgxiFRsKVtDAwVFnWHQ=", + "dev": true + }, + "monk-middleware-wait-for-connection": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/monk-middleware-wait-for-connection/-/monk-middleware-wait-for-connection-0.2.0.tgz", + "integrity": "sha1-MSlY0w5Yi1fQl1TdfJe0hDMWg1o=", + "dev": true + }, "morgan": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", @@ -14475,6 +14602,12 @@ "pinkie-promise": "2.0.1" } }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, "pause": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", @@ -14699,9 +14832,9 @@ } }, "pluralize": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", - "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", "dev": true }, "pngjs": { @@ -16041,25 +16174,6 @@ "set-immediate-shim": "1.0.1" } }, - "readline2": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", - "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "mute-stream": "0.0.5" - }, - "dependencies": { - "mute-stream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", - "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", - "dev": true - } - } - }, "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", @@ -16648,12 +16762,6 @@ "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=" }, - "rx-lite": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", - "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", - "dev": true - }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", @@ -17130,10 +17238,21 @@ "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" }, "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", - "dev": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } }, "sliced": { "version": "1.0.1", @@ -18207,28 +18326,24 @@ } }, "table": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", - "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", "dev": true, "requires": { - "ajv": "4.11.8", - "ajv-keywords": "1.5.1", - "chalk": "1.1.3", + "ajv": "5.5.2", + "ajv-keywords": "2.1.1", + "chalk": "2.3.0", "lodash": "4.17.5", - "slice-ansi": "0.0.4", + "slice-ansi": "1.0.0", "string-width": "2.1.1" }, "dependencies": { - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } + "ajv-keywords": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "dev": true }, "ansi-regex": { "version": "3.0.0", @@ -18236,36 +18351,6 @@ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - } - } - }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -18923,9 +19008,9 @@ } }, "type-detect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", - "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, "type-is": { diff --git a/package.json b/package.json index 1666a4f8d4..85bebd4807 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "axios": "^0.16.0", "axios-progress-bar": "^0.1.7", "babel-core": "^6.0.0", - "babel-eslint": "^7.2.3", + "babel-eslint": "^8.2.1", "babel-loader": "^6.0.0", "babel-plugin-syntax-async-functions": "^6.13.0", "babel-plugin-syntax-dynamic-import": "^6.18.0", @@ -36,8 +36,8 @@ "coupon-code": "^0.4.5", "cross-env": "^5.1.3", "css-loader": "^0.28.0", - "cwait": "^1.1.1", "csv-stringify": "^2.0.1", + "cwait": "^1.1.1", "domain-middleware": "~0.1.0", "express": "^4.16.2", "express-basic-auth": "^1.0.1", @@ -46,7 +46,7 @@ "glob": "^7.1.2", "got": "^6.1.1", "gulp": "^4.0.0", - "gulp-babel": "^6.1.2", + "gulp-babel": "^7.0.1", "gulp-imagemin": "^4.1.0", "gulp-nodemon": "^2.2.1", "gulp.spritesmith": "^6.9.0", @@ -85,8 +85,8 @@ "request": "^2.83.0", "rimraf": "^2.4.3", "sass-loader": "^6.0.2", - "stripe": "^5.4.0", "shelljs": "^0.8.1", + "stripe": "^5.4.0", "superagent": "^3.4.3", "svg-inline-loader": "^0.7.1", "svg-url-loader": "^2.0.2", @@ -142,18 +142,18 @@ }, "devDependencies": { "babel-plugin-istanbul": "^4.0.0", - "chai": "^3.4.0", - "chai-as-promised": "^5.1.0", + "chai": "^4.1.2", + "chai-as-promised": "^7.1.1", "chalk": "^2.3.0", "chromedriver": "^2.27.2", "connect-history-api-fallback": "^1.1.0", "coveralls": "^3.0.0", "cross-spawn": "^6.0.4", - "eslint": "^3.0.0", - "eslint-config-habitrpg": "^3.0.0", - "eslint-friendly-formatter": "^2.0.5", + "eslint": "^4.17.0", + "eslint-config-habitrpg": "^4.0.0", + "eslint-friendly-formatter": "^3.0.0", "eslint-loader": "^1.3.0", - "eslint-plugin-html": "^2.0.0", + "eslint-plugin-html": "^4.0.2", "eslint-plugin-mocha": "^4.7.0", "eventsource-polyfill": "^0.9.6", "expect.js": "^0.3.1", @@ -173,7 +173,7 @@ "karma-webpack": "^2.0.2", "lcov-result-merger": "^2.0.0", "mocha": "^5.0.0", - "monk": "^4.0.0", + "monk": "^6.0.5", "nightwatch": "^0.9.12", "phantomjs-prebuilt": "^2.1.12", "require-again": "^2.0.0", diff --git a/scripts/paypalBillingSetup.js b/scripts/paypalBillingSetup.js index 78fcedce2e..b941254420 100644 --- a/scripts/paypalBillingSetup.js +++ b/scripts/paypalBillingSetup.js @@ -1,111 +1,120 @@ -require("babel-register"); -require("babel-polyfill"); +require('babel-register'); +require('babel-polyfill'); + // This file is used for creating paypal billing plans. PayPal doesn't have a web interface for setting up recurring // payment plan definitions, instead you have to create it via their REST SDK and keep it updated the same way. So this // file will be used once for initing your billing plan (then you get the resultant plan.id to store in config.json), // and once for any time you need to edit the plan thereafter -var path = require('path'); -var nconf = require('nconf'); -var _ = require('lodash'); -var paypal = require('paypal-rest-sdk'); -var blocks = require('../website/common').content.subscriptionBlocks; -var live = nconf.get('PAYPAL:mode')=='live'; +/* eslint-disable no-console, camelcase, no-case-declarations */ + +const path = require('path'); +const nconf = require('nconf'); +const _ = require('lodash'); +const paypal = require('paypal-rest-sdk'); +const blocks = require('../website/common').content.subscriptionBlocks; +const live = nconf.get('PAYPAL:mode') === 'live'; nconf.argv().env().file('user', path.join(path.resolve(__dirname, '../config.json'))); -var OP = 'create'; // list create update remove +let OP = 'create'; // list create update remove paypal.configure({ - 'mode': nconf.get("PAYPAL:mode"), //sandbox or live - 'client_id': nconf.get("PAYPAL:client_id"), - 'client_secret': nconf.get("PAYPAL:client_secret") + mode: nconf.get('PAYPAL:mode'), // sandbox or live + client_id: nconf.get('PAYPAL:client_id'), + client_secret: nconf.get('PAYPAL:client_secret'), }); // https://developer.paypal.com/docs/api/#billing-plans-and-agreements -var billingPlanTitle ="Habitica Subscription"; -var billingPlanAttributes = { - "name": billingPlanTitle, - "description": billingPlanTitle, - "type": "INFINITE", - "merchant_preferences": { - "auto_bill_amount": "yes", - "cancel_url": live ? 'https://habitica.com' : 'http://localhost:3000', - "return_url": (live ? 'https://habitica.com' : 'http://localhost:3000') + '/paypal/subscribe/success' +let billingPlanTitle = 'Habitica Subscription'; +let billingPlanAttributes = { + name: billingPlanTitle, + description: billingPlanTitle, + type: 'INFINITE', + merchant_preferences: { + auto_bill_amount: 'yes', + cancel_url: live ? 'https://habitica.com' : 'http://localhost:3000', + return_url: `${live ? 'https://habitica.com' : 'http://localhost:3000' }/paypal/subscribe/success`, }, payment_definitions: [{ - "type": "REGULAR", - "frequency": "MONTH", - "cycles": "0" - }] + type: 'REGULAR', + frequency: 'MONTH', + cycles: '0', + }], }; -_.each(blocks, function(block){ + +_.each(blocks, (block) => { block.definition = _.cloneDeep(billingPlanAttributes); _.merge(block.definition.payment_definitions[0], { - "name": billingPlanTitle + ' ($'+block.price+' every '+block.months+' months, recurring)', - "frequency_interval": ""+block.months, - "amount": { - "currency": "USD", - "value": ""+block.price - } + name: `${billingPlanTitle } ($${block.price} every ${block.months} months, recurring)`, + frequency_interval: `${block.months}`, + amount: { + currency: 'USD', + value: `${block.price}`, + }, }); -}) +}); // @TODO: Add cli library for this -switch(OP) { - case "list": - paypal.billingPlan.list({status: 'ACTIVE'}, function(err, plans){ - console.log({err:err, plans:plans}); +switch (OP) { + case 'list': + paypal.billingPlan.list({status: 'ACTIVE'}, (err, plans) => { + console.log({err, plans}); }); break; - case "get": - paypal.billingPlan.get(nconf.get("PAYPAL:billing_plans:12"), function (err, plan) { - console.log({err:err, plan:plan}); - }) + case 'get': + paypal.billingPlan.get(nconf.get('PAYPAL:billing_plans:12'), (err, plan) => { + console.log({err, plan}); + }); break; - case "update": - var update = { - "op": "replace", - "path": "/merchant_preferences", - "value": { - "cancel_url": "https://habitica.com" - } + case 'update': + let updatePayload = { + op: 'replace', + path: '/merchant_preferences', + value: { + cancel_url: 'https://habitica.com', + }, }; - paypal.billingPlan.update(nconf.get("PAYPAL:billing_plans:12"), update, function (err, res) { - console.log({err:err, plan:res}); + paypal.billingPlan.update(nconf.get('PAYPAL:billing_plans:12'), updatePayload, (err, res) => { + console.log({err, plan: res}); }); break; - case "create": - paypal.billingPlan.create(blocks["google_6mo"].definition, function(err,plan){ + case 'create': + paypal.billingPlan.create(blocks.google_6mo.definition, (err, plan) => { if (err) return console.log(err); - if (plan.state == "ACTIVE") - return console.log({err:err, plan:plan}); - var billingPlanUpdateAttributes = [{ - "op": "replace", - "path": "/", - "value": { - "state": "ACTIVE" - } + + if (plan.state === 'ACTIVE') { + return console.log({err, plan}); + } + + let billingPlanUpdateAttributes = [{ + op: 'replace', + path: '/', + value: { + state: 'ACTIVE', + }, }]; + // Activate the plan by changing status to Active - paypal.billingPlan.update(plan.id, billingPlanUpdateAttributes, function(err, response){ - console.log({err:err, response:response, id:plan.id}); + paypal.billingPlan.update(plan.id, billingPlanUpdateAttributes, (err2, response) => { + console.log({err: err2, response, id: plan.id}); }); }); break; - case "remove": break; + + case 'remove': break; case 'create-webprofile': let webexpinfo = { - "name": "HabiticaProfile", - "input_fields": { - "no_shipping": 1, + name: 'HabiticaProfile', + input_fields: { + no_shipping: 1, }, }; paypal.webProfile.create(webexpinfo, (error, result) => { - console.log(error, result) - }) + console.log(error, result); + }); break; -} +} \ No newline at end of file diff --git a/test/api/v3/integration/challenges/POST-challenges_challengeId_winner_winnerId.test.js b/test/api/v3/integration/challenges/POST-challenges_challengeId_winner_winnerId.test.js index e0dc561c66..19e8deaf12 100644 --- a/test/api/v3/integration/challenges/POST-challenges_challengeId_winner_winnerId.test.js +++ b/test/api/v3/integration/challenges/POST-challenges_challengeId_winner_winnerId.test.js @@ -99,7 +99,7 @@ describe('POST /challenges/:challengeId/winner/:winnerId', () => { await sleep(0.5); - await expect(winningUser.sync()).to.eventually.have.deep.property('achievements.challenges').to.include(challenge.name); + await expect(winningUser.sync()).to.eventually.have.nested.property('achievements.challenges').to.include(challenge.name); expect(winningUser.notifications.length).to.equal(2); // 2 because winningUser just joined the challenge, which now awards an achievement expect(winningUser.notifications[1].type).to.equal('WON_CHALLENGE'); }); diff --git a/test/api/v3/integration/chat/POST-chat.test.js b/test/api/v3/integration/chat/POST-chat.test.js index 395c6b88e1..c9b2d97384 100644 --- a/test/api/v3/integration/chat/POST-chat.test.js +++ b/test/api/v3/integration/chat/POST-chat.test.js @@ -119,21 +119,21 @@ describe('POST /chat', () => { it('errors when word is part of a phrase', async () => { let wordInPhrase = `phrase ${testBannedWordMessage} end`; await expect(user.post('/groups/habitrpg/chat', { message: wordInPhrase})) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: bannedWordErrorMessage, - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: bannedWordErrorMessage, + }); }); it('errors when word is surrounded by non alphabet characters', async () => { let wordInPhrase = `_!${testBannedWordMessage}@_`; await expect(user.post('/groups/habitrpg/chat', { message: wordInPhrase})) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: bannedWordErrorMessage, - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: bannedWordErrorMessage, + }); }); it('checks error message has the banned words used', async () => { diff --git a/test/api/v3/integration/content/GET-content.test.js b/test/api/v3/integration/content/GET-content.test.js index fad68edae2..1489738577 100644 --- a/test/api/v3/integration/content/GET-content.test.js +++ b/test/api/v3/integration/content/GET-content.test.js @@ -7,19 +7,19 @@ import i18n from '../../../../../website/common/script/i18n'; describe('GET /content', () => { it('returns content (and does not require authentication)', async () => { let res = await requester().get('/content'); - expect(res).to.have.deep.property('backgrounds.backgrounds062014.beach'); + expect(res).to.have.nested.property('backgrounds.backgrounds062014.beach'); expect(res.backgrounds.backgrounds062014.beach.text).to.equal(t('backgroundBeachText')); }); it('returns content not in English', async () => { let res = await requester().get('/content?language=de'); - expect(res).to.have.deep.property('backgrounds.backgrounds062014.beach'); + expect(res).to.have.nested.property('backgrounds.backgrounds062014.beach'); expect(res.backgrounds.backgrounds062014.beach.text).to.equal(i18n.t('backgroundBeachText', 'de')); }); it('falls back to English if the desired language is not found', async () => { let res = await requester().get('/content?language=wrong'); - expect(res).to.have.deep.property('backgrounds.backgrounds062014.beach'); + expect(res).to.have.nested.property('backgrounds.backgrounds062014.beach'); expect(res.backgrounds.backgrounds062014.beach.text).to.equal(t('backgroundBeachText')); }); }); diff --git a/test/api/v3/integration/debug/POST-debug_addTenGems.test.js b/test/api/v3/integration/debug/POST-debug_addTenGems.test.js index fd01aea5d3..d1a3b39eb2 100644 --- a/test/api/v3/integration/debug/POST-debug_addTenGems.test.js +++ b/test/api/v3/integration/debug/POST-debug_addTenGems.test.js @@ -26,10 +26,10 @@ describe('POST /debug/add-ten-gems', () => { nconf.set('IS_PROD', true); await expect(userToGainTenGems.post('/debug/add-ten-gems')) - .eventually.be.rejected.and.to.deep.equal({ - code: 404, - error: 'NotFound', - message: 'Not found.', - }); + .eventually.be.rejected.and.to.deep.equal({ + code: 404, + error: 'NotFound', + message: 'Not found.', + }); }); }); diff --git a/test/api/v3/integration/debug/POST-debug_make-admin.test.js b/test/api/v3/integration/debug/POST-debug_make-admin.test.js index 98818fb07e..0696b6469f 100644 --- a/test/api/v3/integration/debug/POST-debug_make-admin.test.js +++ b/test/api/v3/integration/debug/POST-debug_make-admin.test.js @@ -26,10 +26,10 @@ describe('POST /debug/make-admin (pended for v3 prod testing)', () => { nconf.set('IS_PROD', true); await expect(user.post('/debug/make-admin')) - .eventually.be.rejected.and.to.deep.equal({ - code: 404, - error: 'NotFound', - message: 'Not found.', - }); + .eventually.be.rejected.and.to.deep.equal({ + code: 404, + error: 'NotFound', + message: 'Not found.', + }); }); }); diff --git a/test/api/v3/integration/debug/POST-debug_modify-inventory.test.js b/test/api/v3/integration/debug/POST-debug_modify-inventory.test.js index 93f9081492..7047150477 100644 --- a/test/api/v3/integration/debug/POST-debug_modify-inventory.test.js +++ b/test/api/v3/integration/debug/POST-debug_modify-inventory.test.js @@ -151,10 +151,10 @@ describe('POST /debug/modify-inventory', () => { nconf.set('IS_PROD', true); await expect(user.post('/debug/modify-inventory')) - .eventually.be.rejected.and.to.deep.equal({ - code: 404, - error: 'NotFound', - message: 'Not found.', - }); + .eventually.be.rejected.and.to.deep.equal({ + code: 404, + error: 'NotFound', + message: 'Not found.', + }); }); }); diff --git a/test/api/v3/integration/debug/POST-debug_quest-progress.test.js b/test/api/v3/integration/debug/POST-debug_quest-progress.test.js index 6f10b78ba4..90c14c4470 100644 --- a/test/api/v3/integration/debug/POST-debug_quest-progress.test.js +++ b/test/api/v3/integration/debug/POST-debug_quest-progress.test.js @@ -16,11 +16,11 @@ describe('POST /debug/quest-progress', () => { it('errors if user is not on a quest', async () => { await expect(user.post('/debug/quest-progress')) - .to.eventually.be.rejected.and.to.deep.equal({ - code: 400, - error: 'BadRequest', - message: 'User is not on a valid quest.', - }); + .to.eventually.be.rejected.and.to.deep.equal({ + code: 400, + error: 'BadRequest', + message: 'User is not on a valid quest.', + }); }); it('increases boss quest progress by 1000', async () => { @@ -51,10 +51,10 @@ describe('POST /debug/quest-progress', () => { nconf.set('IS_PROD', true); await expect(user.post('/debug/quest-progress')) - .eventually.be.rejected.and.to.deep.equal({ - code: 404, - error: 'NotFound', - message: 'Not found.', - }); + .eventually.be.rejected.and.to.deep.equal({ + code: 404, + error: 'NotFound', + message: 'Not found.', + }); }); }); diff --git a/test/api/v3/integration/debug/POST-debug_set-cron.test.js b/test/api/v3/integration/debug/POST-debug_set-cron.test.js index c737831d95..10fe75fa14 100644 --- a/test/api/v3/integration/debug/POST-debug_set-cron.test.js +++ b/test/api/v3/integration/debug/POST-debug_set-cron.test.js @@ -30,10 +30,10 @@ describe('POST /debug/set-cron', () => { nconf.set('IS_PROD', true); await expect(user.post('/debug/set-cron')) - .eventually.be.rejected.and.to.deep.equal({ - code: 404, - error: 'NotFound', - message: 'Not found.', - }); + .eventually.be.rejected.and.to.deep.equal({ + code: 404, + error: 'NotFound', + message: 'Not found.', + }); }); }); diff --git a/test/api/v3/integration/groups/GET-groups.test.js b/test/api/v3/integration/groups/GET-groups.test.js index b7c8fbd942..10821a7dcb 100644 --- a/test/api/v3/integration/groups/GET-groups.test.js +++ b/test/api/v3/integration/groups/GET-groups.test.js @@ -93,14 +93,14 @@ describe('GET /groups', () => { it('returns only the tavern when tavern passed in as query', async () => { await expect(user.get('/groups?type=tavern')) .to.eventually.have.a.lengthOf(1) - .and.to.have.deep.property('[0]') + .and.to.have.nested.property('[0]') .and.to.have.property('_id', TAVERN_ID); }); it('returns only the user\'s party when party passed in as query', async () => { await expect(user.get('/groups?type=party')) .to.eventually.have.a.lengthOf(1) - .and.to.have.deep.property('[0]'); + .and.to.have.nested.property('[0]'); }); it('returns all public guilds when publicGuilds passed in as query', async () => { diff --git a/test/api/v3/integration/groups/POST-groups_groupId_join.test.js b/test/api/v3/integration/groups/POST-groups_groupId_join.test.js index 33b1b7f5a4..a06f40bc4e 100644 --- a/test/api/v3/integration/groups/POST-groups_groupId_join.test.js +++ b/test/api/v3/integration/groups/POST-groups_groupId_join.test.js @@ -58,7 +58,7 @@ describe('POST /group/:groupId/join', () => { await joiningUser.post(`/groups/${publicGuild._id}/join`); - await expect(joiningUser.get(`/groups/${publicGuild._id}`)).to.eventually.have.deep.property('leader._id', joiningUser._id); + await expect(joiningUser.get(`/groups/${publicGuild._id}`)).to.eventually.have.nested.property('leader._id', joiningUser._id); }); it('increments memberCount when joining guilds', async () => { @@ -72,7 +72,7 @@ describe('POST /group/:groupId/join', () => { it('awards Joined Guild achievement', async () => { await joiningUser.post(`/groups/${publicGuild._id}/join`); - await expect(joiningUser.get('/user')).to.eventually.have.deep.property('achievements.joinedGuild', true); + await expect(joiningUser.get('/user')).to.eventually.have.nested.property('achievements.joinedGuild', true); }); }); @@ -115,7 +115,7 @@ describe('POST /group/:groupId/join', () => { await invitedUser.post(`/groups/${guild._id}/join`); await expect(invitedUser.get('/user')) - .to.eventually.have.deep.property('invitations.guilds') + .to.eventually.have.nested.property('invitations.guilds') .to.not.include({id: guild._id}); }); @@ -130,7 +130,7 @@ describe('POST /group/:groupId/join', () => { it('does not give basilist quest to inviter when joining a guild', async () => { await invitedUser.post(`/groups/${guild._id}/join`); - await expect(user.get('/user')).to.eventually.not.have.deep.property('items.quests.basilist'); + await expect(user.get('/user')).to.eventually.not.have.nested.property('items.quests.basilist'); }); it('does not increment basilist quest count to inviter with basilist when joining a guild', async () => { @@ -138,7 +138,7 @@ describe('POST /group/:groupId/join', () => { await invitedUser.post(`/groups/${guild._id}/join`); - await expect(user.get('/user')).to.eventually.have.deep.property('items.quests.basilist', 1); + await expect(user.get('/user')).to.eventually.have.nested.property('items.quests.basilist', 1); }); it('notifies inviting user that their invitation was accepted', async () => { @@ -160,7 +160,7 @@ describe('POST /group/:groupId/join', () => { it('awards Joined Guild achievement', async () => { await invitedUser.post(`/groups/${guild._id}/join`); - await expect(invitedUser.get('/user')).to.eventually.have.deep.property('achievements.joinedGuild', true); + await expect(invitedUser.get('/user')).to.eventually.have.nested.property('achievements.joinedGuild', true); }); }); }); @@ -197,7 +197,7 @@ describe('POST /group/:groupId/join', () => { it('allows invited user to join party', async () => { await invitedUser.post(`/groups/${party._id}/join`); - await expect(invitedUser.get('/user')).to.eventually.have.deep.property('party._id', party._id); + await expect(invitedUser.get('/user')).to.eventually.have.nested.property('party._id', party._id); }); it('notifies inviting user that their invitation was accepted', async () => { @@ -220,7 +220,7 @@ describe('POST /group/:groupId/join', () => { it('clears invitation from user when joining party', async () => { await invitedUser.post(`/groups/${party._id}/join`); - await expect(invitedUser.get('/user')).to.eventually.not.have.deep.property('invitations.parties[0].id'); + await expect(invitedUser.get('/user')).to.eventually.not.have.nested.property('invitations.parties[0].id'); }); it('increments memberCount when joining party', async () => { @@ -234,7 +234,7 @@ describe('POST /group/:groupId/join', () => { it('gives basilist quest item to the inviter when joining a party', async () => { await invitedUser.post(`/groups/${party._id}/join`); - await expect(user.get('/user')).to.eventually.have.deep.property('items.quests.basilist', 1); + await expect(user.get('/user')).to.eventually.have.nested.property('items.quests.basilist', 1); }); it('increments basilist quest item count to inviter when joining a party', async () => { @@ -242,7 +242,7 @@ describe('POST /group/:groupId/join', () => { await invitedUser.post(`/groups/${party._id}/join`); - await expect(user.get('/user')).to.eventually.have.deep.property('items.quests.basilist', 2); + await expect(user.get('/user')).to.eventually.have.nested.property('items.quests.basilist', 2); }); it('deletes previous party where the user was the only member', async () => { @@ -258,7 +258,7 @@ describe('POST /group/:groupId/join', () => { }); await userToInvite.post(`/groups/${party._id}/join`); - await expect(user.get('/user')).to.eventually.have.deep.property('party._id', party._id); + await expect(user.get('/user')).to.eventually.have.nested.property('party._id', party._id); await expect(checkExistence('groups', oldParty._id)).to.eventually.equal(false); }); @@ -273,8 +273,8 @@ describe('POST /group/:groupId/join', () => { await invitedUser.sync(); await party.sync(); - expect(invitedUser).to.have.deep.property('party.quest.RSVPNeeded', true); - expect(invitedUser).to.have.deep.property('party.quest.key', party.quest.key); + expect(invitedUser).to.have.nested.property('party.quest.RSVPNeeded', true); + expect(invitedUser).to.have.nested.property('party.quest.key', party.quest.key); expect(party.quest.members[invitedUser._id]).to.be.null; }); }); @@ -300,16 +300,16 @@ describe('POST /group/:groupId/join', () => { await member.sync(); await leader.sync(); - expect(member).to.have.deep.property('achievements.partyUp', true); - expect(leader).to.have.deep.property('achievements.partyUp', true); + expect(member).to.have.nested.property('achievements.partyUp', true); + expect(leader).to.have.nested.property('achievements.partyUp', true); }); it('does not award Party On achievement to party of size 2', async () => { await member.sync(); await leader.sync(); - expect(member).to.not.have.deep.property('achievements.partyOn'); - expect(leader).to.not.have.deep.property('achievements.partyOn'); + expect(member).to.not.have.nested.property('achievements.partyOn'); + expect(leader).to.not.have.nested.property('achievements.partyOn'); }); it('awards Party On achievement to party of size 4', async () => { @@ -324,8 +324,8 @@ describe('POST /group/:groupId/join', () => { await member.sync(); await leader.sync(); - expect(member).to.have.deep.property('achievements.partyOn', true); - expect(leader).to.have.deep.property('achievements.partyOn', true); + expect(member).to.have.nested.property('achievements.partyOn', true); + expect(leader).to.have.nested.property('achievements.partyOn', true); }); }); }); diff --git a/test/api/v3/integration/groups/POST-groups_groupId_leave.js b/test/api/v3/integration/groups/POST-groups_groupId_leave.js index c9dd64e7a9..615b5e01e2 100644 --- a/test/api/v3/integration/groups/POST-groups_groupId_leave.js +++ b/test/api/v3/integration/groups/POST-groups_groupId_leave.js @@ -85,7 +85,7 @@ describe('POST /groups/:groupId/leave', () => { expect(leader.notifications.find(n => { return n.type === 'NEW_CHAT_MESSAGE' && n.data.group.id === groupToLeave._id; })).to.not.exist; - expect(leader.newMessages[groupToLeave._id]).to.be.empty; + expect(leader.newMessages[groupToLeave._id]).to.be.undefined; }); context('with challenges', () => { @@ -257,7 +257,7 @@ describe('POST /groups/:groupId/leave', () => { let userWithoutInvitation = await invitedUser.get('/user'); - expect(userWithoutInvitation.invitations.parties[0]).to.be.empty; + expect(userWithoutInvitation.invitations.parties[0]).to.be.undefined; }); }); diff --git a/test/api/v3/integration/groups/POST-groups_groupId_reject.test.js b/test/api/v3/integration/groups/POST-groups_groupId_reject.test.js index 626c6ff896..7f1e31df85 100644 --- a/test/api/v3/integration/groups/POST-groups_groupId_reject.test.js +++ b/test/api/v3/integration/groups/POST-groups_groupId_reject.test.js @@ -36,7 +36,7 @@ describe('POST /group/:groupId/reject-invite', () => { await invitedUser.post(`/groups/${publicGuild._id}/reject-invite`); await expect(invitedUser.get('/user')) - .to.eventually.have.deep.property('invitations.guilds') + .to.eventually.have.nested.property('invitations.guilds') .to.not.include({id: publicGuild._id}); }); }); @@ -72,7 +72,7 @@ describe('POST /group/:groupId/reject-invite', () => { await invitedUser.post(`/groups/${guild._id}/reject-invite`); await expect(invitedUser.get('/user')) - .to.eventually.have.deep.property('invitations.guilds') + .to.eventually.have.nested.property('invitations.guilds') .to.not.include({id: guild._id}); }); }); @@ -107,7 +107,7 @@ describe('POST /group/:groupId/reject-invite', () => { it('clears invitation from user', async () => { await invitedUser.post(`/groups/${party._id}/reject-invite`); - await expect(invitedUser.get('/user')).to.eventually.not.have.deep.property('invitations.parties[0].id'); + await expect(invitedUser.get('/user')).to.eventually.not.have.nested.property('invitations.parties[0].id'); }); }); }); diff --git a/test/api/v3/integration/groups/POST-groups_id_removeMember.test.js b/test/api/v3/integration/groups/POST-groups_id_removeMember.test.js index 6fb2af0704..97819e7a41 100644 --- a/test/api/v3/integration/groups/POST-groups_id_removeMember.test.js +++ b/test/api/v3/integration/groups/POST-groups_id_removeMember.test.js @@ -184,7 +184,7 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => { let invitedUserWithoutInvite = await partyInvitedUser.get('/user'); - expect(invitedUserWithoutInvite.invitations.parties[0]).to.be.empty; + expect(invitedUserWithoutInvite.invitations.parties[0]).to.be.undefined; }); it('removes new messages from a member who is removed', async () => { @@ -203,7 +203,7 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => { expect(removedMember.notifications.find(n => { return n.type === 'NEW_CHAT_MESSAGE' && n.data.group.id === party._id; })).to.not.exist; - expect(removedMember.newMessages[party._id]).to.be.empty; + expect(removedMember.newMessages[party._id]).to.be.undefined; }); it('removes user from quest when removing user from party after quest starts', async () => { diff --git a/test/api/v3/integration/groups/POST-groups_invite.test.js b/test/api/v3/integration/groups/POST-groups_invite.test.js index 3de01d0d40..385fa431a6 100644 --- a/test/api/v3/integration/groups/POST-groups_invite.test.js +++ b/test/api/v3/integration/groups/POST-groups_invite.test.js @@ -30,22 +30,22 @@ describe('Post /groups/:groupId/invite', () => { await expect(inviter.post(`/groups/${group._id}/invite`, { uuids: [fakeID], })) - .to.eventually.be.rejected.and.eql({ - code: 404, - error: 'NotFound', - message: t('userWithIDNotFound', {userId: fakeID}), - }); + .to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('userWithIDNotFound', {userId: fakeID}), + }); }); it('returns an error when inviting yourself to a group', async () => { await expect(inviter.post(`/groups/${group._id}/invite`, { uuids: [inviter._id], })) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('cannotInviteSelfToGroup'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('cannotInviteSelfToGroup'), + }); }); it('returns an error when uuids is not an array', async () => { @@ -54,11 +54,11 @@ describe('Post /groups/:groupId/invite', () => { await expect(inviter.post(`/groups/${group._id}/invite`, { uuids: {fakeID}, })) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('uuidsMustBeAnArray'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('uuidsMustBeAnArray'), + }); }); it('returns an error when uuids and emails are empty', async () => { @@ -66,22 +66,22 @@ describe('Post /groups/:groupId/invite', () => { emails: [], uuids: [], })) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('inviteMustNotBeEmpty'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('inviteMustNotBeEmpty'), + }); }); it('returns an error when uuids is empty and emails is not passed', async () => { await expect(inviter.post(`/groups/${group._id}/invite`, { uuids: [], })) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('inviteMissingUuid'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('inviteMissingUuid'), + }); }); it('returns an error when there are more than INVITES_LIMIT uuids', async () => { @@ -94,11 +94,11 @@ describe('Post /groups/:groupId/invite', () => { await expect(inviter.post(`/groups/${group._id}/invite`, { uuids, })) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('canOnlyInviteMaxInvites', {maxInvites: INVITES_LIMIT}), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('canOnlyInviteMaxInvites', {maxInvites: INVITES_LIMIT}), + }); }); it('invites a user to a group by uuid', async () => { @@ -114,7 +114,7 @@ describe('Post /groups/:groupId/invite', () => { }]); await expect(userToInvite.get('/user')) - .to.eventually.have.deep.property('invitations.guilds[0].id', group._id); + .to.eventually.have.nested.property('invitations.guilds[0].id', group._id); }); it('invites multiple users to a group by uuid', async () => { @@ -138,8 +138,8 @@ describe('Post /groups/:groupId/invite', () => { }, ]); - await expect(userToInvite.get('/user')).to.eventually.have.deep.property('invitations.guilds[0].id', group._id); - await expect(userToInvite2.get('/user')).to.eventually.have.deep.property('invitations.guilds[0].id', group._id); + await expect(userToInvite.get('/user')).to.eventually.have.nested.property('invitations.guilds[0].id', group._id); + await expect(userToInvite2.get('/user')).to.eventually.have.nested.property('invitations.guilds[0].id', group._id); }); it('returns an error when inviting multiple users and a user is not found', async () => { @@ -149,11 +149,11 @@ describe('Post /groups/:groupId/invite', () => { await expect(inviter.post(`/groups/${group._id}/invite`, { uuids: [userToInvite._id, fakeID], })) - .to.eventually.be.rejected.and.eql({ - code: 404, - error: 'NotFound', - message: t('userWithIDNotFound', {userId: fakeID}), - }); + .to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('userWithIDNotFound', {userId: fakeID}), + }); }); }); @@ -164,33 +164,33 @@ describe('Post /groups/:groupId/invite', () => { await expect(inviter.post(`/groups/${group._id}/invite`, { emails: [{name: 'test'}], })) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('inviteMissingEmail'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('inviteMissingEmail'), + }); }); it('returns an error when emails is not an array', async () => { await expect(inviter.post(`/groups/${group._id}/invite`, { emails: {testInvite}, })) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('emailsMustBeAnArray'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('emailsMustBeAnArray'), + }); }); it('returns an error when emails is empty and uuids is not passed', async () => { await expect(inviter.post(`/groups/${group._id}/invite`, { emails: [], })) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('inviteMissingEmail'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('inviteMissingEmail'), + }); }); it('returns an error when there are more than INVITES_LIMIT emails', async () => { @@ -203,11 +203,11 @@ describe('Post /groups/:groupId/invite', () => { await expect(inviter.post(`/groups/${group._id}/invite`, { emails, })) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('canOnlyInviteMaxInvites', {maxInvites: INVITES_LIMIT}), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('canOnlyInviteMaxInvites', {maxInvites: INVITES_LIMIT}), + }); }); it('returns an error when a user has sent the max number of email invites', async () => { @@ -224,11 +224,11 @@ describe('Post /groups/:groupId/invite', () => { emails: [testInvite], inviter: 'inviter name', })) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('inviteLimitReached', {techAssistanceEmail: nconf.get('EMAILS:TECH_ASSISTANCE_EMAIL')}), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('inviteLimitReached', {techAssistanceEmail: nconf.get('EMAILS:TECH_ASSISTANCE_EMAIL')}), + }); }); it('invites a user to a group by email', async () => { @@ -258,11 +258,11 @@ describe('Post /groups/:groupId/invite', () => { describe('user and email invites', () => { it('returns an error when emails and uuids are not provided', async () => { await expect(inviter.post(`/groups/${group._id}/invite`)) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('canOnlyInviteEmailUuid'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('canOnlyInviteEmailUuid'), + }); }); it('returns an error when there are more than INVITES_LIMIT uuids and emails', async () => { @@ -281,11 +281,11 @@ describe('Post /groups/:groupId/invite', () => { emails, uuids, })) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('canOnlyInviteMaxInvites', {maxInvites: INVITES_LIMIT}), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('canOnlyInviteMaxInvites', {maxInvites: INVITES_LIMIT}), + }); }); it('invites users to a group by uuid and email', async () => { @@ -330,11 +330,11 @@ describe('Post /groups/:groupId/invite', () => { await expect(inviter.post(`/groups/${group._id}/invite`, { uuids: [userToInivite._id], })) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('userAlreadyInvitedToGroup'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('userAlreadyInvitedToGroup'), + }); }); it('returns an error when invited user is already in the group', async () => { @@ -347,11 +347,11 @@ describe('Post /groups/:groupId/invite', () => { await expect(inviter.post(`/groups/${group._id}/invite`, { uuids: [userToInvite._id], })) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('userAlreadyInGroup'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('userAlreadyInGroup'), + }); }); it('allows 30+ members in a guild', async () => { @@ -380,11 +380,11 @@ describe('Post /groups/:groupId/invite', () => { await expect(nonGroupLeader.post(`/groups/${group._id}/invite`, { uuids: [userToInvite._id], })) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('onlyGroupLeaderCanInviteToGroupPlan'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('onlyGroupLeaderCanInviteToGroupPlan'), + }); }); }); @@ -407,11 +407,11 @@ describe('Post /groups/:groupId/invite', () => { await expect(inviter.post(`/groups/${party._id}/invite`, { uuids: [userToInvite._id], })) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('userAlreadyPendingInvitation'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('userAlreadyPendingInvitation'), + }); }); it('returns an error when invited user is already in a party of more than 1 member', async () => { @@ -426,11 +426,11 @@ describe('Post /groups/:groupId/invite', () => { await expect(inviter.post(`/groups/${party._id}/invite`, { uuids: [userToInvite._id], })) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('userAlreadyInAParty'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('userAlreadyInAParty'), + }); }); it('allow inviting a user to a party if they are partying solo', async () => { @@ -512,11 +512,11 @@ describe('Post /groups/:groupId/invite', () => { await expect(inviter.post(`/groups/${party._id}/invite`, { uuids: generatedInvites.map(invite => invite._id), })) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('partyExceedsMembersLimit', {maxMembersParty: PARTY_LIMIT_MEMBERS}), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('partyExceedsMembersLimit', {maxMembersParty: PARTY_LIMIT_MEMBERS}), + }); }); }); }); diff --git a/test/api/v3/integration/news/POST-news_tell_me_later.test.js b/test/api/v3/integration/news/POST-news_tell_me_later.test.js index 47444574c4..491a33f210 100644 --- a/test/api/v3/integration/news/POST-news_tell_me_later.test.js +++ b/test/api/v3/integration/news/POST-news_tell_me_later.test.js @@ -24,7 +24,7 @@ describe('POST /news/tell-me-later', () => { const notification = user.notifications[user.notifications.length - 1]; expect(notification.type).to.equal('NEW_STUFF'); - // should be marked as seen by default so it's not counted in the number of notifications + // should be marked as seen by default so it's not counted in the number of notifications expect(notification.seen).to.equal(true); expect(notification.data.title).to.be.a.string; }); diff --git a/test/api/v3/integration/payments/apple/POST-payments_apple_verifyiap.js b/test/api/v3/integration/payments/apple/POST-payments_apple_verifyiap.js index a5d4c1e769..72ebf915f7 100644 --- a/test/api/v3/integration/payments/apple/POST-payments_apple_verifyiap.js +++ b/test/api/v3/integration/payments/apple/POST-payments_apple_verifyiap.js @@ -26,9 +26,9 @@ describe('payments : apple #verify', () => { }); await user.post(endpoint, { - transaction: { - receipt: 'receipt', - }}); + transaction: { + receipt: 'receipt', + }}); expect(verifyStub).to.be.calledOnce; expect(verifyStub.args[0][0]._id).to.eql(user._id); diff --git a/test/api/v3/integration/quests/POST-groups_groupId_quests_accept.test.js b/test/api/v3/integration/quests/POST-groups_groupId_quests_accept.test.js index ecff25cfd3..9faa405054 100644 --- a/test/api/v3/integration/quests/POST-groups_groupId_quests_accept.test.js +++ b/test/api/v3/integration/quests/POST-groups_groupId_quests_accept.test.js @@ -33,20 +33,20 @@ describe('POST /groups/:groupId/quests/accept', () => { context('failure conditions', () => { it('does not accept quest without an invite', async () => { await expect(leader.post(`/groups/${questingGroup._id}/quests/accept`)) - .to.eventually.be.rejected.and.eql({ - code: 404, - error: 'NotFound', - message: t('questInviteNotFound'), - }); + .to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('questInviteNotFound'), + }); }); it('does not accept quest for a group in which user is not a member', async () => { await expect(user.post(`/groups/${questingGroup._id}/quests/accept`)) - .to.eventually.be.rejected.and.eql({ - code: 404, - error: 'NotFound', - message: t('groupNotFound'), - }); + .to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('groupNotFound'), + }); }); it('does not accept quest for a guild', async () => { @@ -55,11 +55,11 @@ describe('POST /groups/:groupId/quests/accept', () => { }); await expect(guildLeader.post(`/groups/${guild._id}/quests/accept`)) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('guildQuestsNotSupported'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('guildQuestsNotSupported'), + }); }); it('does not accept invite twice', async () => { @@ -67,11 +67,11 @@ describe('POST /groups/:groupId/quests/accept', () => { await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`); await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`)) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('questAlreadyAccepted'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('questAlreadyAccepted'), + }); }); it('clears the invalid invite from the user when the request fails', async () => { @@ -79,11 +79,11 @@ describe('POST /groups/:groupId/quests/accept', () => { await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`); await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`)) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('questAlreadyAccepted'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('questAlreadyAccepted'), + }); await partyMembers[0].sync(); expect(partyMembers[0].party.quest.RSVPNeeded).to.be.false; @@ -96,11 +96,11 @@ describe('POST /groups/:groupId/quests/accept', () => { await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`); await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`)) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('questAlreadyUnderway'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('questAlreadyUnderway'), + }); }); }); diff --git a/test/api/v3/integration/quests/POST-groups_groupId_quests_force-start.test.js b/test/api/v3/integration/quests/POST-groups_groupId_quests_force-start.test.js index 76497f50e9..bcbf6a6cf1 100644 --- a/test/api/v3/integration/quests/POST-groups_groupId_quests_force-start.test.js +++ b/test/api/v3/integration/quests/POST-groups_groupId_quests_force-start.test.js @@ -32,11 +32,11 @@ describe('POST /groups/:groupId/quests/force-start', () => { let nonMember = await generateUser(); await expect(nonMember.post(`/groups/${questingGroup._id}/quests/force-start`)) - .to.eventually.be.rejected.and.eql({ - code: 404, - error: 'NotFound', - message: t('groupNotFound'), - }); + .to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('groupNotFound'), + }); }); it('does not force start quest for a guild', async () => { @@ -45,20 +45,20 @@ describe('POST /groups/:groupId/quests/force-start', () => { }); await expect(guildLeader.post(`/groups/${guild._id}/quests/force-start`)) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('guildQuestsNotSupported'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('guildQuestsNotSupported'), + }); }); it('does not force start for a party without a pending quest', async () => { await expect(leader.post(`/groups/${questingGroup._id}/quests/force-start`)) - .to.eventually.be.rejected.and.eql({ - code: 404, - error: 'NotFound', - message: t('questNotPending'), - }); + .to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('questNotPending'), + }); }); it('does not force start for a quest already underway', async () => { @@ -69,22 +69,22 @@ describe('POST /groups/:groupId/quests/force-start', () => { await partyMembers[2].post(`/groups/${questingGroup._id}/quests/accept`); await expect(leader.post(`/groups/${questingGroup._id}/quests/force-start`)) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('questAlreadyUnderway'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('questAlreadyUnderway'), + }); }); it('does not allow non-quest leader or non-group leader to force start a quest', async () => { await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`); await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/force-start`)) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('questOrGroupLeaderOnlyStartQuest'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('questOrGroupLeaderOnlyStartQuest'), + }); }); }); diff --git a/test/api/v3/integration/quests/POST-groups_groupid_quests_abort.test.js b/test/api/v3/integration/quests/POST-groups_groupid_quests_abort.test.js index 9b5503736b..aa90c5940d 100644 --- a/test/api/v3/integration/quests/POST-groups_groupid_quests_abort.test.js +++ b/test/api/v3/integration/quests/POST-groups_groupid_quests_abort.test.js @@ -42,11 +42,11 @@ describe('POST /groups/:groupId/quests/abort', () => { it('returns an error for a group in which user is not a member', async () => { await expect(user.post(`/groups/${questingGroup._id}/quests/abort`)) - .to.eventually.be.rejected.and.eql({ - code: 404, - error: 'NotFound', - message: t('groupNotFound'), - }); + .to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('groupNotFound'), + }); }); it('returns an error when group is a guild', async () => { @@ -55,11 +55,11 @@ describe('POST /groups/:groupId/quests/abort', () => { }); await expect(guildLeader.post(`/groups/${guild._id}/quests/abort`)) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('guildQuestsNotSupported'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('guildQuestsNotSupported'), + }); }); it('returns an error when quest is not active', async () => { diff --git a/test/api/v3/integration/quests/POST-groups_groupid_quests_cancel.test.js b/test/api/v3/integration/quests/POST-groups_groupid_quests_cancel.test.js index 96d78c9bb4..3a5afe4254 100644 --- a/test/api/v3/integration/quests/POST-groups_groupid_quests_cancel.test.js +++ b/test/api/v3/integration/quests/POST-groups_groupid_quests_cancel.test.js @@ -41,11 +41,11 @@ describe('POST /groups/:groupId/quests/cancel', () => { it('does not reject quest for a group in which user is not a member', async () => { await expect(user.post(`/groups/${questingGroup._id}/quests/cancel`)) - .to.eventually.be.rejected.and.eql({ - code: 404, - error: 'NotFound', - message: t('groupNotFound'), - }); + .to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('groupNotFound'), + }); }); it('returns an error when group is a guild', async () => { @@ -54,11 +54,11 @@ describe('POST /groups/:groupId/quests/cancel', () => { }); await expect(guildLeader.post(`/groups/${guild._id}/quests/cancel`)) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('guildQuestsNotSupported'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('guildQuestsNotSupported'), + }); }); it('returns an error when group is not on a quest', async () => { @@ -74,11 +74,11 @@ describe('POST /groups/:groupId/quests/cancel', () => { await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`); await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/cancel`)) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('onlyLeaderCancelQuest'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('onlyLeaderCancelQuest'), + }); }); it('does not cancel a quest already underway', async () => { @@ -88,11 +88,11 @@ describe('POST /groups/:groupId/quests/cancel', () => { await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`); await expect(leader.post(`/groups/${questingGroup._id}/quests/cancel`)) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('cantCancelActiveQuest'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('cantCancelActiveQuest'), + }); }); }); diff --git a/test/api/v3/integration/quests/POST-groups_groupid_quests_leave.test.js b/test/api/v3/integration/quests/POST-groups_groupid_quests_leave.test.js index 89c65d0c73..8a2870681d 100644 --- a/test/api/v3/integration/quests/POST-groups_groupid_quests_leave.test.js +++ b/test/api/v3/integration/quests/POST-groups_groupid_quests_leave.test.js @@ -41,11 +41,11 @@ describe('POST /groups/:groupId/quests/leave', () => { it('returns an error for a group in which user is not a member', async () => { await expect(user.post(`/groups/${questingGroup._id}/quests/leave`)) - .to.eventually.be.rejected.and.eql({ - code: 404, - error: 'NotFound', - message: t('groupNotFound'), - }); + .to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('groupNotFound'), + }); }); it('returns an error when group is a guild', async () => { @@ -54,11 +54,11 @@ describe('POST /groups/:groupId/quests/leave', () => { }); await expect(guildLeader.post(`/groups/${guild._id}/quests/leave`)) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('guildQuestsNotSupported'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('guildQuestsNotSupported'), + }); }); it('returns an error when quest is not active', async () => { @@ -89,11 +89,11 @@ describe('POST /groups/:groupId/quests/leave', () => { await partyMembers[1].post(`/groups/${questingGroup._id}/quests/reject`); await expect(partyMembers[1].post(`/groups/${questingGroup._id}/quests/leave`)) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('notPartOfQuest'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('notPartOfQuest'), + }); }); }); diff --git a/test/api/v3/integration/quests/POST-groups_groupid_quests_reject.test.js b/test/api/v3/integration/quests/POST-groups_groupid_quests_reject.test.js index 46dfb5a88d..aacec2598b 100644 --- a/test/api/v3/integration/quests/POST-groups_groupid_quests_reject.test.js +++ b/test/api/v3/integration/quests/POST-groups_groupid_quests_reject.test.js @@ -42,11 +42,11 @@ describe('POST /groups/:groupId/quests/reject', () => { it('does not accept quest for a group in which user is not a member', async () => { await expect(user.post(`/groups/${questingGroup._id}/quests/accept`)) - .to.eventually.be.rejected.and.eql({ - code: 404, - error: 'NotFound', - message: t('groupNotFound'), - }); + .to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('groupNotFound'), + }); }); it('returns an error when group is a guild', async () => { @@ -55,11 +55,11 @@ describe('POST /groups/:groupId/quests/reject', () => { }); await expect(guildLeader.post(`/groups/${guild._id}/quests/reject`)) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('guildQuestsNotSupported'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('guildQuestsNotSupported'), + }); }); it('returns an error when group is not on a quest', async () => { @@ -76,11 +76,11 @@ describe('POST /groups/:groupId/quests/reject', () => { await partyMembers[0].post(`/groups/${questingGroup._id}/quests/reject`); await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/reject`)) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('questAlreadyRejected'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('questAlreadyRejected'), + }); }); it('clears the user rsvp needed if the request fails because the request is invalid', async () => { @@ -88,11 +88,11 @@ describe('POST /groups/:groupId/quests/reject', () => { await partyMembers[0].post(`/groups/${questingGroup._id}/quests/reject`); await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/reject`)) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('questAlreadyRejected'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('questAlreadyRejected'), + }); await partyMembers[0].sync(); expect(partyMembers[0].party.quest.RSVPNeeded).to.be.false; @@ -103,11 +103,11 @@ describe('POST /groups/:groupId/quests/reject', () => { await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`); await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/reject`)) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('questAlreadyAccepted'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('questAlreadyAccepted'), + }); }); it('does not reject invite for a quest already underway', async () => { @@ -117,11 +117,11 @@ describe('POST /groups/:groupId/quests/reject', () => { await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`); await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/reject`)) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('questAlreadyUnderway'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('questAlreadyUnderway'), + }); }); }); diff --git a/test/api/v3/integration/tasks/POST-tasks_id_score_direction.test.js b/test/api/v3/integration/tasks/POST-tasks_id_score_direction.test.js index fa435c5e31..458fed6f10 100644 --- a/test/api/v3/integration/tasks/POST-tasks_id_score_direction.test.js +++ b/test/api/v3/integration/tasks/POST-tasks_id_score_direction.test.js @@ -141,11 +141,11 @@ describe('POST /tasks/:id/score/:direction', () => { it('doesn\'t let a todo be completed twice', async () => { await user.post(`/tasks/${todo._id}/score/up`); await expect(user.post(`/tasks/${todo._id}/score/up`)) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('sessionOutdated'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('sessionOutdated'), + }); }); it('doesn\'t let a todo be uncompleted twice', async () => { @@ -380,11 +380,11 @@ describe('POST /tasks/:id/score/:direction', () => { await expect(user.post(`/tasks/${habit._id}/score/up`, { scoreNotes: scoreNotesString, })) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('taskScoreNotesTooLong'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('taskScoreNotesTooLong'), + }); }); }); diff --git a/test/api/v3/integration/tasks/POST-tasks_unlink-all_challengeId.test.js b/test/api/v3/integration/tasks/POST-tasks_unlink-all_challengeId.test.js index ee60541017..cd8bec5acc 100644 --- a/test/api/v3/integration/tasks/POST-tasks_unlink-all_challengeId.test.js +++ b/test/api/v3/integration/tasks/POST-tasks_unlink-all_challengeId.test.js @@ -41,30 +41,30 @@ describe('POST /tasks/unlink-all/:challengeId', () => { it('fails if no keep query', async () => { await expect(user.post(`/tasks/unlink-all/${challenge._id}`)) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('invalidReqParams'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('invalidReqParams'), + }); }); it('fails if invalid challenge id', async () => { await expect(user.post('/tasks/unlink-all/123?keep=remove-all')) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('invalidReqParams'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('invalidReqParams'), + }); }); it('fails on an unbroken challenge', async () => { await user.post(`/tasks/challenge/${challenge._id}`, tasksToTest.daily); await expect(user.post(`/tasks/unlink-all/${challenge._id}?keep=remove-all`)) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('cantOnlyUnlinkChalTask'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('cantOnlyUnlinkChalTask'), + }); }); it('unlinks all tasks from a challenge and deletes them on keep=remove-all', async () => { @@ -87,7 +87,7 @@ describe('POST /tasks/unlink-all/:challengeId', () => { const daily = await user.post(`/tasks/challenge/${challenge._id}`, tasksToTest.daily); const anotherUser = await generateUser(); await user.post(`/groups/${guild._id}/invite`, { - uuids: [anotherUser._id], + uuids: [anotherUser._id], }); // Have the second user join the group and challenge await anotherUser.post(`/groups/${guild._id}/join`); diff --git a/test/api/v3/integration/tasks/POST-tasks_unlink-one_taskId.test.js b/test/api/v3/integration/tasks/POST-tasks_unlink-one_taskId.test.js index 570f46c92e..fb67ec1451 100644 --- a/test/api/v3/integration/tasks/POST-tasks_unlink-one_taskId.test.js +++ b/test/api/v3/integration/tasks/POST-tasks_unlink-one_taskId.test.js @@ -43,50 +43,50 @@ describe('POST /tasks/unlink-one/:taskId', () => { it('fails if no keep query', async () => { const daily = await user.post(`/tasks/challenge/${challenge._id}`, tasksToTest.daily); await expect(user.post(`/tasks/unlink-one/${daily._id}`)) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('invalidReqParams'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('invalidReqParams'), + }); }); it('fails if invalid task id', async () => { await expect(user.post('/tasks/unlink-one/123?keep=remove')) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('invalidReqParams'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('invalidReqParams'), + }); }); it('fails on task not found', async () => { await expect(user.post(`/tasks/unlink-one/${generateUUID()}?keep=keep`)) - .to.eventually.be.rejected.and.eql({ - code: 404, - error: 'NotFound', - message: t('taskNotFound'), - }); + .to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('taskNotFound'), + }); }); it('fails on task unlinked to challenge', async () => { let daily = await user.post('/tasks/user', tasksToTest.daily); await expect(user.post(`/tasks/unlink-one/${daily._id}?keep=keep`)) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('cantOnlyUnlinkChalTask'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('cantOnlyUnlinkChalTask'), + }); }); it('fails on unbroken challenge', async () => { await user.post(`/tasks/challenge/${challenge._id}`, tasksToTest.daily); let [daily] = await user.get('/tasks/user'); await expect(user.post(`/tasks/unlink-one/${daily._id}?keep=keep`)) - .to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('cantOnlyUnlinkChalTask'), - }); + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('cantOnlyUnlinkChalTask'), + }); }); it('unlinks a task from a challenge and saves it on keep=keep', async () => { diff --git a/test/api/v3/integration/tasks/PUT-tasks_id.test.js b/test/api/v3/integration/tasks/PUT-tasks_id.test.js index b2b802f837..aba6c254cf 100644 --- a/test/api/v3/integration/tasks/PUT-tasks_id.test.js +++ b/test/api/v3/integration/tasks/PUT-tasks_id.test.js @@ -46,7 +46,7 @@ describe('PUT /tasks/:id', () => { expect(savedTask.userId).to.equal(task.userId); expect(savedTask.history).to.eql(task.history); expect(savedTask.createdAt).to.equal(task.createdAt); - expect(savedTask.updatedAt).to.be.greaterThan(task.updatedAt); + expect(new Date(savedTask.updatedAt)).to.be.greaterThan(new Date(task.updatedAt)); expect(savedTask.challenge).to.eql(task.challenge); expect(savedTask.completed).to.eql(task.completed); expect(savedTask.streak).to.equal(savedTask.streak); // it's an habit, dailies can change it @@ -54,7 +54,7 @@ describe('PUT /tasks/:id', () => { }); it('ignores invalid fields', async () => { - let savedTask = await user.put(`/tasks/${task._id}`, { + let savedTask = await user.put(`/tasks/${task._id}`, { notValid: true, }); @@ -133,7 +133,7 @@ describe('PUT /tasks/:id', () => { expect(savedChallengeUserTask.text).to.equal(challengeUserTask.text); expect(savedChallengeUserTask.history).to.eql(challengeUserTask.history); expect(savedChallengeUserTask.createdAt).to.equal(challengeUserTask.createdAt); - expect(savedChallengeUserTask.updatedAt).to.be.greaterThan(challengeUserTask.updatedAt); + expect(new Date(savedChallengeUserTask.updatedAt)).to.be.greaterThan(new Date(challengeUserTask.updatedAt)); expect(savedChallengeUserTask.challenge).to.eql(challengeUserTask.challenge); expect(savedChallengeUserTask.completed).to.equal(challengeUserTask.completed); expect(savedChallengeUserTask.dateCompleted).to.equal(challengeUserTask.dateCompleted); diff --git a/test/api/v3/integration/tasks/challenges/POST-tasks_challenge_challengeId_taskId_checklist.test.js b/test/api/v3/integration/tasks/challenges/POST-tasks_challenge_challengeId_taskId_checklist.test.js index 06bc7b68b2..ec4d09f7b3 100644 --- a/test/api/v3/integration/tasks/challenges/POST-tasks_challenge_challengeId_taskId_checklist.test.js +++ b/test/api/v3/integration/tasks/challenges/POST-tasks_challenge_challengeId_taskId_checklist.test.js @@ -40,11 +40,11 @@ describe('POST /tasks/:taskId/checklist/', () => { ignored: false, _id: 123, })) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('onlyChalLeaderEditTasks'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('onlyChalLeaderEditTasks'), + }); }); it('adds a checklist item to a daily', async () => { diff --git a/test/api/v3/integration/tasks/challenges/PUT-tasks_challenge_challengeId.test.js b/test/api/v3/integration/tasks/challenges/PUT-tasks_challenge_challengeId.test.js index bd3be00508..94d96367b8 100644 --- a/test/api/v3/integration/tasks/challenges/PUT-tasks_challenge_challengeId.test.js +++ b/test/api/v3/integration/tasks/challenges/PUT-tasks_challenge_challengeId.test.js @@ -98,7 +98,7 @@ describe('PUT /tasks/:id', () => { expect(savedTask.userId).to.equal(task.userId); expect(savedTask.history).to.eql(task.history); expect(savedTask.createdAt).to.equal(task.createdAt); - expect(savedTask.updatedAt).to.be.greaterThan(task.updatedAt); + expect(new Date(savedTask.updatedAt)).to.be.greaterThan(new Date(task.updatedAt)); expect(savedTask.challenge._id).to.equal(task.challenge._id); expect(savedTask.completed).to.equal(task.completed); expect(savedTask.streak).to.equal(task.streak); diff --git a/test/api/v3/integration/tasks/challenges/PUT-tasks_challenge_challengeId_tasksId_checklist_itemId.test.js b/test/api/v3/integration/tasks/challenges/PUT-tasks_challenge_challengeId_tasksId_checklist_itemId.test.js index 4dc2ceaa3e..cce7ce79f3 100644 --- a/test/api/v3/integration/tasks/challenges/PUT-tasks_challenge_challengeId_tasksId_checklist_itemId.test.js +++ b/test/api/v3/integration/tasks/challenges/PUT-tasks_challenge_challengeId_tasksId_checklist_itemId.test.js @@ -28,11 +28,11 @@ describe('PUT /tasks/:taskId/checklist/:itemId', () => { completed: true, _id: 123, // ignored })) - .to.eventually.be.rejected.and.eql({ - code: 404, - error: 'NotFound', - message: t('checklistItemNotFound'), - }); + .to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('checklistItemNotFound'), + }); }); it('returns error when user is not a member of the challenge', async () => { @@ -53,11 +53,11 @@ describe('PUT /tasks/:taskId/checklist/:itemId', () => { completed: true, _id: 123, // ignored })) - .to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('onlyChalLeaderEditTasks'), - }); + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('onlyChalLeaderEditTasks'), + }); }); it('updates a checklist item on dailies', async () => { diff --git a/test/api/v3/integration/tasks/groups/tags/POST-tasks_taskId_tags.test.js b/test/api/v3/integration/tasks/groups/tags/POST-tasks_taskId_tags.test.js index bfa5f31840..ead583d122 100644 --- a/test/api/v3/integration/tasks/groups/tags/POST-tasks_taskId_tags.test.js +++ b/test/api/v3/integration/tasks/groups/tags/POST-tasks_taskId_tags.test.js @@ -3,6 +3,7 @@ import { translate as t, } from '../../../../../../helpers/api-integration/v3'; import { v4 as generateUUID } from 'uuid'; + // Currently we do not support adding tags to group original tasks, but if we do in the future, these tests will check xdescribe('POST group /tasks/:taskId/tags/:tagId', () => { let user, guild, task; diff --git a/test/api/v3/integration/user/POST-user_release_both.test.js b/test/api/v3/integration/user/POST-user_release_both.test.js index 047ea5a2c9..ce8e5246dd 100644 --- a/test/api/v3/integration/user/POST-user_release_both.test.js +++ b/test/api/v3/integration/user/POST-user_release_both.test.js @@ -54,9 +54,9 @@ describe('POST /user/release-both', () => { expect(response.message).to.equal(t('mountsAndPetsReleased')); expect(user.balance).to.equal(0); - expect(user.items.currentMount).to.be.empty; - expect(user.items.currentPet).to.be.empty; - expect(user.items.pets[animal]).to.be.empty; + expect(user.items.currentMount).to.equal(''); + expect(user.items.currentPet).to.equal(''); + expect(user.items.pets[animal]).to.equal(0); expect(user.items.mounts[animal]).to.equal(null); expect(user.achievements.beastMasterCount).to.equal(1); expect(user.achievements.mountMasterCount).to.equal(1); diff --git a/test/api/v3/integration/user/auth/DELETE-user_auth_social_network.test.js b/test/api/v3/integration/user/auth/DELETE-user_auth_social_network.test.js index ae4fde82a9..55c5522322 100644 --- a/test/api/v3/integration/user/auth/DELETE-user_auth_social_network.test.js +++ b/test/api/v3/integration/user/auth/DELETE-user_auth_social_network.test.js @@ -41,7 +41,7 @@ describe('DELETE social registration', () => { let response = await user.del('/user/auth/social/facebook'); expect(response).to.eql({}); await user.sync(); - expect(user.auth.facebook).to.be.empty; + expect(user.auth.facebook).to.be.undefined; }); it('succeeds if user has a google registration', async () => { @@ -54,7 +54,7 @@ describe('DELETE social registration', () => { let response = await user.del('/user/auth/social/facebook'); expect(response).to.eql({}); await user.sync(); - expect(user.auth.facebook).to.be.empty; + expect(user.auth.facebook).to.be.undefined; }); }); @@ -79,7 +79,7 @@ describe('DELETE social registration', () => { let response = await user.del('/user/auth/social/google'); expect(response).to.eql({}); await user.sync(); - expect(user.auth.google).to.be.empty; + expect(user.auth.google).to.be.undefined; }); it('succeeds if user has a facebook registration', async () => { @@ -92,7 +92,7 @@ describe('DELETE social registration', () => { let response = await user.del('/user/auth/social/google'); expect(response).to.eql({}); await user.sync(); - expect(user.auth.google).to.be.empty; + expect(user.auth.goodl).to.be.undefined; }); }); }); diff --git a/test/api/v3/integration/user/auth/POST-register_local.test.js b/test/api/v3/integration/user/auth/POST-register_local.test.js index 259d848c05..1ad091a79e 100644 --- a/test/api/v3/integration/user/auth/POST-register_local.test.js +++ b/test/api/v3/integration/user/auth/POST-register_local.test.js @@ -579,7 +579,7 @@ describe('POST /user/auth/local/register', () => { it('adds a user to a guild on an invite of type other than party', async () => { let { group, groupLeader } = await createAndPopulateGroup({ - groupDetails: { type: 'guild', privacy: 'private' }, + groupDetails: { type: 'guild', privacy: 'private' }, }); let invite = encrypt(JSON.stringify({ diff --git a/test/api/v3/integration/world-state/GET-world-state.test.js b/test/api/v3/integration/world-state/GET-world-state.test.js index fac08ad639..5c91062769 100644 --- a/test/api/v3/integration/world-state/GET-world-state.test.js +++ b/test/api/v3/integration/world-state/GET-world-state.test.js @@ -19,7 +19,7 @@ describe('GET /world-state', () => { await updateDocument('groups', {_id: TAVERN_ID}, {quest: {active: true, key: 'dysheartener', progress: {hp: 50000, rage: 9999}}}); const res = await requester().get('/world-state'); - expect(res).to.have.deep.property('worldBoss'); + expect(res).to.have.nested.property('worldBoss'); expect(res.worldBoss).to.eql({ active: true, diff --git a/test/api/v3/unit/libs/apiMessages.js b/test/api/v3/unit/libs/apiMessages.js index 09ff86f5ba..3d2ac2fad1 100644 --- a/test/api/v3/unit/libs/apiMessages.js +++ b/test/api/v3/unit/libs/apiMessages.js @@ -14,7 +14,7 @@ describe('API Messages', () => { let vars = {a: 1}; sandbox.stub(_, 'clone').returns({}); apiMessages('guildsOnlyPaginate', vars); - expect(_.clone).to.have.been.called.once; + expect(_.clone).to.have.been.calledOnce; expect(_.clone).to.have.been.calledWith(vars); }); @@ -23,9 +23,9 @@ describe('API Messages', () => { let stub = sinon.stub().returns('string'); sandbox.stub(_, 'template').returns(stub); apiMessages('guildsOnlyPaginate', vars); - expect(_.template).to.have.been.called.once; + expect(_.template).to.have.been.calledOnce; expect(_.template).to.have.been.calledWith(message); - expect(stub).to.have.been.called.once; + expect(stub).to.have.been.calledOnce; expect(stub).to.have.been.calledWith(vars); }); }); diff --git a/test/api/v3/unit/libs/collectionManipulators.test.js b/test/api/v3/unit/libs/collectionManipulators.test.js index a2bc6e71c4..2736b37502 100644 --- a/test/api/v3/unit/libs/collectionManipulators.test.js +++ b/test/api/v3/unit/libs/collectionManipulators.test.js @@ -1,6 +1,6 @@ import mongoose from 'mongoose'; import { - removeFromArray, + removeFromArray, } from '../../../../../website/server/libs/collectionManipulators'; describe('Collection Manipulators', () => { diff --git a/test/api/v3/unit/libs/cron.test.js b/test/api/v3/unit/libs/cron.test.js index 2fefcbf483..76990aa20a 100644 --- a/test/api/v3/unit/libs/cron.test.js +++ b/test/api/v3/unit/libs/cron.test.js @@ -181,9 +181,9 @@ describe('cron', () => { cron({user, tasksByType, daysMissed, analytics}); expect(user.purchased.plan.customerId).to.not.exist; - expect(user.purchased.plan.consecutive.gemCapExtra).to.be.empty; - expect(user.purchased.plan.consecutive.count).to.be.empty; - expect(user.purchased.plan.consecutive.offset).to.be.empty; + expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(0); + expect(user.purchased.plan.consecutive.count).to.equal(0); + expect(user.purchased.plan.consecutive.offset).to.equal(0); }); }); @@ -1027,7 +1027,7 @@ describe('cron', () => { cron({user, tasksByType, daysMissed, analytics}); expect(user.party.quest.progress.up).to.equal(0); expect(user.party.quest.progress.down).to.equal(0); - expect(user.party.quest.progress.collectedItems).to.be.empty; + expect(user.party.quest.progress.collectedItems).to.equal(0); }); it('applies the user progress', () => { diff --git a/test/api/v3/unit/libs/payments/amazon/checkout.test.js b/test/api/v3/unit/libs/payments/amazon/checkout.test.js index 9e74cdb145..0581c76772 100644 --- a/test/api/v3/unit/libs/payments/amazon/checkout.test.js +++ b/test/api/v3/unit/libs/payments/amazon/checkout.test.js @@ -132,11 +132,11 @@ describe('Amazon Payments - Checkout', () => { }; await expect(amzLib.checkout({gift, user, orderReferenceId, headers})) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 400, - message: 'Amount must be at least 1.', - name: 'BadRequest', - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 400, + message: 'Amount must be at least 1.', + name: 'BadRequest', + }); }); it('should error if user cannot get gems gems', async () => { diff --git a/test/api/v3/unit/libs/payments/amazon/subscribe.test.js b/test/api/v3/unit/libs/payments/amazon/subscribe.test.js index 8095f90c63..930c64d06a 100644 --- a/test/api/v3/unit/libs/payments/amazon/subscribe.test.js +++ b/test/api/v3/unit/libs/payments/amazon/subscribe.test.js @@ -123,11 +123,11 @@ describe('Amazon Payments - Subscribe', () => { groupId, headers, })) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 400, - name: 'BadRequest', - message: i18n.t('missingSubscriptionCode'), - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 400, + name: 'BadRequest', + message: i18n.t('missingSubscriptionCode'), + }); }); it('should throw an error if we are missing a billingAgreementId', async () => { @@ -138,11 +138,11 @@ describe('Amazon Payments - Subscribe', () => { groupId, headers, })) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 400, - name: 'BadRequest', - message: 'Missing req.body.billingAgreementId', - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 400, + name: 'BadRequest', + message: 'Missing req.body.billingAgreementId', + }); }); it('should throw an error when coupon code is missing', async () => { @@ -156,11 +156,11 @@ describe('Amazon Payments - Subscribe', () => { groupId, headers, })) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 400, - name: 'BadRequest', - message: i18n.t('couponCodeRequired'), - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 400, + name: 'BadRequest', + message: i18n.t('couponCodeRequired'), + }); }); it('should throw an error when coupon code is invalid', async () => { @@ -182,11 +182,11 @@ describe('Amazon Payments - Subscribe', () => { groupId, headers, })) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 401, - name: 'NotAuthorized', - message: i18n.t('invalidCoupon'), - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 401, + name: 'NotAuthorized', + message: i18n.t('invalidCoupon'), + }); cc.validate.restore(); }); diff --git a/test/api/v3/unit/libs/payments/group-plans/group-payments-create.test.js b/test/api/v3/unit/libs/payments/group-plans/group-payments-create.test.js index 96bcde4134..72489aa4df 100644 --- a/test/api/v3/unit/libs/payments/group-plans/group-payments-create.test.js +++ b/test/api/v3/unit/libs/payments/group-plans/group-payments-create.test.js @@ -305,7 +305,7 @@ describe('Purchasing a group plan for group', () => { await api.createSubscription(data); - expect(sender.sendTxn).to.be.calledFourTimes; + expect(sender.sendTxn).to.have.callCount(4); expect(sender.sendTxn.args[0][0]._id).to.equal(TECH_ASSISTANCE_EMAIL); expect(sender.sendTxn.args[0][1]).to.equal('admin-user-subscription-details'); expect(sender.sendTxn.args[1][0]._id).to.equal(recipient._id); @@ -338,7 +338,7 @@ describe('Purchasing a group plan for group', () => { await api.createSubscription(data); - expect(sender.sendTxn).to.be.calledFourTimes; + expect(sender.sendTxn).to.have.callCount(4); expect(sender.sendTxn.args[0][0]._id).to.equal(TECH_ASSISTANCE_EMAIL); expect(sender.sendTxn.args[0][1]).to.equal('admin-user-subscription-details'); expect(sender.sendTxn.args[1][0]._id).to.equal(recipient._id); diff --git a/test/api/v3/unit/libs/payments/paypal/checkout.test.js b/test/api/v3/unit/libs/payments/paypal/checkout.test.js index 9ed40c1ad8..aaa83535ee 100644 --- a/test/api/v3/unit/libs/payments/paypal/checkout.test.js +++ b/test/api/v3/unit/libs/payments/paypal/checkout.test.js @@ -71,11 +71,11 @@ describe('checkout', () => { }; await expect(paypalPayments.checkout({gift})) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 400, - message: 'Amount must be at least 1.', - name: 'BadRequest', - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 400, + message: 'Amount must be at least 1.', + name: 'BadRequest', + }); }); it('should error if the user cannot get gems', async () => { diff --git a/test/api/v3/unit/libs/payments/stripe/cancel-subscription.test.js b/test/api/v3/unit/libs/payments/stripe/cancel-subscription.test.js index 2e09014a39..d062ad0b89 100644 --- a/test/api/v3/unit/libs/payments/stripe/cancel-subscription.test.js +++ b/test/api/v3/unit/libs/payments/stripe/cancel-subscription.test.js @@ -42,11 +42,11 @@ describe('cancel subscription', () => { user, groupId: undefined, })) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 401, - name: 'NotAuthorized', - message: i18n.t('missingSubscription'), - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 401, + name: 'NotAuthorized', + message: i18n.t('missingSubscription'), + }); }); it('throws an error if the group is not found', async () => { @@ -54,11 +54,11 @@ describe('cancel subscription', () => { user, groupId: 'fake-group', })) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 404, - name: 'NotFound', - message: i18n.t('groupNotFound'), - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 404, + name: 'NotFound', + message: i18n.t('groupNotFound'), + }); }); it('throws an error if user is not the group leader', async () => { @@ -70,11 +70,11 @@ describe('cancel subscription', () => { user: nonLeader, groupId, })) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 401, - name: 'NotAuthorized', - message: i18n.t('onlyGroupLeaderCanManageSubscription'), - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 401, + name: 'NotAuthorized', + message: i18n.t('onlyGroupLeaderCanManageSubscription'), + }); }); describe('success', () => { diff --git a/test/api/v3/unit/libs/payments/stripe/checkout-subscription.test.js b/test/api/v3/unit/libs/payments/stripe/checkout-subscription.test.js index d1d908c7fa..7271b1dbd3 100644 --- a/test/api/v3/unit/libs/payments/stripe/checkout-subscription.test.js +++ b/test/api/v3/unit/libs/payments/stripe/checkout-subscription.test.js @@ -88,11 +88,11 @@ describe('checkout with subscription', () => { headers, coupon, })) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 400, - name: 'BadRequest', - message: 'Missing req.body.id', - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 400, + name: 'BadRequest', + message: 'Missing req.body.id', + }); }); it('should throw an error when coupon code is missing', async () => { @@ -108,11 +108,11 @@ describe('checkout with subscription', () => { headers, coupon, })) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 400, - name: 'BadRequest', - message: i18n.t('couponCodeRequired'), - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 400, + name: 'BadRequest', + message: i18n.t('couponCodeRequired'), + }); }); it('should throw an error when coupon code is invalid', async () => { @@ -136,11 +136,11 @@ describe('checkout with subscription', () => { headers, coupon, })) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 400, - name: 'BadRequest', - message: i18n.t('invalidCoupon'), - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 400, + name: 'BadRequest', + message: i18n.t('invalidCoupon'), + }); cc.validate.restore(); }); diff --git a/test/api/v3/unit/libs/payments/stripe/checkout.test.js b/test/api/v3/unit/libs/payments/stripe/checkout.test.js index f784bca300..b8f398ef6e 100644 --- a/test/api/v3/unit/libs/payments/stripe/checkout.test.js +++ b/test/api/v3/unit/libs/payments/stripe/checkout.test.js @@ -57,11 +57,11 @@ describe('checkout', () => { headers, coupon, }, stripe)) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 400, - message: 'Amount must be at least 1.', - name: 'BadRequest', - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 400, + message: 'Amount must be at least 1.', + name: 'BadRequest', + }); }); diff --git a/test/api/v3/unit/libs/payments/stripe/edit-subscription.test.js b/test/api/v3/unit/libs/payments/stripe/edit-subscription.test.js index 9c58c4bae5..40153f8496 100644 --- a/test/api/v3/unit/libs/payments/stripe/edit-subscription.test.js +++ b/test/api/v3/unit/libs/payments/stripe/edit-subscription.test.js @@ -43,11 +43,11 @@ describe('edit subscription', () => { user, groupId: undefined, })) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 401, - name: 'NotAuthorized', - message: i18n.t('missingSubscription'), - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 401, + name: 'NotAuthorized', + message: i18n.t('missingSubscription'), + }); }); it('throws an error if a token is not provided', async () => { @@ -55,11 +55,11 @@ describe('edit subscription', () => { user, groupId: undefined, })) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 400, - name: 'BadRequest', - message: 'Missing req.body.id', - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 400, + name: 'BadRequest', + message: 'Missing req.body.id', + }); }); it('throws an error if the group is not found', async () => { @@ -68,11 +68,11 @@ describe('edit subscription', () => { user, groupId: 'fake-group', })) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 404, - name: 'NotFound', - message: i18n.t('groupNotFound'), - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 404, + name: 'NotFound', + message: i18n.t('groupNotFound'), + }); }); it('throws an error if user is not the group leader', async () => { @@ -85,11 +85,11 @@ describe('edit subscription', () => { user: nonLeader, groupId, })) - .to.eventually.be.rejected.and.to.eql({ - httpCode: 401, - name: 'NotAuthorized', - message: i18n.t('onlyGroupLeaderCanManageSubscription'), - }); + .to.eventually.be.rejected.and.to.eql({ + httpCode: 401, + name: 'NotAuthorized', + message: i18n.t('onlyGroupLeaderCanManageSubscription'), + }); }); describe('success', () => { diff --git a/test/api/v3/unit/libs/payments/stripe/handle-webhook.test.js b/test/api/v3/unit/libs/payments/stripe/handle-webhook.test.js index 762e840c8a..f8786b2718 100644 --- a/test/api/v3/unit/libs/payments/stripe/handle-webhook.test.js +++ b/test/api/v3/unit/libs/payments/stripe/handle-webhook.test.js @@ -34,7 +34,7 @@ describe('Stripe - Webhooks', () => { it('logs an error if an unsupported webhook event is passed', async () => { const error = new Error(`Missing handler for Stripe webhook ${eventType}`); await stripePayments.handleWebhooks({requestBody: event}, stripe); - expect(logger.error).to.have.been.called.once; + expect(logger.error).to.have.been.calledOnce; const calledWith = logger.error.getCall(0).args; expect(calledWith[0].message).to.equal(error.message); @@ -43,7 +43,7 @@ describe('Stripe - Webhooks', () => { it('retrieves and validates the event from Stripe', async () => { await stripePayments.handleWebhooks({requestBody: event}, stripe); - expect(stripe.events.retrieve).to.have.been.called.once; + expect(stripe.events.retrieve).to.have.been.calledOnce; expect(stripe.events.retrieve).to.have.been.calledWith(event.id); }); }); @@ -70,7 +70,7 @@ describe('Stripe - Webhooks', () => { await stripePayments.handleWebhooks({requestBody: {}}, stripe); - expect(stripe.events.retrieve).to.have.been.called.once; + expect(stripe.events.retrieve).to.have.been.calledOnce; expect(stripe.customers.del).to.not.have.been.called; expect(payments.cancelSubscription).to.not.have.been.called; stripe.events.retrieve.restore(); diff --git a/test/api/v3/unit/libs/taskManager.js b/test/api/v3/unit/libs/taskManager.js index 546fcbe19f..14dc8feb49 100644 --- a/test/api/v3/unit/libs/taskManager.js +++ b/test/api/v3/unit/libs/taskManager.js @@ -54,7 +54,7 @@ describe('taskManager', () => { expect(newTask.type).to.equal(testHabit.type); expect(newTask.up).to.equal(testHabit.up); expect(newTask.down).to.equal(testHabit.down); - expect(newTask.createdAt).isNotEmtpy; + expect(newTask.createdAt).to.exist; }); it('gets user tasks', async () => { @@ -75,7 +75,7 @@ describe('taskManager', () => { expect(task.type).to.equal(testHabit.type); expect(task.up).to.equal(testHabit.up); expect(task.down).to.equal(testHabit.down); - expect(task.createdAt).isNotEmtpy; + expect(task.createdAt).to.exist; }); it('creates group tasks', async () => { @@ -89,7 +89,7 @@ describe('taskManager', () => { expect(newTask.type).to.equal(testHabit.type); expect(newTask.up).to.equal(testHabit.up); expect(newTask.down).to.equal(testHabit.down); - expect(newTask.createdAt).isNotEmtpy; + expect(newTask.createdAt).to.exist; expect(newTask.group.id).to.equal(group._id); }); @@ -111,7 +111,7 @@ describe('taskManager', () => { expect(task.type).to.equal(testHabit.type); expect(task.up).to.equal(testHabit.up); expect(task.down).to.equal(testHabit.down); - expect(task.createdAt).isNotEmtpy; + expect(task.createdAt).to.exist; expect(task.group.id).to.equal(group._id); }); @@ -126,7 +126,7 @@ describe('taskManager', () => { expect(newTask.type).to.equal(testHabit.type); expect(newTask.up).to.equal(testHabit.up); expect(newTask.down).to.equal(testHabit.down); - expect(newTask.createdAt).isNotEmtpy; + expect(newTask.createdAt).to.exist; expect(newTask.challenge.id).to.equal(challenge._id); }); @@ -148,7 +148,7 @@ describe('taskManager', () => { expect(task.type).to.equal(testHabit.type); expect(task.up).to.equal(testHabit.up); expect(task.down).to.equal(testHabit.down); - expect(task.createdAt).isNotEmtpy; + expect(task.createdAt).to.exist; expect(task.challenge.id).to.equal(challenge._id); }); @@ -171,7 +171,7 @@ describe('taskManager', () => { expect(syncableTask.updatedAt).to.not.exist; }); - it('moves tasks to a specified position', async() => { + it('moves tasks to a specified position', async () => { let order = ['task-id-1', 'task-id-2']; moveTask(order, 'task-id-2', 0); diff --git a/test/api/v3/unit/middlewares/cors.test.js b/test/api/v3/unit/middlewares/cors.test.js index 8ece1e9ca2..8cf6dbc3ff 100644 --- a/test/api/v3/unit/middlewares/cors.test.js +++ b/test/api/v3/unit/middlewares/cors.test.js @@ -23,7 +23,7 @@ describe('cors middleware', () => { 'Access-Control-Allow-Headers': 'Content-Type,Accept,Content-Encoding,X-Requested-With,x-api-user,x-api-key,x-client', }); expect(res.sendStatus).to.not.have.been.called; - expect(next).to.have.been.called.once; + expect(next).to.have.been.calledOnce; }); it('responds immediately if method is OPTIONS', () => { diff --git a/test/api/v3/unit/middlewares/cronMiddleware.js b/test/api/v3/unit/middlewares/cronMiddleware.js index fd38079691..12209b1a87 100644 --- a/test/api/v3/unit/middlewares/cronMiddleware.js +++ b/test/api/v3/unit/middlewares/cronMiddleware.js @@ -36,12 +36,12 @@ describe('cron middleware', () => { }); user.save() - .then(savedUser => { - res.locals.user = savedUser; - res.analytics = analyticsService; - done(); - }) - .catch(done); + .then(savedUser => { + res.locals.user = savedUser; + res.analytics = analyticsService; + done(); + }) + .catch(done); }); afterEach(() => { diff --git a/test/api/v3/unit/middlewares/maintenanceMode.test.js b/test/api/v3/unit/middlewares/maintenanceMode.test.js index 857fb2c941..566d572206 100644 --- a/test/api/v3/unit/middlewares/maintenanceMode.test.js +++ b/test/api/v3/unit/middlewares/maintenanceMode.test.js @@ -22,7 +22,7 @@ describe('maintenance mode middleware', () => { attachMaintenanceMode(req, res, next); - expect(next).to.have.been.called.once; + expect(next).to.have.been.calledOnce; expect(res.status).to.not.have.been.called; }); diff --git a/test/api/v3/unit/middlewares/redirects.js b/test/api/v3/unit/middlewares/redirects.js index ed210c169a..184d60b706 100644 --- a/test/api/v3/unit/middlewares/redirects.js +++ b/test/api/v3/unit/middlewares/redirects.js @@ -43,7 +43,7 @@ describe('redirects middleware', () => { attachRedirects.forceSSL(req, res, next); - expect(res.redirect).to.be.notCalled; + expect(res.redirect).to.have.not.been.called; }); it('does not redirect outside of production environments', () => { @@ -57,7 +57,7 @@ describe('redirects middleware', () => { attachRedirects.forceSSL(req, res, next); - expect(res.redirect).to.be.notCalled; + expect(res.redirect).to.have.not.been.called; }); it('does not redirect if base URL is not https', () => { @@ -71,7 +71,7 @@ describe('redirects middleware', () => { attachRedirects.forceSSL(req, res, next); - expect(res.redirect).to.be.notCalled; + expect(res.redirect).to.have.not.been.called; }); }); @@ -108,7 +108,7 @@ describe('redirects middleware', () => { attachRedirects.forceHabitica(req, res, next); - expect(res.redirect).to.be.notCalled; + expect(res.redirect).to.have.not.been.called; }); it('does not redirect if env is set to ignore redirection', () => { @@ -125,7 +125,7 @@ describe('redirects middleware', () => { attachRedirects.forceHabitica(req, res, next); - expect(res.redirect).to.be.notCalled; + expect(res.redirect).to.have.not.been.called; }); it('does not redirect if request hostname matches base URL host', () => { @@ -142,7 +142,7 @@ describe('redirects middleware', () => { attachRedirects.forceHabitica(req, res, next); - expect(res.redirect).to.be.notCalled; + expect(res.redirect).to.have.not.been.called; }); it('does not redirect if request is an API URL', () => { @@ -159,7 +159,7 @@ describe('redirects middleware', () => { attachRedirects.forceHabitica(req, res, next); - expect(res.redirect).to.be.notCalled; + expect(res.redirect).to.have.not.been.called; }); it('does not redirect if request method is not GET', () => { @@ -176,7 +176,7 @@ describe('redirects middleware', () => { attachRedirects.forceHabitica(req, res, next); - expect(res.redirect).to.be.notCalled; + expect(res.redirect).to.have.not.been.called; }); }); }); diff --git a/test/api/v3/unit/models/challenge.test.js b/test/api/v3/unit/models/challenge.test.js index 18e7de7bcf..a27d8b65a7 100644 --- a/test/api/v3/unit/models/challenge.test.js +++ b/test/api/v3/unit/models/challenge.test.js @@ -65,7 +65,7 @@ describe('Challenge Model', () => { each(tasksToTest, (taskValue, taskType) => { context(`${taskType}`, () => { - beforeEach(async() => { + beforeEach(async () => { task = new Tasks[`${taskType}`](Tasks.Task.sanitize(taskValue)); task.challenge.id = challenge._id; await task.save(); @@ -194,7 +194,7 @@ describe('Challenge Model', () => { }); expect(syncedTask).to.exist; - expect(syncedTask.challenge._id).to.be.empty; + expect(syncedTask.challenge._id).to.be.undefined; }); }); }); diff --git a/test/api/v3/unit/models/group_tasks.test.js b/test/api/v3/unit/models/group_tasks.test.js index 331b91ac1b..76e7666c96 100644 --- a/test/api/v3/unit/models/group_tasks.test.js +++ b/test/api/v3/unit/models/group_tasks.test.js @@ -64,7 +64,7 @@ describe('Group Task Methods', () => { each(tasksToTest, (taskValue, taskType) => { context(`${taskType}`, () => { - beforeEach(async() => { + beforeEach(async () => { task = new Tasks[`${taskType}`](Tasks.Task.sanitize(taskValue)); task.group.id = guild._id; await task.save(); @@ -127,7 +127,7 @@ describe('Group Task Methods', () => { expect(syncedTask.checklist[0].text).to.equal(task.checklist[0].text); }); - describe('syncs updated info', async() => { + describe('syncs updated info', async () => { let newMember; beforeEach(async () => { @@ -274,7 +274,7 @@ describe('Group Task Methods', () => { expect(task.group.assignedUsers).to.not.contain(leader._id); expect(updatedSyncedTask).to.exist; - expect(updatedSyncedTask.group._id).to.be.empty; + expect(updatedSyncedTask.group._id).to.be.undefined; }); }); }); diff --git a/test/api/v3/unit/models/task.test.js b/test/api/v3/unit/models/task.test.js index ddab40b26f..fc3ab9a513 100644 --- a/test/api/v3/unit/models/task.test.js +++ b/test/api/v3/unit/models/task.test.js @@ -54,7 +54,7 @@ describe('Task Model', () => { each(tasksToTest, (taskValue, taskType) => { context(`${taskType}`, () => { - beforeEach(async() => { + beforeEach(async () => { task = new Tasks[`${taskType}`](Tasks.Task.sanitize(taskValue)); task.challenge.id = challenge._id; task.history = generateHistory(396); diff --git a/test/api/v3/unit/models/user.test.js b/test/api/v3/unit/models/user.test.js index 7305401d5a..63d99193b6 100644 --- a/test/api/v3/unit/models/user.test.js +++ b/test/api/v3/unit/models/user.test.js @@ -120,7 +120,7 @@ describe('User Model', () => { expect(User.pushNotification({_id: user._id}, 'CRON', null, 'INVALID_SEEN')).to.eventually.be.rejected; }); - it('adds notifications without data for all given users via static method', async() => { + it('adds notifications without data for all given users via static method', async () => { let user = new User(); let otherUser = new User(); await Bluebird.all([user.save(), otherUser.save()]); diff --git a/test/client/e2e/specs/test.js b/test/client/e2e/specs/test.js index e3e19d4d96..da58561b29 100644 --- a/test/client/e2e/specs/test.js +++ b/test/client/e2e/specs/test.js @@ -9,7 +9,7 @@ module.exports = { const devServer = browser.globals.devServerURL; browser - .url(devServer) + .url(devServer) .waitForElementVisible('#app', 5000) .assert.elementPresent('.logo') .assert.containsText('h1', 'Hello Vue!') diff --git a/test/common/libs/taskClasses.test.js b/test/common/libs/taskClasses.test.js deleted file mode 100644 index dbfa6881a6..0000000000 --- a/test/common/libs/taskClasses.test.js +++ /dev/null @@ -1,82 +0,0 @@ -import taskClasses from '../../../website/common/script/libs/taskClasses'; - -describe('taskClasses', () => { - let task = {}; - let filters = {}; - let result; - - describe('a todo task', () => { - beforeEach(() => { - task = { type: 'todo', _editing: false, tags: [] }; - }); - - it('is hidden', () => { - filters = { a: true }; - result = taskClasses(task, filters, 0, Number(new Date()), false, true); - expect(result).to.eql('hidden'); - }); - it('is beingEdited', () => { - task._editing = true; - result = taskClasses(task, filters); - expect(result.split(' ').indexOf('beingEdited')).to.not.eql(-1); - }); - it('is completed', () => { - task.completed = true; - result = taskClasses(task, filters); - expect(result.split(' ').indexOf('completed')).to.not.eql(-1); - task.completed = false; - result = taskClasses(task, filters); - expect(result.split(' ').indexOf('completed')).to.eql(-1); - expect(result.split(' ').indexOf('uncompleted')).to.not.eql(-1); - }); - }); - - describe('a daily task', () => { - it('is completed', () => { - task = { type: 'daily' }; - result = taskClasses(task); - expect(result.split(' ').indexOf('completed')).to.not.eql(-1); - }); - - it('is uncompleted'); // this requires stubbing the internal dependency shouldDo in taskClasses - }); - - describe('a habit', () => { - it('that is wide', () => { - task = { type: 'habit', up: true, down: true }; - result = taskClasses(task); - expect(result.split(' ').indexOf('habit-wide')).to.not.eql(-1); - }); - it('that is narrow', () => { - task = { type: 'habit' }; - result = taskClasses(task); - expect(result.split(' ').indexOf('habit-narrow')).to.not.eql(-1); - }); - }); - - describe('varies based on priority', () => { - it('trivial', () => { - task.priority = 0.1; - result = taskClasses(task); - expect(result.split(' ').indexOf('difficulty-trivial')).to.not.eql(-1); - }); - it('hard', () => { - task.priority = 2; - result = taskClasses(task); - expect(result.split(' ').indexOf('difficulty-hard')).to.not.eql(-1); - }); - }); - - describe('varies based on value', () => { - it('color-worst', () => { - task.value = -30; - result = taskClasses(task); - expect(result.split(' ').indexOf('color-worst')).to.not.eql(-1); - }); - it('color-neutral', () => { - task.value = 0; - result = taskClasses(task); - expect(result.split(' ').indexOf('color-neutral')).to.not.eql(-1); - }); - }); -}); diff --git a/test/common/ops/releaseBoth.js b/test/common/ops/releaseBoth.js index 880f62a316..72411e419b 100644 --- a/test/common/ops/releaseBoth.js +++ b/test/common/ops/releaseBoth.js @@ -72,7 +72,7 @@ describe('shared.ops.releaseBoth', () => { let message = releaseBoth(user)[1]; expect(message).to.equal(i18n.t('mountsAndPetsReleased')); - expect(user.items.pets[animal]).to.be.empty; + expect(user.items.pets[animal]).to.equal(0); expect(user.items.mounts[animal]).to.equal(null); }); diff --git a/test/mocha.opts b/test/mocha.opts index f7bf61a268..63c3988a56 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -2,7 +2,6 @@ --reporter spec --timeout 8000 --check-leaks ---growl --globals io -r babel-polyfill --require babel-register diff --git a/website/client/components/groups/membersModal.vue b/website/client/components/groups/membersModal.vue index eb0199fc03..8b63c6b21a 100644 --- a/website/client/components/groups/membersModal.vue +++ b/website/client/components/groups/membersModal.vue @@ -432,10 +432,10 @@ export default { if (!lastMember) return; let newMembers = await this.$store.state.memberModalOptions.fetchMoreMembers({ - challengeId: this.challengeId, - groupId: this.groupId, - lastMemberId: lastMember._id, - includeAllPublicFields: true, + challengeId: this.challengeId, + groupId: this.groupId, + lastMemberId: lastMember._id, + includeAllPublicFields: true, }); this.members = this.members.concat(newMembers); diff --git a/website/client/components/inventory/items/index.vue b/website/client/components/inventory/items/index.vue index efd01112b9..f0839b2d4d 100644 --- a/website/client/components/inventory/items/index.vue +++ b/website/client/components/inventory/items/index.vue @@ -478,7 +478,7 @@ export default { this.$refs.clickPotionInfo.style.left = `${$event.x - 60}px`; this.$refs.clickPotionInfo.style.top = `${$event.y + 10}px`; } else if (this.eggClickMode) { - // dragging eggInfo is 180px wide (90 would be centered) + // dragging eggInfo is 180px wide (90 would be centered) this.$refs.clickEggInfo.style.left = `${$event.x - 60}px`; this.$refs.clickEggInfo.style.top = `${$event.y + 10}px`; } else { diff --git a/website/client/components/static/faq.vue b/website/client/components/static/faq.vue index cec8430cd6..a26e1260cd 100644 --- a/website/client/components/static/faq.vue +++ b/website/client/components/static/faq.vue @@ -62,8 +62,8 @@ wikiTechAssistanceEmail: `mailto:${TECH_ASSISTANCE_EMAIL}`, }, // @TODO webFaqStillNeedHelp: { - // linkStart: '[', - // linkEnd: '](/groups/guild/5481ccf3-5d2d-48a9-a871-70a7380cee5a)', + // linkStart: '[', + // linkEnd: '](/groups/guild/5481ccf3-5d2d-48a9-a871-70a7380cee5a)', // }, // "webFaqStillNeedHelp": "If you have a question that isn't on this list or on the [Wiki FAQ](http://habitica.wikia.com/wiki/FAQ), come ask in the <%= linkStart %>Habitica Help guild<%= linkEnd %>! We're happy to help." }; diff --git a/website/client/store/actions/common.js b/website/client/store/actions/common.js index ae242fea7b..e3e2ac8428 100644 --- a/website/client/store/actions/common.js +++ b/website/client/store/actions/common.js @@ -8,9 +8,9 @@ export function equip (store, params) { equipOp(user, {params}); axios .post(`/api/v3/user/equip/${params.type}/${params.key}`); - // TODO - // .then((res) => console.log('equip', res)) - // .catch((err) => console.error('equip', err)); + // TODO + // .then((res) => console.log('equip', res)) + // .catch((err) => console.error('equip', err)); } export function hatch (store, params) { @@ -18,9 +18,9 @@ export function hatch (store, params) { hatchOp(user, {params}); axios .post(`/api/v3/user/hatch/${params.egg}/${params.hatchingPotion}`); - // TODO - // .then((res) => console.log('equip', res)) - // .catch((err) => console.error('equip', err)); + // TODO + // .then((res) => console.log('equip', res)) + // .catch((err) => console.error('equip', err)); } export async function feed (store, params) { diff --git a/website/client/store/actions/shops.js b/website/client/store/actions/shops.js index 554694f499..46480de461 100644 --- a/website/client/store/actions/shops.js +++ b/website/client/store/actions/shops.js @@ -69,16 +69,16 @@ async function buyArmoire (store, params) { // @TODO: We might need to abstract notifications to library rather than mixin const notificationOptions = isExperience ? - { - text: `+ ${item.value}`, - type: 'xp', - flavorMessage: message, - } : - { - text: message, - type: 'drop', - icon: getDropClass({type: item.type, key: item.dropKey}), - }; + { + text: `+ ${item.value}`, + type: 'xp', + flavorMessage: message, + } : + { + text: message, + type: 'drop', + icon: getDropClass({type: item.type, key: item.dropKey}), + }; store.dispatch('snackbars:add', { title: '', diff --git a/website/common/script/content/appearance/skin.js b/website/common/script/content/appearance/skin.js index 1de36e767a..ea23edd4ea 100644 --- a/website/common/script/content/appearance/skin.js +++ b/website/common/script/content/appearance/skin.js @@ -73,5 +73,5 @@ module.exports = prefill({ 'snowy': {price: 2, set: sets.winterySkins}, 'winterstar': {price: 2, set: sets.winterySkins}, - /* eslint-enable quote-props */ + /* eslint-enable quote-props */ }); diff --git a/website/common/script/content/shop-featuredItems.js b/website/common/script/content/shop-featuredItems.js index 31010cb4e4..b1881440c4 100644 --- a/website/common/script/content/shop-featuredItems.js +++ b/website/common/script/content/shop-featuredItems.js @@ -37,7 +37,7 @@ const featuredItems = { ], seasonal: 'summerMage', timeTravelers: [ - // TODO + // TODO ], }; diff --git a/website/common/script/index.js b/website/common/script/index.js index d5de49a42b..57eaa890ca 100644 --- a/website/common/script/index.js +++ b/website/common/script/index.js @@ -86,9 +86,6 @@ api.gold = gold; import silver from './libs/silver'; api.silver = silver; -import taskClasses from './libs/taskClasses'; -api.taskClasses = taskClasses; - import noTags from './libs/noTags'; api.noTags = noTags; diff --git a/website/common/script/libs/taskClasses.js b/website/common/script/libs/taskClasses.js deleted file mode 100644 index a61a82dd75..0000000000 --- a/website/common/script/libs/taskClasses.js +++ /dev/null @@ -1,83 +0,0 @@ -import { - shouldDo, -} from '../cron'; -import moment from 'moment'; - -/* -Task classes given everything about the class -*/ - -// TODO move to the client - -module.exports = function taskClasses (task, filters = [], dayStart = 0, lastCron = Number(new Date()), showCompleted = false, main = false, processingYesterdailies = false) { - if (!task) { - return ''; - } - let type = task.type; - let completed = task.completed; - let value = task.value; - let priority = task.priority; - - if (main && !task._editing) { - for (let filter in filters) { - let enabled = filters[filter]; - if (!task.tags) task.tags = []; - if (enabled && task.tags.indexOf(filter) === -1) { - return 'hidden'; - } - } - } - - let classes = task.type; - if (task._editing) { - classes += ' beingEdited'; - } - - if (type === 'todo' || type === 'daily') { - let dayShouldDo = moment(); - if (processingYesterdailies) dayShouldDo.subtract(1, 'days'); - let notDue = !shouldDo(Number(dayShouldDo), task, { dayStart }); - let isNotDueDaily = type === 'daily' && notDue; - - if (completed || isNotDueDaily) { - classes += ' completed'; - } else { - classes += ' uncompleted'; - } - } else if (type === 'habit') { - if (task.down && task.up) { - classes += ' habit-wide'; - } - if (!task.down && !task.up) { - classes += ' habit-narrow'; - } - } - - if (priority === 0.1) { - classes += ' difficulty-trivial'; - } else if (priority === 1) { - classes += ' difficulty-easy'; - } else if (priority === 1.5) { - classes += ' difficulty-medium'; - } else if (priority === 2) { - classes += ' difficulty-hard'; - } - - if (value < -20) { - classes += ' color-worst'; - } else if (value < -10) { - classes += ' color-worse'; - } else if (value < -1) { - classes += ' color-bad'; - } else if (value < 1) { - classes += ' color-neutral'; - } else if (value < 5) { - classes += ' color-good'; - } else if (value < 10) { - classes += ' color-better'; - } else { - classes += ' color-best'; - } - - return classes; -}; diff --git a/website/server/controllers/api-v3/auth.js b/website/server/controllers/api-v3/auth.js index 910568ed4a..ddb4ccbfcc 100644 --- a/website/server/controllers/api-v3/auth.js +++ b/website/server/controllers/api-v3/auth.js @@ -347,11 +347,11 @@ api.loginSocial = { // Clean previous email preferences if (savedUser.auth[network].emails && savedUser.auth[network].emails[0] && savedUser.auth[network].emails[0].value) { EmailUnsubscription - .remove({email: savedUser.auth[network].emails[0].value.toLowerCase()}) - .exec() - .then(() => { - if (!existingUser) sendTxnEmail(savedUser, 'welcome'); - }); // eslint-disable-line max-nested-callbacks + .remove({email: savedUser.auth[network].emails[0].value.toLowerCase()}) + .exec() + .then(() => { + if (!existingUser) sendTxnEmail(savedUser, 'welcome'); + }); // eslint-disable-line max-nested-callbacks } if (!existingUser) { diff --git a/website/server/controllers/api-v3/challenges.js b/website/server/controllers/api-v3/challenges.js index 7dbd7b0448..6bfb5e116b 100644 --- a/website/server/controllers/api-v3/challenges.js +++ b/website/server/controllers/api-v3/challenges.js @@ -434,11 +434,11 @@ api.getUserChallenges = { let challenges = await Challenge.find({ $or: orOptions, }) - .sort('-official -createdAt') - // see below why we're not using populate - // .populate('group', basicGroupFields) - // .populate('leader', nameFields) - .exec(); + .sort('-official -createdAt') + // see below why we're not using populate + // .populate('group', basicGroupFields) + // .populate('leader', nameFields) + .exec(); let resChals = challenges.map(challenge => challenge.toJSON()); // Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 diff --git a/website/server/controllers/api-v3/groups.js b/website/server/controllers/api-v3/groups.js index ae7c8c1234..bcc321d427 100644 --- a/website/server/controllers/api-v3/groups.js +++ b/website/server/controllers/api-v3/groups.js @@ -1018,9 +1018,9 @@ async function _inviteByEmail (invite, group, inviter, req, res) { if (!invite.email) throw new BadRequest(res.t('inviteMissingEmail')); let userToContact = await User.findOne({$or: [ - {'auth.local.email': invite.email}, - {'auth.facebook.emails.value': invite.email}, - {'auth.google.emails.value': invite.email}, + {'auth.local.email': invite.email}, + {'auth.facebook.emails.value': invite.email}, + {'auth.google.emails.value': invite.email}, ]}) .select({_id: true, 'preferences.emailNotifications': true}) .exec(); diff --git a/website/server/controllers/api-v3/hall.js b/website/server/controllers/api-v3/hall.js index af2b8bd104..5d836407b3 100644 --- a/website/server/controllers/api-v3/hall.js +++ b/website/server/controllers/api-v3/hall.js @@ -71,15 +71,15 @@ api.getPatrons = { const perPage = 50; let patrons = await User - .find({ - 'backer.tier': {$gt: 0}, - }) - .select('contributor backer profile.name') - .sort('-backer.tier') - .skip(page * perPage) - .limit(perPage) - .lean() - .exec(); + .find({ + 'backer.tier': {$gt: 0}, + }) + .select('contributor backer profile.name') + .sort('-backer.tier') + .skip(page * perPage) + .limit(perPage) + .lean() + .exec(); res.respond(200, patrons); }, @@ -123,13 +123,13 @@ api.getHeroes = { middlewares: [authWithHeaders()], async handler (req, res) { let heroes = await User - .find({ - 'contributor.level': {$gt: 0}, - }) - .select('contributor backer profile.name') - .sort('-contributor.level') - .lean() - .exec(); + .find({ + 'contributor.level': {$gt: 0}, + }) + .select('contributor backer profile.name') + .sort('-contributor.level') + .lean() + .exec(); res.respond(200, heroes); }, diff --git a/website/server/controllers/api-v3/i18n.js b/website/server/controllers/api-v3/i18n.js index ed1b744133..505df9b0fa 100644 --- a/website/server/controllers/api-v3/i18n.js +++ b/website/server/controllers/api-v3/i18n.js @@ -13,11 +13,11 @@ function geti18nBrowserScript (language) { return `(function () { if (!window) return; window['habitica-i18n'] = ${JSON.stringify({ - availableLanguages, - language, - strings: translations[langCode], - momentLang: momentLangs[language.momentLangCode], - })}; + availableLanguages, + language, + strings: translations[langCode], + momentLang: momentLangs[language.momentLangCode], + })}; })()`; } diff --git a/website/server/controllers/api-v3/members.js b/website/server/controllers/api-v3/members.js index b9e390891e..279f759ee7 100644 --- a/website/server/controllers/api-v3/members.js +++ b/website/server/controllers/api-v3/members.js @@ -408,8 +408,8 @@ api.getChallengeMemberProgress = { userId: memberId, 'challenge.id': challengeId, }) - .select('-tags') // We don't want to return the tags publicly TODO same for other data? - .exec(); + .select('-tags') // We don't want to return the tags publicly TODO same for other data? + .exec(); // manually call toJSON with minimize: true so empty paths aren't returned let response = member.toJSON({minimize: true}); diff --git a/website/server/controllers/api-v3/quests.js b/website/server/controllers/api-v3/quests.js index 8d4bd71992..792c37e124 100644 --- a/website/server/controllers/api-v3/quests.js +++ b/website/server/controllers/api-v3/quests.js @@ -79,8 +79,8 @@ api.inviteToQuest = { 'party._id': group._id, _id: {$ne: user._id}, }) - .select('auth.facebook auth.local preferences.emailNotifications profile.name pushDevices') - .exec(); + .select('auth.facebook auth.local preferences.emailNotifications profile.name pushDevices') + .exec(); group.markModified('quest'); group.quest.key = questKey; diff --git a/website/server/controllers/api-v3/tasks/groups.js b/website/server/controllers/api-v3/tasks/groups.js index 2f1a1675d8..62ebb403ee 100644 --- a/website/server/controllers/api-v3/tasks/groups.js +++ b/website/server/controllers/api-v3/tasks/groups.js @@ -489,8 +489,8 @@ api.getGroupApprovals = { 'group.approval.approved': false, 'group.approval.requested': true, }, 'userId group text') - .populate('userId', 'profile') - .exec(); + .populate('userId', 'profile') + .exec(); res.respond(200, approvals); }, diff --git a/website/server/controllers/top-level/dataexport.js b/website/server/controllers/top-level/dataexport.js index 817d252d29..d8d63f01a3 100644 --- a/website/server/controllers/top-level/dataexport.js +++ b/website/server/controllers/top-level/dataexport.js @@ -159,7 +159,7 @@ api.exportUserDataXml = { * * @apiUse UserNotFound */ - // @TODO fix +// @TODO fix api.exportUserAvatarHtml = { method: 'GET', url: '/export/avatar-:memberId.html', @@ -223,11 +223,11 @@ api.exportUserAvatarPng = { } let [stream] = await new Pageres() - .src(`${BASE_URL}/export/avatar-${memberId}.html`, ['140x147'], { - crop: true, - filename: filename.replace('.png', ''), - }) - .run(); + .src(`${BASE_URL}/export/avatar-${memberId}.html`, ['140x147'], { + crop: true, + filename: filename.replace('.png', ''), + }) + .run(); let s3upload = S3.upload({ Bucket: S3_BUCKET, diff --git a/website/server/libs/logger.js b/website/server/libs/logger.js index 9d1cd13aab..67bc4df2a7 100644 --- a/website/server/libs/logger.js +++ b/website/server/libs/logger.js @@ -22,10 +22,10 @@ if (IS_PROD) { prettyPrint: false, }); logger.add(winston.transports.Loggly, { - inputToken: nconf.get('LOGGLY:TOKEN'), - subdomain: nconf.get('LOGGLY:SUBDOMAIN'), - tags: ['Winston-NodeJS'], - json: true, + inputToken: nconf.get('LOGGLY:TOKEN'), + subdomain: nconf.get('LOGGLY:SUBDOMAIN'), + tags: ['Winston-NodeJS'], + json: true, }); } } else if (!IS_TEST || IS_TEST && ENABLE_LOGS_IN_TEST) { // Do not log anything when testing unless specified diff --git a/website/server/libs/password.js b/website/server/libs/password.js index c81c836e04..12d162f6ef 100644 --- a/website/server/libs/password.js +++ b/website/server/libs/password.js @@ -21,17 +21,17 @@ export function bcryptCompare (passwordToCheck, hashedPassword) { // Used for legacy passwords that have not yet been migrated to bcrypt export function sha1Encrypt (password, salt) { return crypto - .createHmac('sha1', salt) - .update(password) - .digest('hex'); + .createHmac('sha1', salt) + .update(password) + .digest('hex'); } // Create a salt, default length is 10 export function sha1MakeSalt (len = 10) { return crypto - .randomBytes(Math.ceil(len / 2)) - .toString('hex') - .substring(0, len); + .randomBytes(Math.ceil(len / 2)) + .toString('hex') + .substring(0, len); } // Compare the password for an user diff --git a/website/server/libs/payments.js b/website/server/libs/payments.js index 33ea4c2b39..c041e3c479 100644 --- a/website/server/libs/payments.js +++ b/website/server/libs/payments.js @@ -42,7 +42,7 @@ function revealMysteryItems (user) { moment().isBefore(shared.content.mystery[item.mystery].end) && !user.items.gear.owned[item.key] && user.purchased.plan.mysteryItems.indexOf(item.key) === -1 - ) { + ) { user.purchased.plan.mysteryItems.push(item.key); pushedItems.push(item.key); } @@ -511,9 +511,9 @@ api.cancelSubscription = async function cancelSubscription (data) { plan.dateTerminated = moment(nowStr, nowStrFormat) - .add({days: remaining}) - .add({days: extraDays}) - .toDate(); + .add({days: remaining}) + .add({days: extraDays}) + .toDate(); plan.extraMonths = 0; // clear extra time. If they subscribe again, it'll be recalculated from p.dateTerminated diff --git a/website/server/libs/pushNotifications.js b/website/server/libs/pushNotifications.js index ddd7f12667..e7071c03bc 100644 --- a/website/server/libs/pushNotifications.js +++ b/website/server/libs/pushNotifications.js @@ -31,33 +31,33 @@ if (APN_ENABLED) { Key: 'apple_apn/key.pem', }).promise(), ]) - .then(([certObj, keyObj]) => { - let cert = certObj.Body.toString(); - let key = keyObj.Body.toString(); + .then(([certObj, keyObj]) => { + let cert = certObj.Body.toString(); + let key = keyObj.Body.toString(); - apn = pushNotify.apn({ - key, - cert, - }); + apn = pushNotify.apn({ + key, + cert, + }); - apn.on('error', err => logger.error('APN error', err)); - apn.on('transmissionError', (errorCode, notification, device) => { - logger.error('APN transmissionError', errorCode, notification, device); - }); + apn.on('error', err => logger.error('APN error', err)); + apn.on('transmissionError', (errorCode, notification, device) => { + logger.error('APN transmissionError', errorCode, notification, device); + }); - let feedback = new apnLib.Feedback({ - key, - cert, - batchFeedback: true, - interval: 3600, // Check for feedback once an hour - }); + let feedback = new apnLib.Feedback({ + key, + cert, + batchFeedback: true, + interval: 3600, // Check for feedback once an hour + }); - feedback.on('feedback', (devices) => { - if (devices && devices.length > 0) { - logger.info('Delivery of push notifications failed for some Apple devices.', devices); - } + feedback.on('feedback', (devices) => { + if (devices && devices.length > 0) { + logger.info('Delivery of push notifications failed for some Apple devices.', devices); + } + }); }); - }); } function sendNotification (user, details = {}) { if (!user) throw new Error('User is required.'); diff --git a/website/server/middlewares/analytics.js b/website/server/middlewares/analytics.js index f86becb773..2ab8b1fd7d 100644 --- a/website/server/middlewares/analytics.js +++ b/website/server/middlewares/analytics.js @@ -1,8 +1,8 @@ import nconf from 'nconf'; import { - track, - trackPurchase, - mockAnalyticsService, + track, + trackPurchase, + mockAnalyticsService, } from '../libs/analyticsService'; let service; diff --git a/website/server/middlewares/auth.js b/website/server/middlewares/auth.js index 012d52584a..b86f525d35 100644 --- a/website/server/middlewares/auth.js +++ b/website/server/middlewares/auth.js @@ -44,17 +44,17 @@ export function authWithHeaders (optional = false, userFieldProjection = '') { const findPromise = fields ? User.findOne(userQuery, fields) : User.findOne(userQuery); return findPromise - .exec() - .then((user) => { - if (!user) throw new NotAuthorized(res.t('invalidCredentials')); - if (user.auth.blocked) throw new NotAuthorized(res.t('accountSuspended', {communityManagerEmail: COMMUNITY_MANAGER_EMAIL, userId: user._id})); + .exec() + .then((user) => { + if (!user) throw new NotAuthorized(res.t('invalidCredentials')); + if (user.auth.blocked) throw new NotAuthorized(res.t('accountSuspended', {communityManagerEmail: COMMUNITY_MANAGER_EMAIL, userId: user._id})); - res.locals.user = user; + res.locals.user = user; - req.session.userId = user._id; - return next(); - }) - .catch(next); + req.session.userId = user._id; + return next(); + }) + .catch(next); }; } @@ -74,14 +74,14 @@ export function authWithSession (req, res, next) { return User.findOne({ _id: userId, }) - .exec() - .then((user) => { - if (!user) throw new NotAuthorized(res.t('invalidCredentials')); + .exec() + .then((user) => { + if (!user) throw new NotAuthorized(res.t('invalidCredentials')); - res.locals.user = user; - return next(); - }) - .catch(next); + res.locals.user = user; + return next(); + }) + .catch(next); } export function authWithUrl (req, res, next) { @@ -98,11 +98,11 @@ export function authWithUrl (req, res, next) { } return User.findOne({ _id: userId, apiToken }).exec() - .then((user) => { - if (!user) throw new NotAuthorized(res.t('invalidCredentials')); + .then((user) => { + if (!user) throw new NotAuthorized(res.t('invalidCredentials')); - res.locals.user = user; - return next(); - }) - .catch(next); + res.locals.user = user; + return next(); + }) + .catch(next); } diff --git a/website/server/middlewares/language.js b/website/server/middlewares/language.js index 50ef8a2b16..d5d6f2d96f 100644 --- a/website/server/middlewares/language.js +++ b/website/server/middlewares/language.js @@ -77,13 +77,13 @@ export function getUserLanguage (req, res, next) { return User.findOne({ _id: req.session.userId, }, 'preferences.language') - .lean() - .exec() - .then((user) => { - req.language = _getFromUser(user, req); - return next(); - }) - .catch(next); + .lean() + .exec() + .then((user) => { + req.language = _getFromUser(user, req); + return next(); + }) + .catch(next); } else { // Otherwise get from browser req.language = _getFromUser(null, req); return next(); diff --git a/website/server/models/group.js b/website/server/models/group.js index 49006ac0e3..45c7c99235 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -1012,16 +1012,16 @@ let tavernQ = {_id: TAVERN_ID, 'quest.key': {$ne: null}}; // we use process.nextTick because at this point the model is not yet available process.nextTick(() => { model // eslint-disable-line no-use-before-define - .findOne(tavernQ).exec() - .then(tavern => { - if (!tavern) return; // No tavern quest + .findOne(tavernQ).exec() + .then(tavern => { + if (!tavern) return; // No tavern quest - // Using _assign so we don't lose the reference to the exported tavernQuest - _.assign(tavernQuest, tavern.quest.toObject()); - }) - .catch(err => { - throw err; - }); + // Using _assign so we don't lose the reference to the exported tavernQuest + _.assign(tavernQuest, tavern.quest.toObject()); + }) + .catch(err => { + throw err; + }); }); // returns a promise From 80fe58bc6f138d0e9b9e044561013b9ac0335d73 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Sat, 17 Feb 2018 18:15:43 +0100 Subject: [PATCH 15/49] Upgrade client deps (#9990) * upgrade axios * upgrade axios-progress-bar * upgrade bootstrap-vue * fix issues --- package-lock.json | 27 ++++++++++----------------- package.json | 6 +++--- website/client/app.vue | 1 + 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 50cc140df2..1d7df247bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1005,21 +1005,18 @@ "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" }, "axios": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.16.2.tgz", - "integrity": "sha1-uk+S8XFn37q0CYN4VFS5rBScPG0=", + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.17.1.tgz", + "integrity": "sha1-LY4+XQvb1zJ/kbyBT1xXZg+Bgk0=", "requires": { "follow-redirects": "1.4.1", "is-buffer": "1.1.6" } }, "axios-progress-bar": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/axios-progress-bar/-/axios-progress-bar-0.1.7.tgz", - "integrity": "sha512-xStxJUtcQUH0ulLni5qc8YwvNMTUjO7rmUTsvnxS1bD2GJKEemozP6sJ5OeoC6jjd6MS5FZyx5BlkU/lD8JLUw==", - "requires": { - "nprogress": "0.2.0" - } + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/axios-progress-bar/-/axios-progress-bar-1.1.8.tgz", + "integrity": "sha512-CDjRu+qCj1EmuIXkZbluZE0qz0mOugBrAlDwnhn2VRP6Bvg7ddkfXqyFlEM+Rtgxzn1rWQiFTuCC46azyRQOpA==" }, "babel-code-frame": { "version": "6.26.0", @@ -1981,10 +1978,11 @@ "integrity": "sha512-gulJE5dGFo6Q61V/whS6VM4WIyrlydXfCgkE+Gxe5hjrJ8rXLLZlALq7zq2RPhOc45PSwQpJkrTnc2KgD6cvmA==" }, "bootstrap-vue": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/bootstrap-vue/-/bootstrap-vue-1.5.1.tgz", - "integrity": "sha512-bkob7vTHA5VZN6U0Wj34Yj+6jNtqAtc6MwcsLdBz78fcy8Ju5tlYUYMDUefQ0rQH7hhdtFDn9GuDiqhUPmE1sA==", + "version": "2.0.0-rc.1", + "resolved": "https://registry.npmjs.org/bootstrap-vue/-/bootstrap-vue-2.0.0-rc.1.tgz", + "integrity": "sha512-X9dr6gKi3pF3K2yMJah7nYhlo/HB3JLJQ+grr8442s/HJsfnW9P1iALJtmiAxH8/RXLCSes00gs4lYlP0zg21w==", "requires": { + "bootstrap": "4.0.0", "lodash.startcase": "4.4.0", "opencollective": "1.0.3", "popper.js": "1.13.0", @@ -13953,11 +13951,6 @@ "set-blocking": "2.0.0" } }, - "nprogress": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", - "integrity": "sha1-y480xTIT2JVyP8urkH6UIq28r7E=" - }, "nth-check": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", diff --git a/package.json b/package.json index 0840975822..e1efeb63fe 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "apn": "^1.7.6", "autoprefixer": "^8.0.0", "aws-sdk": "^2.0.25", - "axios": "^0.16.0", - "axios-progress-bar": "^0.1.7", + "axios": "^0.17.1", + "axios-progress-bar": "^1.1.8", "babel-core": "^6.0.0", "babel-loader": "^7.1.2", "babel-eslint": "^8.2.1", @@ -30,7 +30,7 @@ "bluebird": "^3.3.5", "body-parser": "^1.15.0", "bootstrap": "^4.0.0", - "bootstrap-vue": "^1.5.0", + "bootstrap-vue": "^2.0.0-rc.1", "compression": "^1.6.1", "cookie-session": "^1.2.0", "coupon-code": "^0.4.5", diff --git a/website/client/app.vue b/website/client/app.vue index 10778ef381..18f1adbe50 100644 --- a/website/client/app.vue +++ b/website/client/app.vue @@ -436,6 +436,7 @@ export default { + From b619496f8eba70ed28c5837c2f24eae391b5451a Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Sat, 17 Feb 2018 18:41:49 +0100 Subject: [PATCH 16/49] fix linting --- migrations/users/achievement-restore.js | 2 +- test/api/v3/integration/chat/POST-chat.test.js | 8 ++++---- test/api/v3/integration/tasks/GET-tasks_user.test.js | 4 ++-- .../tasks/POST-tasks_clearCompletedTodos.test.js | 4 ++-- website/client/store/actions/user.js | 4 ++-- website/server/libs/applePayments.js | 6 +++--- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/migrations/users/achievement-restore.js b/migrations/users/achievement-restore.js index e87300bc18..45e7866398 100644 --- a/migrations/users/achievement-restore.js +++ b/migrations/users/achievement-restore.js @@ -74,8 +74,8 @@ module.exports = async function achievementRestore () { const userIds = [ ]; + /* eslint-disable no-await-in-loop */ for (let index in userIds) { - /* eslint-disable no-await-in-loop */ const userId = userIds[index]; const oldUser = await UsersOld.findOne({_id: userId}, 'achievements'); const newUser = await Users.findOne({_id: userId}, 'achievements'); diff --git a/test/api/v3/integration/chat/POST-chat.test.js b/test/api/v3/integration/chat/POST-chat.test.js index c9b2d97384..8892c8e845 100644 --- a/test/api/v3/integration/chat/POST-chat.test.js +++ b/test/api/v3/integration/chat/POST-chat.test.js @@ -478,8 +478,8 @@ describe('POST /chat', () => { context('Spam prevention', () => { it('Returns an error when the user has been posting too many messages', async () => { // Post as many messages are needed to reach the spam limit - for (let i = 0; i < SPAM_MESSAGE_LIMIT; i++) { - let result = await additionalMember.post(`/groups/${TAVERN_ID}/chat`, { message: testMessage }); // eslint-disable-line no-await-in-loop + for (let i = 0; i < SPAM_MESSAGE_LIMIT; i++) { // eslint-disable-line no-await-in-loop + let result = await additionalMember.post(`/groups/${TAVERN_ID}/chat`, { message: testMessage }); expect(result.message.id).to.exist; } @@ -494,8 +494,8 @@ describe('POST /chat', () => { let userSocialite = await member.update({'contributor.level': SPAM_MIN_EXEMPT_CONTRIB_LEVEL, 'flags.chatRevoked': false}); // Post 1 more message than the spam limit to ensure they do not reach the limit - for (let i = 0; i < SPAM_MESSAGE_LIMIT + 1; i++) { - let result = await userSocialite.post(`/groups/${TAVERN_ID}/chat`, { message: testMessage }); // eslint-disable-line no-await-in-loop + for (let i = 0; i < SPAM_MESSAGE_LIMIT + 1; i++) { // eslint-disable-line no-await-in-loop + let result = await userSocialite.post(`/groups/${TAVERN_ID}/chat`, { message: testMessage }); expect(result.message.id).to.exist; } }); diff --git a/test/api/v3/integration/tasks/GET-tasks_user.test.js b/test/api/v3/integration/tasks/GET-tasks_user.test.js index ea8f2a49d0..0525f6b4b5 100644 --- a/test/api/v3/integration/tasks/GET-tasks_user.test.js +++ b/test/api/v3/integration/tasks/GET-tasks_user.test.js @@ -113,10 +113,10 @@ describe('GET /tasks/user', () => { await user.sync(); let initialTodoCount = user.tasksOrder.todos.length; - for (let i = 0; i < numberOfTodos; i++) { + for (let i = 0; i < numberOfTodos; i++) { // eslint-disable-line no-await-in-loop let id = todos[i]._id; - await user.post(`/tasks/${id}/score/up`); // eslint-disable-line no-await-in-loop + await user.post(`/tasks/${id}/score/up`); } await user.sync(); diff --git a/test/api/v3/integration/tasks/POST-tasks_clearCompletedTodos.test.js b/test/api/v3/integration/tasks/POST-tasks_clearCompletedTodos.test.js index 7cdd7753d2..f3c79fe868 100644 --- a/test/api/v3/integration/tasks/POST-tasks_clearCompletedTodos.test.js +++ b/test/api/v3/integration/tasks/POST-tasks_clearCompletedTodos.test.js @@ -33,9 +33,9 @@ describe('POST /tasks/clearCompletedTodos', () => { let tasks = await user.get('/tasks/user?type=todos'); expect(tasks.length).to.equal(initialTodoCount + 7); - for (let task of tasks) { + for (let task of tasks) { // eslint-disable-line no-await-in-loop if (['todo 2', 'todo 3', 'todo 6'].indexOf(task.text) !== -1) { - await user.post(`/tasks/${task._id}/score/up`); // eslint-disable-line no-await-in-loop + await user.post(`/tasks/${task._id}/score/up`); } } diff --git a/website/client/store/actions/user.js b/website/client/store/actions/user.js index 35402430f9..07d7d599a6 100644 --- a/website/client/store/actions/user.js +++ b/website/client/store/actions/user.js @@ -21,7 +21,7 @@ export function fetch (store, options = {}) { // eslint-disable-line no-shadow export async function set (store, changes) { const user = store.state.user.data; - for (let key in changes) { + for (let key in changes) { // eslint-disable-line no-await-in-loop if (key === 'tags') { // Keep challenge and group tags const oldTags = user.tags.filter(t => { @@ -31,7 +31,7 @@ export async function set (store, changes) { user.tags = changes[key].concat(oldTags); // Remove deleted tags from tasks - const userTasksByType = (await store.dispatch('tasks:fetchUserTasks')).data; // eslint-disable-line no-await-in-loop + const userTasksByType = (await store.dispatch('tasks:fetchUserTasks')).data; Object.keys(userTasksByType).forEach(taskType => { userTasksByType[taskType].forEach(task => { diff --git a/website/server/libs/applePayments.js b/website/server/libs/applePayments.js index e77f452a78..4cb0f0e76f 100644 --- a/website/server/libs/applePayments.js +++ b/website/server/libs/applePayments.js @@ -33,16 +33,16 @@ api.verifyGemPurchase = async function verifyGemPurchase (user, receipt, headers let correctReceipt = false; // Purchasing one item at a time (processing of await(s) below is sequential not parallel) - for (let index in purchaseDataList) { + for (let index in purchaseDataList) { // eslint-disable-line no-await-in-loop let purchaseData = purchaseDataList[index]; let token = purchaseData.transactionId; - let existingReceipt = await IapPurchaseReceipt.findOne({ // eslint-disable-line no-await-in-loop + let existingReceipt = await IapPurchaseReceipt.findOne({ _id: token, }).exec(); if (!existingReceipt) { - await IapPurchaseReceipt.create({ // eslint-disable-line no-await-in-loop + await IapPurchaseReceipt.create({ _id: token, consumed: true, userId: user._id, From faa068f690216c03cf0a0dd22f9cf30fee0318fe Mon Sep 17 00:00:00 2001 From: Keith Holliday Date: Mon, 19 Feb 2018 08:15:43 -0700 Subject: [PATCH 17/49] Challenge small refactor (#9964) * Removed challenge group duplication * Removed duplication of joined challenge * Moved helpers to challenge library * Passed group to function * Fixed group response --- .../server/controllers/api-v3/challenges.js | 120 ++---------------- website/server/libs/challenges/index.js | 110 ++++++++++++++++ 2 files changed, 120 insertions(+), 110 deletions(-) create mode 100644 website/server/libs/challenges/index.js diff --git a/website/server/controllers/api-v3/challenges.js b/website/server/controllers/api-v3/challenges.js index 6bfb5e116b..326a66d5e4 100644 --- a/website/server/controllers/api-v3/challenges.js +++ b/website/server/controllers/api-v3/challenges.js @@ -1,8 +1,6 @@ import { authWithHeaders, authWithSession } from '../../middlewares/auth'; import _ from 'lodash'; import cloneDeep from 'lodash/cloneDeep'; -import omit from 'lodash/omit'; -import uuid from 'uuid'; import { model as Challenge } from '../../models/challenge'; import { model as Group, @@ -24,77 +22,15 @@ import { createTasks, } from '../../libs/taskManager'; -const TASK_KEYS_TO_REMOVE = ['_id', 'completed', 'dateCompleted', 'history', 'id', 'streak', 'createdAt', 'challenge']; +import { + addUserJoinChallengeNotification, + getChallengeGroupResponse, + createChallenge, + cleanUpTask, +} from '../../libs/challenges'; let api = {}; -async function createChallenge (user, req, res) { - let groupId = req.body.group; - let prize = req.body.prize; - - let group = await Group.getGroup({user, groupId, fields: '-chat', mustBeMember: true}); - if (!group) throw new NotFound(res.t('groupNotFound')); - if (!group.isMember(user)) throw new NotAuthorized(res.t('mustBeGroupMember')); - - if (group.leaderOnly && group.leaderOnly.challenges && group.leader !== user._id) { - throw new NotAuthorized(res.t('onlyGroupLeaderChal')); - } - - if (group._id === TAVERN_ID && prize < 1) { - throw new NotAuthorized(res.t('tavChalsMinPrize')); - } - - if (prize > 0) { - let groupBalance = group.balance && group.leader === user._id ? group.balance : 0; - let prizeCost = prize / 4; - - if (prizeCost > user.balance + groupBalance) { - throw new NotAuthorized(res.t('cantAfford')); - } - - if (groupBalance >= prizeCost) { - // Group pays for all of prize - group.balance -= prizeCost; - } else if (groupBalance > 0) { - // User pays remainder of prize cost after group - let remainder = prizeCost - group.balance; - group.balance = 0; - user.balance -= remainder; - } else { - // User pays for all of prize - user.balance -= prizeCost; - } - } - - group.challengeCount += 1; - - if (!req.body.summary) { - req.body.summary = req.body.name; - } - req.body.leader = user._id; - req.body.official = user.contributor.admin && req.body.official ? true : false; - let challenge = new Challenge(Challenge.sanitize(req.body)); - - // First validate challenge so we don't save group if it's invalid (only runs sync validators) - let challengeValidationErrors = challenge.validateSync(); - if (challengeValidationErrors) throw challengeValidationErrors; - - // Add achievement if user's first challenge - if (!user.achievements.joinedChallenge) { - user.achievements.joinedChallenge = true; - user.addNotification('CHALLENGE_JOINED_ACHIEVEMENT'); - } - - let results = await Bluebird.all([challenge.save({ - validateBeforeSave: false, // already validate - }), group.save()]); - let savedChal = results[0]; - - await savedChal.syncToUser(user); // (it also saves the user) - - return {savedChal, group}; -} - /** * @apiDefine ChallengeLeader Challenge Leader * The leader of the challenge can use this route. @@ -264,12 +200,7 @@ api.createChallenge = { _id: user._id, profile: {name: user.profile.name}, }; - response.group = { // we already have the group data - _id: group._id, - name: group.name, - type: group.type, - privacy: group.privacy, - }; + response.group = getChallengeGroupResponse(group); res.analytics.track('challenge create', { uuid: user._id, @@ -320,22 +251,13 @@ api.joinChallenge = { challenge.memberCount += 1; - // Add achievement if user's first challenge - if (!user.achievements.joinedChallenge) { - user.achievements.joinedChallenge = true; - user.addNotification('CHALLENGE_JOINED_ACHIEVEMENT'); - } + addUserJoinChallengeNotification(user); // Add all challenge's tasks to user's tasks and save the challenge let results = await Bluebird.all([challenge.syncToUser(user), challenge.save()]); let response = results[1].toJSON(); - response.group = { // we already have the group data - _id: group._id, - name: group.name, - type: group.type, - privacy: group.privacy, - }; + response.group = getChallengeGroupResponse(group); let chalLeader = await User.findById(response.leader).select(nameFields).exec(); response.leader = chalLeader ? chalLeader.toJSON({minimize: true}) : null; @@ -692,12 +614,7 @@ api.updateChallenge = { let savedChal = await challenge.save(); let response = savedChal.toJSON(); - response.group = { // we already have the group data - _id: group._id, - name: group.name, - type: group.type, - privacy: group.privacy, - }; + response.group = getChallengeGroupResponse(group); let chalLeader = await User.findById(response.leader).select(nameFields).exec(); response.leader = chalLeader ? chalLeader.toJSON({minimize: true}) : null; res.respond(200, response); @@ -798,23 +715,6 @@ api.selectChallengeWinner = { }, }; -function cleanUpTask (task) { - let cleansedTask = omit(task, TASK_KEYS_TO_REMOVE); - - // Copy checklists but reset to uncomplete and assign new id - if (!cleansedTask.checklist) cleansedTask.checklist = []; - cleansedTask.checklist.forEach((item) => { - item.completed = false; - item.id = uuid(); - }); - - if (cleansedTask.type !== 'reward') { - delete cleansedTask.value; - } - - return cleansedTask; -} - /** * @api {post} /api/v3/challenges/:challengeId/clone Clone a challenge * @apiName CloneChallenge diff --git a/website/server/libs/challenges/index.js b/website/server/libs/challenges/index.js new file mode 100644 index 0000000000..84be475b87 --- /dev/null +++ b/website/server/libs/challenges/index.js @@ -0,0 +1,110 @@ +// Currently this holds helpers for challenge api, but we should break this up into submodules as it expands +import omit from 'lodash/omit'; +import uuid from 'uuid'; +import Bluebird from 'bluebird'; +import { model as Challenge } from '../../models/challenge'; +import { + model as Group, + TAVERN_ID, +} from '../../models/group'; +import { + NotFound, + NotAuthorized, +} from '../errors'; + +const TASK_KEYS_TO_REMOVE = ['_id', 'completed', 'dateCompleted', 'history', 'id', 'streak', 'createdAt', 'challenge']; + +export function addUserJoinChallengeNotification (user) { + if (user.achievements.joinedChallenge) return; + user.achievements.joinedChallenge = true; + user.addNotification('CHALLENGE_JOINED_ACHIEVEMENT'); +} + +export function getChallengeGroupResponse (group) { + return { + _id: group._id, + name: group.name, + type: group.type, + privacy: group.privacy, + }; +} + +export async function createChallenge (user, req, res) { + let groupId = req.body.group; + let prize = req.body.prize; + + let group = await Group.getGroup({user, groupId, fields: '-chat', mustBeMember: true}); + if (!group) throw new NotFound(res.t('groupNotFound')); + if (!group.isMember(user)) throw new NotAuthorized(res.t('mustBeGroupMember')); + + if (group.leaderOnly && group.leaderOnly.challenges && group.leader !== user._id) { + throw new NotAuthorized(res.t('onlyGroupLeaderChal')); + } + + if (group._id === TAVERN_ID && prize < 1) { + throw new NotAuthorized(res.t('tavChalsMinPrize')); + } + + if (prize > 0) { + let groupBalance = group.balance && group.leader === user._id ? group.balance : 0; + let prizeCost = prize / 4; + + if (prizeCost > user.balance + groupBalance) { + throw new NotAuthorized(res.t('cantAfford')); + } + + if (groupBalance >= prizeCost) { + // Group pays for all of prize + group.balance -= prizeCost; + } else if (groupBalance > 0) { + // User pays remainder of prize cost after group + let remainder = prizeCost - group.balance; + group.balance = 0; + user.balance -= remainder; + } else { + // User pays for all of prize + user.balance -= prizeCost; + } + } + + group.challengeCount += 1; + + if (!req.body.summary) { + req.body.summary = req.body.name; + } + req.body.leader = user._id; + req.body.official = user.contributor.admin && req.body.official ? true : false; + let challenge = new Challenge(Challenge.sanitize(req.body)); + + // First validate challenge so we don't save group if it's invalid (only runs sync validators) + let challengeValidationErrors = challenge.validateSync(); + if (challengeValidationErrors) throw challengeValidationErrors; + + addUserJoinChallengeNotification(user); + + let results = await Bluebird.all([challenge.save({ + validateBeforeSave: false, // already validate + }), group.save()]); + let savedChal = results[0]; + + await savedChal.syncToUser(user); // (it also saves the user) + + return {savedChal, group}; +} + +export function cleanUpTask (task) { + let cleansedTask = omit(task, TASK_KEYS_TO_REMOVE); + + // Copy checklists but reset to uncomplete and assign new id + if (!cleansedTask.checklist) cleansedTask.checklist = []; + cleansedTask.checklist.forEach((item) => { + item.completed = false; + item.id = uuid(); + }); + + if (cleansedTask.type !== 'reward') { + delete cleansedTask.value; + } + + return cleansedTask; +} From 1c1b050e2c42f494573632fdc5ef9a2bcccb6968 Mon Sep 17 00:00:00 2001 From: Ignacio Alvarez Date: Mon, 19 Feb 2018 14:51:43 -0300 Subject: [PATCH 18/49] If message uuid is set as system append as user 'system message' #9985 (#9988) --- website/client/components/chat/copyAsTodoModal.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/client/components/chat/copyAsTodoModal.vue b/website/client/components/chat/copyAsTodoModal.vue index 7e61dd3523..b347fe13fe 100644 --- a/website/client/components/chat/copyAsTodoModal.vue +++ b/website/client/components/chat/copyAsTodoModal.vue @@ -38,7 +38,7 @@ export default { }, mounted () { this.$root.$on('habitica::copy-as-todo', message => { - const notes = `${message.user} wrote in [${this.groupName}](${baseUrl}/groups/guild/${this.groupId})`; + const notes = `${message.user || 'system message'}${message.user ? ' wrote' : ''} in [${this.groupName}](${baseUrl}/groups/guild/${this.groupId})`; const newTask = { text: message.text, type: 'todo', From 311a8989386e50d31ddea288e2086301f233dc09 Mon Sep 17 00:00:00 2001 From: Mateus Etto Date: Mon, 19 Feb 2018 14:52:07 -0300 Subject: [PATCH 19/49] Fix attributes box size being too small on Japanese (#9984) --- website/client/components/userMenu/profile.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/client/components/userMenu/profile.vue b/website/client/components/userMenu/profile.vue index 1e7a2c9a39..ee89f50450 100644 --- a/website/client/components/userMenu/profile.vue +++ b/website/client/components/userMenu/profile.vue @@ -525,7 +525,7 @@ div } .box { - width: 141px; + width: 148px; height: 84px; padding: .5em; margin: 0 auto; From 119f21ddce84461a7343e64c9fe79832a471fcfa Mon Sep 17 00:00:00 2001 From: Mike Tung Date: Mon, 19 Feb 2018 12:55:16 -0500 Subject: [PATCH 20/49] New PR for margins in stable - fixes #9651 (#9982) * made style change for pet item class to have more margins. * made style change for pet item class to have more margins. * files got added * encapsulated the items as stated in #9903 * #9903 moved the css to stable -> petitem.vue for better encapsulation. * saving pngs * update on files * updated files as requested in PR! * Revert "saving pngs" This reverts commit a38ea664e133ce7a24dc3f041e190d699cab7ee9. * made css changes just to pet slot * #9982 removed redundant css * #9982 applied scss to mount slots as well. * #9982 refactored scss to be in one place. * #9982 changed selector --- website/client/assets/scss/item.scss | 4 ++++ website/client/components/inventory/stable/mountItem.vue | 2 +- website/client/components/inventory/stable/petItem.vue | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/website/client/assets/scss/item.scss b/website/client/assets/scss/item.scss index 01021ec567..9b8bd68e79 100644 --- a/website/client/assets/scss/item.scss +++ b/website/client/assets/scss/item.scss @@ -19,6 +19,10 @@ margin-bottom: 8px; } +.item.pet-slot { + margin: 0 2px; +} + .item { position: relative; width: 94px; diff --git a/website/client/components/inventory/stable/mountItem.vue b/website/client/components/inventory/stable/mountItem.vue index 28dfc8a8fd..3d05e3d5c7 100644 --- a/website/client/components/inventory/stable/mountItem.vue +++ b/website/client/components/inventory/stable/mountItem.vue @@ -1,7 +1,7 @@