2018-02-17 17:11:24 +00:00
/ *
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
2025-06-12 00:20:11 +00:00
require ( '../../website/server/libs/api-v3/setupNconf' ) . default ( ) ;
2018-02-17 17:11:24 +00:00
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. \n Matching 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 ) ;
} ) ;