mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-23 15:48:11 -05:00
Add script that migrates users with email-formatted usernames
This commit is contained in:
parent
76e461398e
commit
a5f18f88be
5 changed files with 293 additions and 4 deletions
167
scripts/node/fixEmailFormattedUsernames.coffee
Normal file
167
scripts/node/fixEmailFormattedUsernames.coffee
Normal file
|
@ -0,0 +1,167 @@
|
|||
# Usage:
|
||||
# > coffee -c scripts/node/fixEmailFormattedUsernames.coffee; node scripts/node/fixEmailFormattedUsernames.js run
|
||||
|
||||
require('coffee-script');
|
||||
require('coffee-script/register');
|
||||
|
||||
_ = require 'lodash'
|
||||
sendwithus = require '../../server/sendwithus'
|
||||
log = require 'winston'
|
||||
str = require 'underscore.string'
|
||||
co = require 'co'
|
||||
|
||||
filter = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,63}$/i
|
||||
|
||||
changedUsernameTemplate = _.template("
|
||||
<p>
|
||||
Hi, CodeCombat user!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Just letting you know we've made a change to your account settings which may change how you log in. Here are your old settings:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>Old username: <%= oldUsername %></li>
|
||||
<li>Old email: <%= oldEmail %></li>
|
||||
</ul>
|
||||
|
||||
</p>
|
||||
Your old username was an email address, but to reduce confusion, we now make sure email addresses can't be used as usernames.
|
||||
<b><%= specialMessage %></b>
|
||||
Here are your new settings:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>New username: <%= newUsername %></li>
|
||||
<li>New email: <%= newEmail %></li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Please <a href='https://codecombat.com/account/settings'>visit the site</a> if you would like to update your settings.
|
||||
And let us know if you have any questions!
|
||||
</p>
|
||||
")
|
||||
|
||||
exports.run = ->
|
||||
co ->
|
||||
mongoose = require 'mongoose'
|
||||
User = require '../../server/models/User'
|
||||
users = yield User.find({nameLower: {$regex: filter}}).select({name:1, email:1, anonymous:1, slug:1})
|
||||
console.log 'found', users.length, 'users'
|
||||
|
||||
for user in users
|
||||
oldUsername = user.get('name')
|
||||
oldEmail = user.get('email')
|
||||
newUsername = null
|
||||
newEmail = null
|
||||
specialMessage = ''
|
||||
|
||||
if not oldEmail
|
||||
otherUser = yield User.findByEmail(oldUsername)
|
||||
if otherUser
|
||||
specialMessage = "Since you had no email set, we would have made your old username your new email.
|
||||
But '#{oldUsername}' is already used by another account as an email by another account,
|
||||
so instead we changed your username."
|
||||
newUsername = str.slugify(oldUsername)
|
||||
newEmail = ''
|
||||
|
||||
else
|
||||
specialMessage = "Since you had no email set, we simply made your old username your new email instead."
|
||||
newEmail = oldUsername
|
||||
newUsername = ''
|
||||
|
||||
|
||||
else if oldEmail is oldUsername
|
||||
specialMessage = "Since your email and username are the same, we simply removed your username."
|
||||
newUsername = ''
|
||||
newEmail = oldEmail
|
||||
|
||||
|
||||
else if not filter.test(oldEmail)
|
||||
otherEmailUser = yield User.findByEmail(oldUsername)
|
||||
otherUsernameUser = yield User.findByName(oldEmail)
|
||||
if otherEmailUser
|
||||
specialMessage = "Since your old email looks like a username and your old username looks like an email,
|
||||
we would have swapped them on your account.
|
||||
But '#{oldUsername}' is already used as an email by another account,
|
||||
so instead we changed your username."
|
||||
newUsername = str.slugify(oldUsername)
|
||||
newEmail = oldEmail
|
||||
|
||||
else if otherUsernameUser
|
||||
specialMessage = "Since your old email looks like a username and your old username looks like an email,
|
||||
we would have swapped them on your account.
|
||||
But '#{oldEmail}' is already used as a username by another account,
|
||||
so instead we changed your username."
|
||||
newUsername = str.slugify(oldUsername)
|
||||
newEmail = oldEmail
|
||||
else
|
||||
specialMessage = "Since your old email looks like a username and your old username looks like an email,
|
||||
we swapped them on your account."
|
||||
newUsername = oldEmail
|
||||
newEmail = oldUsername
|
||||
|
||||
|
||||
else if oldUsername and oldEmail
|
||||
# Since oldEmail passed the email filter,
|
||||
specialMessage = "Since your old email is valid, we simply removed your username."
|
||||
newUsername = ''
|
||||
newEmail = oldEmail
|
||||
|
||||
|
||||
else
|
||||
console.log('unhandled user', user.toObject())
|
||||
throw new Error('Unhandled user')
|
||||
|
||||
user.set({name: newUsername, email: newEmail})
|
||||
console.log JSON.stringify({
|
||||
oldUsername, oldEmail, newUsername, newEmail, specialMessage, _id: user.id
|
||||
})
|
||||
yield user.save()
|
||||
|
||||
content = changedUsernameTemplate({
|
||||
oldUsername: oldUsername or '<i>(no username)</i>'
|
||||
oldEmail: oldEmail or '<i>(no email)</i>'
|
||||
newUsername: newUsername or '<i>(no username)</i>'
|
||||
newEmail: newEmail or '<i>(no email)</i>'
|
||||
specialMessage
|
||||
})
|
||||
|
||||
context =
|
||||
template: sendwithus.templates.plain_text_email
|
||||
recipient:
|
||||
address: oldUsername
|
||||
sender:
|
||||
address: 'team@codecombat.com'
|
||||
name: 'CodeCombat Team'
|
||||
template_data:
|
||||
subject: 'Your Username Has Changed'
|
||||
contentHTML: content
|
||||
|
||||
# Also send to the original email if it's valid
|
||||
if filter.test(oldEmail)
|
||||
context.cc = [
|
||||
{ address: oldEmail }
|
||||
]
|
||||
|
||||
yield sendwithus.api.sendAsync(context)
|
||||
|
||||
return 'Done'
|
||||
|
||||
if _.last(process.argv) is 'run'
|
||||
database = require '../../server/commons/database'
|
||||
mongoose = require 'mongoose'
|
||||
|
||||
### SET UP ###
|
||||
do (setupLodash = this) ->
|
||||
GLOBAL._ = require 'lodash'
|
||||
_.str = require 'underscore.string'
|
||||
_.mixin _.str.exports()
|
||||
GLOBAL.tv4 = require('tv4').tv4
|
||||
|
||||
database.connect()
|
||||
co ->
|
||||
yield exports.run()
|
||||
process.exit()
|
||||
|
|
@ -10,6 +10,7 @@ Classroom = require '../models/Classroom'
|
|||
languages = require '../routes/languages'
|
||||
_ = require 'lodash'
|
||||
errors = require '../commons/errors'
|
||||
Promise = require 'bluebird'
|
||||
|
||||
config = require '../../server_config'
|
||||
stripe = require('stripe')(config.stripe.secretKey)
|
||||
|
@ -270,6 +271,8 @@ UserSchema.statics.unconflictName = unconflictName = (name, done) ->
|
|||
suffix = _.random(0, 9) + ''
|
||||
unconflictName name + suffix, done
|
||||
|
||||
UserSchema.statics.unconflictNameAsync = Promise.promisify(unconflictName)
|
||||
|
||||
UserSchema.methods.sendWelcomeEmail = ->
|
||||
return if not @get('email')
|
||||
{ welcome_email_student, welcome_email_user } = sendwithus.templates
|
||||
|
@ -361,9 +364,10 @@ UserSchema.pre('save', (next) ->
|
|||
@set('email', undefined)
|
||||
@set('emailLower', undefined)
|
||||
if name = @get('name')
|
||||
filter = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,63}$/i # https://news.ycombinator.com/item?id=5763990
|
||||
if filter.test(name)
|
||||
return next(new errors.UnprocessableEntity('Name may not be an email'))
|
||||
if not @allowEmailNames # for testing
|
||||
filter = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,63}$/i # https://news.ycombinator.com/item?id=5763990
|
||||
if filter.test(name)
|
||||
return next(new errors.UnprocessableEntity('Name may not be an email'))
|
||||
|
||||
@set('nameLower', name.toLowerCase())
|
||||
else
|
||||
|
|
|
@ -122,4 +122,4 @@ module.exports.setup = (app) ->
|
|||
app.put('/db/trial.request/:handle', mw.auth.checkHasPermission(['admin']), mw.trialRequests.put)
|
||||
app.get('/db/trial.request/-/users', mw.auth.checkHasPermission(['admin']), mw.trialRequests.getUsers)
|
||||
|
||||
app.get('/healthcheck', mw.healthcheck)
|
||||
app.get('/healthcheck', mw.healthcheck)
|
||||
|
|
|
@ -2,6 +2,7 @@ config = require '../server_config'
|
|||
sendwithusAPI = require 'sendwithus'
|
||||
swuAPIKey = config.mail.sendwithusAPIKey
|
||||
log = require 'winston'
|
||||
Promise = require 'bluebird'
|
||||
|
||||
module.exports.setupRoutes = (app) ->
|
||||
return
|
||||
|
@ -14,6 +15,8 @@ module.exports.api =
|
|||
|
||||
if swuAPIKey
|
||||
module.exports.api = new sendwithusAPI swuAPIKey, debug
|
||||
|
||||
Promise.promisifyAll(module.exports.api)
|
||||
|
||||
module.exports.templates =
|
||||
parent_subscribe_email: 'tem_2APERafogvwKhmcnouigud'
|
||||
|
|
115
spec/server/scripts/fixEmailFormattedUsernames.spec.coffee
Normal file
115
spec/server/scripts/fixEmailFormattedUsernames.spec.coffee
Normal file
|
@ -0,0 +1,115 @@
|
|||
# Don't need to run this regularly, only running script once.
|
||||
|
||||
#utils = require '../utils'
|
||||
#User = require '../../../server/models/User'
|
||||
#request = require '../request'
|
||||
#sendwithus = require '../../../server/sendwithus'
|
||||
#fixEmailFormattedUsernames = require '../../../scripts/node/fixEmailFormattedUsernames'
|
||||
#
|
||||
#describe '/scripts/node/fixEmailFormattedUsernames', ->
|
||||
#
|
||||
# beforeEach utils.wrap (done) ->
|
||||
# yield utils.clearModels([User])
|
||||
# console.log('spy on send async')
|
||||
# spyOn(sendwithus.api, 'sendAsync').and.callThrough()
|
||||
# done()
|
||||
#
|
||||
# afterEach ->
|
||||
# expect(sendwithus.api.sendAsync).toHaveBeenCalled()
|
||||
#
|
||||
# describe "when a user has no email set", ->
|
||||
# beforeEach utils.wrap (done) ->
|
||||
# @user = new User({name: 'an@email.com', points:100})
|
||||
# @user.allowEmailNames = true
|
||||
# yield @user.save()
|
||||
# done()
|
||||
#
|
||||
# it 'moves the email-formatted username to be the user\'s email', utils.wrap (done) ->
|
||||
# yield fixEmailFormattedUsernames.run()
|
||||
# user = yield User.findById(@user.id)
|
||||
# expect(user.get('email')).toBe('an@email.com')
|
||||
# expect(user.get('name')).toBeUndefined()
|
||||
# expect(user.get('points')).toBe(100) # make sure properties aren't removed
|
||||
# done()
|
||||
#
|
||||
#
|
||||
# describe "when another user exists with that email", ->
|
||||
# beforeEach utils.wrap (done) ->
|
||||
# @otherUser = new User({email: 'an@email.com'})
|
||||
# yield @otherUser.save()
|
||||
# done()
|
||||
#
|
||||
# it "slugifies the target user's username", utils.wrap (done) ->
|
||||
# yield fixEmailFormattedUsernames.run()
|
||||
# user = yield User.findById(@user.id)
|
||||
# expect(user.get('email')).toBeUndefined()
|
||||
# expect(user.get('name')).toBe('anemailcom')
|
||||
# done()
|
||||
#
|
||||
# describe "when a user has the same email and username", ->
|
||||
# beforeEach utils.wrap (done) ->
|
||||
# @user = new User({name: 'an@email.com', email: 'an@email.com'})
|
||||
# @user.allowEmailNames = true
|
||||
# yield @user.save()
|
||||
# done()
|
||||
#
|
||||
# it "removes the user's username", utils.wrap (done) ->
|
||||
# yield fixEmailFormattedUsernames.run()
|
||||
# user = yield User.findById(@user.id)
|
||||
# expect(user.get('email')).toBe('an@email.com')
|
||||
# expect(user.get('name')).toBeUndefined()
|
||||
# done()
|
||||
#
|
||||
# describe "when the user has an email that isn't formatted like an email", ->
|
||||
# beforeEach utils.wrap (done) ->
|
||||
# @user = new User({name: 'an@email.com', email: 'a name'})
|
||||
# @user.allowEmailNames = true
|
||||
# yield @user.save()
|
||||
# done()
|
||||
#
|
||||
# it "swaps the two", utils.wrap (done) ->
|
||||
# yield fixEmailFormattedUsernames.run()
|
||||
# user = yield User.findById(@user.id)
|
||||
# expect(user.get('email')).toBe('an@email.com')
|
||||
# expect(user.get('name')).toBe('a name')
|
||||
# done()
|
||||
#
|
||||
# describe "when another user already has the email-formatted name as an email", ->
|
||||
# beforeEach utils.wrap (done) ->
|
||||
# @otherUser = new User({email: 'an@email.com'})
|
||||
# yield @otherUser.save()
|
||||
# done()
|
||||
#
|
||||
# it "slugifies the target user's username", utils.wrap (done) ->
|
||||
# yield fixEmailFormattedUsernames.run()
|
||||
# user = yield User.findById(@user.id)
|
||||
# expect(user.get('email')).toBe('a name')
|
||||
# expect(user.get('name')).toBe('anemailcom')
|
||||
# done()
|
||||
#
|
||||
# describe "when another user already has the non-email-formatted email as a username", ->
|
||||
# beforeEach utils.wrap (done) ->
|
||||
# @otherUser = new User({name: 'a name'})
|
||||
# yield @otherUser.save()
|
||||
# done()
|
||||
#
|
||||
# it "slugifies the target user's username", utils.wrap (done) ->
|
||||
# yield fixEmailFormattedUsernames.run()
|
||||
# user = yield User.findById(@user.id)
|
||||
# expect(user.get('email')).toBe('a name')
|
||||
# expect(user.get('name')).toBe('anemailcom')
|
||||
# done()
|
||||
#
|
||||
# describe "when the user has a different but well formatted email set", ->
|
||||
# beforeEach utils.wrap (done) ->
|
||||
# @user = new User({name: 'an@email.com', email: 'another@email.com'})
|
||||
# @user.allowEmailNames = true
|
||||
# yield @user.save()
|
||||
# done()
|
||||
#
|
||||
# it "removes the target user's username", utils.wrap (done) ->
|
||||
# yield fixEmailFormattedUsernames.run()
|
||||
# user = yield User.findById(@user.id)
|
||||
# expect(user.get('email')).toBe('another@email.com')
|
||||
# expect(user.get('name')).toBeUndefined()
|
||||
# done()
|
Loading…
Reference in a new issue