Ejemplo n.º 1
0
function UserFactory (sequelize, validator) {
  class User extends Model {
    static convertFromExternal (data) {
      return data
    }

    static convertToExternal (data) {
      delete data.password

      return data
    }

    static convertFromPersistent (data) {
      delete data.created_at
      delete data.updated_at
      data = _.omit(data, _.isNull)
      return data
    }

    static convertToPersistent (data) {
      return data
    }

    static createBodyParser () {
      const Self = this

      return function * (next) {
        let json = this.body
        const validationResult = Self.validateExternal(json)
        if (validationResult.valid !== true) {
          const message = validationResult.schema
            ? 'Body did not match schema ' + validationResult.schema
            : 'Body did not pass validation'
          throw new InvalidBodyError(message, validationResult.errors)
        }

        const model = new Self()
        model.setDataExternal(json)
        this.body = model

        yield next
      }
    }
  }

  User.validateExternal = validator.create('User')

  PersistentModelMixin(User, sequelize, {
    id: {
      type: Sequelize.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    username: {
      type: Sequelize.STRING,
      unique: true
    },
    account: {
      type: Sequelize.STRING,
      unique: true
    },
    password: Sequelize.STRING
  })

  return User
}
Ejemplo n.º 2
0
function PaymentFactory (sequelize, validator, container, User) {
  class Payment extends Model {
    static convertFromExternal (data) {
      return data
    }

    static convertToExternal (data) {
      return data
    }

    static convertFromPersistent (data) {
      delete data.updated_at
      data = _.omit(data, _.isNull)
      return data
    }

    static convertToPersistent (data) {
      return data
    }

    static createBodyParser () {
      const Self = this

      return function * (next) {
        let json = this.body
        const validationResult = Self.validateExternal(json)
        if (validationResult.valid !== true) {
          const message = validationResult.schema
            ? 'Body did not match schema ' + validationResult.schema
            : 'Body did not pass validation'
          throw new InvalidBodyError(message, validationResult.errors)
        }

        const model = new Self()
        model.setDataExternal(json)
        this.body = model

        yield next
      }
    }

    static getUserPayments (user, page, limit) {
      page = page > 0 ? Number(page) : 1
      limit = Number(limit)

      return Payment.DbModel.findAndCountAll({
        // This is how we get a flat object that includes user username
        attributes: {include: [
          [Sequelize.col('SourceUser.username'), 'sourceUserUsername'],
          [Sequelize.col('SourceUser.profile_picture'), 'sourceUserProfilePicture'],
          [Sequelize.col('DestinationUser.username'), 'destinationUserUsername'],
          [Sequelize.col('DestinationUser.profile_picture'), 'destinationUserProfilePicture']
        ]},
        where: {
          $or: [
            {source_user: user.id},
            {source_account: user.account},
            {destination_user: user.id},
            {destination_account: user.account}
          ]
        },
        limit: limit,
        offset: limit * (page - 1),
        include: [
          // attributes: [] because we want a flat object. See above
          { model: User.DbModel, as: 'SourceUser', attributes: [] },
          { model: User.DbModel, as: 'DestinationUser', attributes: [] }
        ],
        order: [
          ['created_at', 'DESC']
        ]
      })
    }

    static getPayment (transfer) {
      return Payment.findOne({
        attributes: {include: [
          [Sequelize.col('SourceUser.username'), 'sourceUserUsername']
        ]},
        where: {
          transfers: transfer
        },
        include: [{
          model: User.DbModel, as: 'SourceUser', attributes: []
        }]
      })
    }
  }

  Payment.validateExternal = validator.create('Payment')

  PersistentModelMixin(Payment, sequelize, {
    id: {
      type: Sequelize.UUID,
      primaryKey: true,
      defaultValue: Sequelize.UUIDV4
    },
    source_user: Sequelize.INTEGER,
    source_account: Sequelize.STRING(1024),
    destination_user: Sequelize.INTEGER,
    destination_account: Sequelize.STRING(1024),
    transfers: {
      type: Sequelize.STRING(512),
      unique: true
    },
    state: Sequelize.ENUM('pending', 'success', 'fail'),
    source_amount: Sequelize.STRING(1024), // TODO put the right type
    destination_amount: Sequelize.STRING(1024), // TODO put the right type
    message: Sequelize.STRING(1024), // TODO decide on the size
    created_at: Sequelize.DATE,
    completed_at: Sequelize.DATE
  })

  container.schedulePostConstructor((User) => {
    Payment.DbModel.belongsTo(User.DbModel, {
      foreignKey: 'source_user',
      as: 'SourceUser'
    })
    Payment.DbModel.belongsTo(User.DbModel, {
      foreignKey: 'destination_user',
      as: 'DestinationUser'
    })
  }, [ UserFactory ])

  return Payment
}
Ejemplo n.º 3
0
function UserFactory (sequelize, validator, ledger, config) {
  class User extends Model {
    static convertFromExternal (data) {
      return data
    }

    static convertToExternal (data) {
      delete data.password
      delete data.created_at
      delete data.updated_at

      if (data.profile_picture && data.profile_picture.indexOf('http') === -1) {
        data.profile_picture = config.data.getIn(['server', 'base_uri'])
          + '/users/' + data.username + '/profilepic'
      }

      return data
    }

    static convertFromPersistent (data) {
      data = _.omit(data, _.isNull)
      return data
    }

    static convertToPersistent (data) {
      return data
    }

    static createBodyParser () {
      const Self = this

      return function * (next) {
        let json = this.body
        const validationResult = Self.validateExternal(json)
        if (validationResult.valid !== true) {
          const message = validationResult.schema
            ? 'Body did not match schema ' + validationResult.schema
            : 'Body did not pass validation'
          throw new InvalidBodyError(message, validationResult.errors)
        }

        const model = new Self()
        model.setDataExternal(json)
        this.body = model

        yield next
      }
    }

    static getVerificationCode(email) {
      return config.generateSecret('verify' + email).toString('hex')
    }

    static getVerificationLink(username, email) {
      return config.data.get(['client_host']) + '/verify/' + username + '/' + User.getVerificationCode(email)
    }

    * changeEmail (email, verified) {
      if (this.email === email) return this

      this.email = email
      this.email_verified = verified || false

      const validationResult = User.validateExternal({
        email: email
      })

      // Invalid email
      if (validationResult.valid !== true) {
        const message = validationResult.schema
          ? 'Body did not match schema ' + validationResult.schema
          : 'Body did not pass validation'
        throw new InvalidBodyError(message)
      }

      try {
        // TODO verification
        yield this.save()
      } catch (e) {
        // Email is already taken by someone else
        if (e.name === 'SequelizeUniqueConstraintError') {
          throw new EmailTakenError("Email is already taken")
        }

        // Something else went wrong
        throw new ServerError("Failed to change the user email")
      }

      return this
    }

    * appendLedgerAccount (ledgerUser) {
      if (!ledgerUser) {
        ledgerUser = yield ledger.getAccount(this, true)
      }
      this.balance = Math.round(ledgerUser.balance * 100) / 100

      return this
    }

    generateForgotPasswordCode(date) {
      date = date || Math.floor(Date.now() / 1000)

      return date + '.' + config.generateSecret(+date + this.id + this.updated_at.toString()).toString('hex')
    }

    generateForgotPasswordLink() {
      return config.data.get(['client_host']) + '/change-password/' + this.username + '/' + this.generateForgotPasswordCode()
    }

    verifyForgotPasswordCode(code) {
      const parts = code.split('.')
      const date = parts[0]
      const hash = parts[1]

      if (!date || !hash) throw new InvalidBodyError("Invalid code")

      const currentDate = Math.floor(Date.now() / 1000)

      // Code is only valid for an hour
      if (currentDate > date + 3600) {
        // TODO should this be an invalid body error?
        throw new InvalidBodyError("The code has been expired")
      }

      if (code !== this.generateForgotPasswordCode(date)) {
        throw new InvalidBodyError("Invalid code")
      }
    }
  }

  User.validateExternal = validator.create('User')

  PersistentModelMixin(User, sequelize, {
    id: {
      type: Sequelize.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    username: {
      type: Sequelize.STRING,
      unique: true
    },
    account: {
      type: Sequelize.STRING,
      unique: true
    },
    name: {
      type: Sequelize.STRING
    },
    email: {
      type: Sequelize.STRING,
      unique: true
    },
    email_verified: {
      type: Sequelize.BOOLEAN
    },
    github_id: {
      type: Sequelize.INTEGER,
      unique: true
    },
    profile_picture: Sequelize.STRING
  })

  return User
}