about summary refs log tree commit diff
path: root/app/models/account_filter.rb
blob: 1666ea883a79c4d85ff010932e6f18c71d47d78f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# frozen_string_literal: true

class AccountFilter
  KEYS = %i(
    origin
    status
    role_ids
    username
    by_domain
    display_name
    email
    ip
    invited_by
    order
  ).freeze

  attr_reader :params

  def initialize(params)
    @params = params.to_h.symbolize_keys
  end

  def results
    scope = Account.includes(:account_stat, user: [:ips, :invite_request]).without_instance_actor.reorder(nil)

    relevant_params.each do |key, value|
      next if key.to_s == 'page'

      scope.merge!(scope_for(key, value)) if value.present?
    end

    scope
  end

  private

  def relevant_params
    params.tap do |args|
      args.delete(:origin) if origin_is_remote_and_domain_present?
    end
  end

  def origin_is_remote_and_domain_present?
    params[:origin] == 'remote' && params[:by_domain].present?
  end

  def scope_for(key, value)
    case key.to_s
    when 'origin'
      origin_scope(value)
    when 'role_ids'
      role_scope(value)
    when 'status'
      status_scope(value)
    when 'by_domain'
      Account.where(domain: value.to_s.strip)
    when 'username'
      Account.matches_username(value.to_s.strip)
    when 'display_name'
      Account.matches_display_name(value.to_s.strip)
    when 'email'
      accounts_with_users.merge(User.matches_email(value.to_s.strip))
    when 'ip'
      valid_ip?(value) ? accounts_with_users.merge(User.matches_ip(value).group('users.id, accounts.id')) : Account.none
    when 'invited_by'
      invited_by_scope(value)
    when 'order'
      order_scope(value)
    else
      raise Mastodon::InvalidParameterError, "Unknown filter: #{key}"
    end
  end

  def origin_scope(value)
    case value.to_s
    when 'local'
      Account.local
    when 'remote'
      Account.remote
    else
      raise Mastodon::InvalidParameterError, "Unknown origin: #{value}"
    end
  end

  def status_scope(value)
    case value.to_s
    when 'active'
      Account.without_suspended
    when 'pending'
      accounts_with_users.merge(User.pending)
    when 'suspended'
      Account.suspended
    when 'disabled'
      accounts_with_users.merge(User.disabled).without_suspended
    when 'silenced'
      Account.silenced
    when 'sensitized'
      Account.sensitized
    else
      raise Mastodon::InvalidParameterError, "Unknown status: #{value}"
    end
  end

  def order_scope(value)
    case value.to_s
    when 'active'
      accounts_with_users
        .left_joins(:account_stat)
        .order(
          Arel.sql(
            <<~SQL.squish
              COALESCE(users.current_sign_in_at, account_stats.last_status_at, to_timestamp(0)) DESC, accounts.id DESC
            SQL
          )
        )
    when 'recent'
      Account.recent
    else
      raise Mastodon::InvalidParameterError, "Unknown order: #{value}"
    end
  end

  def invited_by_scope(value)
    Account.left_joins(user: :invite).merge(Invite.where(user_id: value.to_s))
  end

  def role_scope(value)
    accounts_with_users.merge(User.where(role_id: Array(value).map(&:to_s)))
  end

  def accounts_with_users
    Account.left_joins(:user)
  end

  def valid_ip?(value)
    IPAddr.new(value.to_s) && true
  rescue IPAddr::InvalidAddressError
    false
  end
end