diff --git a/migrations/archive/2024/2024_purge_invite_accepted.js b/migrations/archive/2024/20240806_purge_invite_accepted.js similarity index 100% rename from migrations/archive/2024/2024_purge_invite_accepted.js rename to migrations/archive/2024/20240806_purge_invite_accepted.js diff --git a/migrations/archive/2024/20241119_gem_caps_hourglasses.js b/migrations/archive/2024/20241119_gem_caps_hourglasses.js new file mode 100644 index 0000000000..9c9f5e1448 --- /dev/null +++ b/migrations/archive/2024/20241119_gem_caps_hourglasses.js @@ -0,0 +1,116 @@ +/* eslint-disable no-console */ +const MIGRATION_NAME = '20241119_gem_caps_hourglasses'; +import { model as User } from '../../../website/server/models/user'; + +const progressCount = 1000; +let count = 0; + +async function updateUser (user) { + count += 1; + if (count % progressCount === 0) console.warn(`${count} ${user._id}`); + + const { consecutive, customerId, dateTerminated, planId } = user.purchased.plan; + const isRecurring = customerId !== 'Gift' && !dateTerminated; + const updateOp = { + $set: { + migration: MIGRATION_NAME, + 'purchased.plan.consecutive.gemCapExtra': Math.max(2 * Math.ceil((consecutive.gemCapExtra + 1) / 2, 26)), + }, + $inc: {}, + }; + + let hourglassBonus = 0; + + if (isRecurring) { + await user.updateBalance( + 5, + 'admin_update_balance', + '', + 'Subscription Reward Migration', + ); + updateOp.$inc.balance = 5; + switch (planId) { + case 'basic': + case 'basic_earned': + case 'group_plan_auto': + hourglassBonus = 2; + break; + case 'basic_3mo': + case 'basic_6mo': + case 'google_6mo': + hourglassBonus = 4; + break; + case 'basic_12mo': + hourglassBonus = 12; + updateOp.$set['purchased.plan.hourglassPromoReceived'] = new Date(); + break; + default: + hourglassBonus = 0; + } + + if (hourglassBonus) { + updateOp.$inc['purchased.plan.consecutive.trinkets'] = hourglassBonus; + await user.updateHourglasses( + hourglassBonus, + 'admin_update_balance', + '', + 'Subscription Reward Migration', + ); + } + updateOp.$push = { + notifications: { + type: 'ITEM_RECEIVED', + data: { + icon: 'notif_subscriber_reward', + title: 'Thanks for being a subscriber!', + text: 'Enjoy these extra Mystic Hourglasses and Gems to celebrate our new benefits.', + }, + seen: false, + }, + }; + } + + return await User.updateOne( + { _id: user._id }, + updateOp, + ).exec(); +} + +export default async function processUsers () { + let query = { + migration: { $ne: MIGRATION_NAME }, + 'auth.local.username': { $in: [] }, // for test population + 'purchased.plan.customerId': { $exists: true }, + $or: [ + { 'purchased.plan.dateTerminated': { $exists: false } }, + { 'purchased.plan.dateTerminated': null }, + { 'purchased.plan.dateTerminated': { $gt: new Date() } }, + ], + }; + + const fields = { + _id: 1, + purchased: 1, + }; + + while (true) { // eslint-disable-line no-constant-condition + const users = await User // eslint-disable-line no-await-in-loop + .find(query) + .limit(250) + .sort({_id: 1}) + .select(fields) + .exec(); + + if (users.length === 0) { + console.warn('All appropriate users found and modified.'); + console.warn(`\n${count} users processed\n`); + break; + } else { + query._id = { + $gt: users[users.length - 1], + }; + } + + await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop + } +};