GeekPark/gpk_account

View on GitHub
app/models/user.rb

Summary

Maintainability
A
0 mins
Test Coverage
class User < ActiveRecord::Base
  include HasRole
  include HasAccessKey
  include SmartFilterable

  scope :yesterday, -> { where("DATE(created_at) = ?", Date.yesterday) }

  has_secure_password validations: false
  has_one  :preference
  has_many :authorizations, dependent: :destroy
  # has_many :cached_authorizations, class_name: "Authorization"
  has_many :devices
  has_many :notifications
  has_many :notifies, class_name: "Notification", foreign_key: "from_user_id"
  has_many :direct_messages
  has_many :access_tokens, -> { where revoked_at: nil },
           class_name: 'Doorkeeper::AccessToken',
    foreign_key: 'resource_owner_id'

  validates_absence_of :password,
                       message: 'please set the email or mobile first',
                       if: ->(user) { user.email.blank? && user.mobile.blank? }
  validates :mobile, uniqueness: true,
            format: { with: /\A\d{11}\z/,
                      message: 'only 11 numbers china mobile' },
            allow_nil: true
  validates :email, uniqueness: { case_sensitive: false },
            format: { with: /\A[^@]+@([^@\.]+\.)+[^@\.]+\z/ },
            allow_nil: true
  validates :nickname, length: { in: 2..20 }, allow_nil: true
  validates :password, length: { in: 6..32 }, allow_nil: true
  validates :city, format: { with: /\A\d{6}\z/ }, allow_nil: true

  enum gender: {
    male:                   'male',
    female:                 'female',
    not_sure:               'not_sure',
    prefer_not_to_disclose: 'prefer_not_to_disclose'
  }

  after_update :revoke_all, if: :password_digest_changed?
  after_create -> { Preference.create(user: self) }
  after_create :set_default_nickname
  # after_commit :flush_cache

  mount_uploader :avatar, AvatarUploader
  has_one_time_password

  class << self
    def find_by_email_or_mobile(param)
      return unless param
      q = param.to_s.downcase
      find_by('lower(email) = ? OR mobile = ?', q, q)
    end

    def create_with_omniauth(auth)
      skip_callback(:create, :after, :set_default_nickname)
      ActiveRecord::Base.transaction do
        user = User.new(nickname: auth['info']['nickname'],
                        remote_avatar_url: auth['info']['avatar'])
        user.save(validate: false)
        user.authorizations.create!(provider: auth['provider'],
                                    uid: auth['uid'])
        user
      end
    rescue ActiveRecord::RecordInvalid
      nil
    ensure
      set_callback(:create, :after, :set_default_nickname)
    end

    def cached_find(id)
      Rails.cache.fetch(['user', id], expires_in: 5.minutes) { find_by_id(id) }
    end
  end

  def authenticate(password)
    super(password)
  rescue BCrypt::Errors::InvalidHash
    nil
  end

  def generate_remember_token
    self.remember_token = SecureRandom.urlsafe_base64
    self.remember_token_created_at = Time.current

    save
    remember_token
  end

  def generate_identify_token
    token = SecureRandom.urlsafe_base64
    token.tap do |t|
      Rails.cache.write("identify_token:#{id}", t, expires_in: 1.hour)
    end
  end

  def identified?(token)
    (token.present? && token == Rails.cache.fetch("identify_token:#{id}")) ||
      is_old? ||
      sns_user?
  end

  def sns_user?
    email.blank? && mobile.blank?
  end

  def two_factor_qr
    update_attribute(:otp_secret_key, ROTP::Base32.random_base32)
    key = email.presence || mobile.presence || nickname.presence || 'noname'
    RQRCode::QRCode.new(provisioning_uri(key, issuer: 'GeekPark'))
                   .as_png(border_modules: 0).resize(200, 200).to_data_url
  end

  def two_factor_switch(code = nil)
    if two_factor_enable?
      update_attribute(:two_factor_enable, false)
    elsif otp_secret_key.present? && authenticate_otp(code.to_s, drift: 60)
      update_attribute(:two_factor_enable, true)
    end
    two_factor_enable?
  end

  def revoke_all
    access_tokens.each(&:revoke)
  end

  def unread_dm_between(user_id)
    DirectMessage.where('user_id = ? and to_user_id = ?', user_id, id).unread
  end

  def wechat_enabled
    authorizations.any? { |auth| auth.provider == 'wechat' }
  end

  def weibo_enabled
    authorizations.any? { |auth| auth.provider == 'weibo' }
  end

  def attributes
    super.merge('weibo_enabled': weibo_enabled,
                 'wechat_enabled': wechat_enabled)
  end

  def set_default_nickname
    update_columns(nickname: "极客#{SecureRandom.random_number(9_999_999).to_s.rjust(7,'0')}")
  end

  def cached_authorizations
    Rails.cache.fetch(['User', id, 'Authorization', updated_at.to_i]) do
      super.to_a
    end
  end

  private

    def flush_cache
      Rails.cache.delete(['user', id])
    end
end