<commit_before>import jwt from 'koa-jwt'
import { hash, compare } from '../utilities/bcrypt-promise'
import KoaRouter from 'koa-router'
let router = KoaRouter()

// list available rooms
router.get('/api/account/rooms', async (ctx, next) => {
  let rooms = await ctx.db.all('SELECT * FROM rooms WHERE status = ?', ['open'])
  return ctx.body = rooms
})

// login
router.post('/api/account/login', async (ctx, next) => {
  let {email, password, roomId} = ctx.request.body

  // check presence of all fields
  if (!email || !password || !roomId) {
    ctx.status = 422
    return ctx.body = 'Email, password, and room are required'
  }

  email = email.trim().toLowerCase()

  // get user
  let user = await ctx.db.get('SELECT * FROM users WHERE email = ?', [email])

  // validate password
  if (!user || !await compare(password, user.password)) {
    ctx.status = 401
    return
  }

  // validate roomId
  let room = await ctx.db.get('SELECT * FROM rooms WHERE id = ?', [roomId])

  if (!room || room.status !== 'open') {
    ctx.status = 401
    return ctx.body = 'Invalid Room'
  }

  delete user.password
  user.roomId = room.id

  // JWT for API calls is stored in httpOnly cookie
  let token = jwt.sign(user, 'shared-secret')
  ctx.cookies.set('id_token', token, {httpOnly: true})

  // client also persists this to localStorage
  ctx.body = user
})

// logout
router.get('/api/account/logout', async (ctx, next) => {
  ctx.cookies.set('id_token', '.', {httpOnly: true})
  ctx.status = 200
})

// create
router.post('/api/account/create', async (ctx, next) => {
  let {name, email, newPassword, newPasswordConfirm} = ctx.request.body

  name = name.trim()
  email = email.trim().toLowerCase()

  // check presence of all fields
  if (!name || !email || !newPassword || !newPasswordConfirm) {
    ctx.status = 422
    return ctx.body = 'All fields are required'
  }

  // validate email
  if (!validateEmail(email)) {
    ctx.status = 422
    return ctx.body = 'Invalid email address'
  }

  // check for duplicate email
  if (await ctx.db.get('SELECT * FROM users WHERE email = ?', [email])) {
    ctx.status = 401
    return ctx.body = 'Email address is already registered'
  }

  // check that passwords match
  if (newPassword !== newPasswordConfirm) {
    ctx.status = 422
    return ctx.body = 'Passwords do not match'
  }

  let hashedPwd = await hash(newPassword, 10)
  let res = await ctx.db.run('INSERT INTO users (email, password, name) VALUES (?, ?, ?)', [email, hashedPwd, name])
  console.log(res)
  ctx.status = 200
})

// update
router.post('/api/account/update', async (ctx, next) => {
  // check jwt validity
  if (!ctx.state.user) {
    ctx.status = 401
    return ctx.body = 'Invalid token'
  }

  let user = await ctx.db.get('SELECT * FROM users WHERE id = ?', [ctx.state.user.id])

  if (!user) {
    ctx.status = 401
    return ctx.body = 'Invalid user id'
  }

  let {name, email, password, newPassword, newPasswordConfirm} = ctx.request.body

  name = name.trim()
  email = email.trim().toLowerCase()

  // check presence of required fields
  if (!name || !email || !password) {
    ctx.status = 422
    return ctx.body = 'Name, email and current password are required'
  }

  // validate current password
  if (!await compare(password, user.password)) {
    ctx.status = 401
    return ctx.body = 'Current password is incorrect'
  }

  // changing password?
  if (newPassword && newPasswordConfirm) {
    if (newPassword !== newPasswordConfirm) {
      ctx.status = 422
      return ctx.body = 'New passwords do not match'
    }

    password = await hash(newPassword, 10)
  } else {
    password = <PASSWORD>
  }

  // validate email
  if (!validateEmail(email)) {
    ctx.status = 422
    return ctx.body = 'Invalid email address'
  }

  // check for duplicate email
  if (await ctx.db.get('SELECT * FROM users WHERE id != ? AND email = ? COLLATE NOCASE ',
      [ctx.state.user.id, email])) {
    ctx.status = 401
    return ctx.body = 'Email address is already registered'
  }

  // do update
  let res = await ctx.db.run('UPDATE users SET name = ?, email = ?, password = ? WHERE id = ?',
    [name, email, password, ctx.state.user.id])

  // client will use this info in UI and
  // cache it in localStorage to persist reloads
  ctx.body = {
    name,
    email,
  }

  // generate new JWT
  let token = jwt.sign({
    id: ctx.state.user.id,
    name,
    email
  }, 'shared-secret')

  // store JWT in httpOnly cookie
  ctx.cookies.set('id_token', token, {httpOnly: true})
  ctx.status = 200
})

export default router

// email validation helper from
// http://www.moreofless.co.uk/validate-email-address-without-regex/
function validateEmail(email) {
  var at = email.indexOf( "@" )
  var dot = email.lastIndexOf( "\." )
  return email.length > 0 &&
         at > 0 &&
         dot > at + 1 &&
         dot < email.length &&
         email[at + 1] !== "." &&
         email.indexOf( " " ) === -1 &&
         email.indexOf( ".." ) === -1
}<commit_msg>Make Update Account response consistent with Login<commit_after>import jwt from 'koa-jwt'
import { hash, compare } from '../utilities/bcrypt-promise'
import KoaRouter from 'koa-router'
let router = KoaRouter()

// list available rooms
router.get('/api/account/rooms', async (ctx, next) => {
  let rooms = await ctx.db.all('SELECT * FROM rooms WHERE status = ?', ['open'])
  return ctx.body = rooms
})

// login
router.post('/api/account/login', async (ctx, next) => {
  let {email, password, roomId} = ctx.request.body

  // check presence of all fields
  if (!email || !password || !roomId) {
    ctx.status = 422
    return ctx.body = 'Email, password, and room are required'
  }

  email = email.trim().toLowerCase()

  // get user
  let user = await ctx.db.get('SELECT * FROM users WHERE email = ?', [email])

  // validate password
  if (!user || !await compare(password, user.password)) {
    ctx.status = 401
    return
  }

  // validate roomId
  let room = await ctx.db.get('SELECT * FROM rooms WHERE id = ?', [roomId])

  if (!room || room.status !== 'open') {
    ctx.status = 401
    return ctx.body = 'Invalid Room'
  }

  delete user.password
  user.roomId = room.id

  // JWT for API calls is stored in httpOnly cookie
  let token = jwt.sign(user, 'shared-secret')
  ctx.cookies.set('id_token', token, {httpOnly: true})

  // client also persists this to localStorage
  ctx.body = user
})

// logout
router.get('/api/account/logout', async (ctx, next) => {
  ctx.cookies.set('id_token', '.', {httpOnly: true})
  ctx.status = 200
})

// create
router.post('/api/account/create', async (ctx, next) => {
  let {name, email, newPassword, newPasswordConfirm} = ctx.request.body

  name = name.trim()
  email = email.trim().toLowerCase()

  // check presence of all fields
  if (!name || !email || !newPassword || !newPasswordConfirm) {
    ctx.status = 422
    return ctx.body = 'All fields are required'
  }

  // validate email
  if (!validateEmail(email)) {
    ctx.status = 422
    return ctx.body = 'Invalid email address'
  }

  // check for duplicate email
  if (await ctx.db.get('SELECT * FROM users WHERE email = ?', [email])) {
    ctx.status = 401
    return ctx.body = 'Email address is already registered'
  }

  // check that passwords match
  if (newPassword !== newPasswordConfirm) {
    ctx.status = 422
    return ctx.body = 'Passwords do not match'
  }

  let hashedPwd = await hash(newPassword, 10)
  let res = await ctx.db.run('INSERT INTO users (email, password, name) VALUES (?, ?, ?)', [email, hashedPwd, name])
  console.log(res)
  ctx.status = 200
})

// update
router.post('/api/account/update', async (ctx, next) => {
  // check jwt validity
  if (!ctx.state.user) {
    ctx.status = 401
    return ctx.body = 'Invalid token'
  }

  let user = await ctx.db.get('SELECT * FROM users WHERE id = ?', [ctx.state.user.id])

  if (!user) {
    ctx.status = 401
    return ctx.body = 'Invalid user id'
  }

  let {name, email, password, newPassword, newPasswordConfirm} = ctx.request.body

  name = name.trim()
  email = email.trim().toLowerCase()

  // check presence of required fields
  if (!name || !email || !password) {
    ctx.status = 422
    return ctx.body = 'Name, email and current password are required'
  }

  // validate current password
  if (!await compare(password, user.password)) {
    ctx.status = 401
    return ctx.body = 'Current password is incorrect'
  }

  // changing password?
  if (newPassword && newPasswordConfirm) {
    if (newPassword !== newPasswordConfirm) {
      ctx.status = 422
      return ctx.body = 'New passwords do not match'
    }

    password = await hash(newPassword, 10)
  } else {
    password = user.password
  }

  // validate email
  if (!validateEmail(email)) {
    ctx.status = 422
    return ctx.body = 'Invalid email address'
  }

  // check for duplicate email
  if (await ctx.db.get('SELECT * FROM users WHERE id != ? AND email = ? COLLATE NOCASE ',
      [ctx.state.user.id, email])) {
    ctx.status = 401
    return ctx.body = 'Email address is already registered'
  }

  // do update
  let res = await ctx.db.run('UPDATE users SET name = ?, email = ?, password = ? WHERE id = ?',
    [name, email, password, ctx.state.user.id])

  // return user (shape should match /login)
  user = await ctx.db.get('SELECT * FROM users WHERE email = ?', [email])

  delete user.password
  user.roomId = ctx.state.user.roomId

  // generate new JWT
  let token = jwt.sign(user, 'shared-secret')

  // store JWT in httpOnly cookie
  ctx.cookies.set('id_token', token, {httpOnly: true})

  // client also persists this to localStorage
  ctx.body = user
})

export default router

// email validation helper from
// http://www.moreofless.co.uk/validate-email-address-without-regex/
function validateEmail(email) {
  var at = email.indexOf( "@" )
  var dot = email.lastIndexOf( "\." )
  return email.length > 0 &&
         at > 0 &&
         dot > at + 1 &&
         dot < email.length &&
         email[at + 1] !== "." &&
         email.indexOf( " " ) === -1 &&
         email.indexOf( ".." ) === -1
}
{'fraction_non_alphanumeric': 0.14596581875993642, 'fraction_numerical': 0.011824324324324325, 'mean_word_length': 2.851894374282434, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 20, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1436352', 'n_tokens_mistral': 3342, 'n_tokens_neox': 3169, 'n_words': 964}