diff options
Diffstat (limited to 'app/lib')
-rw-r--r-- | app/lib/email_validator.rb | 17 | ||||
-rw-r--r-- | app/lib/exceptions.rb | 1 | ||||
-rw-r--r-- | app/lib/feed_manager.rb | 107 | ||||
-rw-r--r-- | app/lib/inline_rabl_scope.rb | 17 |
4 files changed, 86 insertions, 56 deletions
diff --git a/app/lib/email_validator.rb b/app/lib/email_validator.rb index 856b8b1f7..06e9375f6 100644 --- a/app/lib/email_validator.rb +++ b/app/lib/email_validator.rb @@ -2,17 +2,30 @@ class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) - return if Rails.configuration.x.email_domains_blacklist.empty? - record.errors.add(attribute, I18n.t('users.invalid_email')) if blocked_email?(value) end private def blocked_email?(value) + on_blacklist?(value) || not_on_whitelist?(value) + end + + def on_blacklist?(value) + return false if Rails.configuration.x.email_domains_blacklist.blank? + domains = Rails.configuration.x.email_domains_blacklist.gsub('.', '\.') regexp = Regexp.new("@(.+\\.)?(#{domains})", true) value =~ regexp end + + def not_on_whitelist?(value) + return false if Rails.configuration.x.email_domains_whitelist.blank? + + domains = Rails.configuration.x.email_domains_whitelist.gsub('.', '\.') + regexp = Regexp.new("@(.+\\.)?(#{domains})", true) + + value !~ regexp + end end diff --git a/app/lib/exceptions.rb b/app/lib/exceptions.rb index 200da9fe1..9bc802c12 100644 --- a/app/lib/exceptions.rb +++ b/app/lib/exceptions.rb @@ -4,4 +4,5 @@ module Mastodon class Error < StandardError; end class NotPermittedError < Error; end class ValidationError < Error; end + class RaceConditionError < Error; end end diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index cd6ca1291..2cca1cefe 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -5,17 +5,17 @@ require 'singleton' class FeedManager include Singleton - MAX_ITEMS = 800 + MAX_ITEMS = 400 def key(type, id) "feed:#{type}:#{id}" end - def filter?(timeline_type, status, receiver) + def filter?(timeline_type, status, receiver_id) if timeline_type == :home - filter_from_home?(status, receiver) + filter_from_home?(status, receiver_id) elsif timeline_type == :mentions - filter_from_mentions?(status, receiver) + filter_from_mentions?(status, receiver_id) else false end @@ -50,10 +50,18 @@ class FeedManager def merge_into_timeline(from_account, into_account) timeline_key = key(:home, into_account.id) + query = from_account.statuses.limit(FeedManager::MAX_ITEMS / 4) - from_account.statuses.limit(MAX_ITEMS).each do |status| - next if status.direct_visibility? || filter?(:home, status, into_account) - redis.zadd(timeline_key, status.id, status.id) + if redis.zcard(timeline_key) >= FeedManager::MAX_ITEMS / 4 + oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true)&.first&.last&.to_i || 0 + query = query.where('id > ?', oldest_home_score) + end + + redis.pipelined do + query.each do |status| + next if status.direct_visibility? || filter?(:home, status, into_account) + redis.zadd(timeline_key, status.id, status.id) + end end trim(:home, into_account.id) @@ -61,31 +69,20 @@ class FeedManager def unmerge_from_timeline(from_account, into_account) timeline_key = key(:home, into_account.id) - - from_account.statuses.select('id').find_each do |status| - redis.zrem(timeline_key, status.id) - redis.zremrangebyscore(timeline_key, status.id, status.id) + oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true)&.first&.last&.to_i || 0 + + from_account.statuses.select('id').where('id > ?', oldest_home_score).find_in_batches do |statuses| + redis.pipelined do + statuses.each do |status| + redis.zrem(timeline_key, status.id) + redis.zremrangebyscore(timeline_key, status.id, status.id) + end + end end end def inline_render(target_account, template, object) - rabl_scope = Class.new do - include RoutingHelper - - def initialize(account) - @account = account - end - - def current_user - @account.try(:user) - end - - def current_account - @account - end - end - - Rabl::Renderer.new(template, object, view_path: 'app/views', format: :json, scope: rabl_scope.new(target_account)).render + Rabl::Renderer.new(template, object, view_path: 'app/views', format: :json, scope: InlineRablScope.new(target_account)).render end private @@ -94,38 +91,40 @@ class FeedManager Redis.current end - def filter_from_home?(status, receiver) - return true if receiver.muting?(status.account) - - should_filter = false - - if status.reply? && status.in_reply_to_id.nil? - should_filter = true - elsif status.reply? && !status.in_reply_to_account_id.nil? # Filter out if it's a reply - should_filter = !receiver.following?(status.in_reply_to_account) # and I'm not following the person it's a reply to - should_filter &&= !(receiver.id == status.in_reply_to_account_id) # and it's not a reply to me - should_filter &&= !(status.account_id == status.in_reply_to_account_id) # and it's not a self-reply - elsif status.reblog? # Filter out a reblog - should_filter = receiver.blocking?(status.reblog.account) # if I'm blocking the reblogged person - should_filter ||= receiver.muting?(status.reblog.account) # or muting that person - should_filter ||= status.reblog.account.blocking?(receiver) # or if the author of the reblogged status is blocking me - end + def filter_from_home?(status, receiver_id) + return true if status.reply? && status.in_reply_to_id.nil? - should_filter ||= receiver.blocking?(status.mentions.map(&:account_id)) # or if it mentions someone I blocked + check_for_mutes = [status.account_id] + check_for_mutes.concat([status.reblog.account_id]) if status.reblog? - should_filter - end + return true if Mute.where(account_id: receiver_id, target_account_id: check_for_mutes).any? - def filter_from_mentions?(status, receiver) - should_filter = receiver.id == status.account_id # Filter if I'm mentioning myself - should_filter ||= receiver.blocking?(status.account) # or it's from someone I blocked - should_filter ||= receiver.blocking?(status.mentions.includes(:account).map(&:account)) # or if it mentions someone I blocked - should_filter ||= (status.account.silenced? && !receiver.following?(status.account)) # of if the account is silenced and I'm not following them + check_for_blocks = status.mentions.map(&:account_id) + check_for_blocks.concat([status.reblog.account_id]) if status.reblog? - if status.reply? && !status.in_reply_to_account_id.nil? # or it's a reply - should_filter ||= receiver.blocking?(status.in_reply_to_account) # to a user I blocked + return true if Block.where(account_id: receiver_id, target_account_id: check_for_blocks).any? + + if status.reply? && !status.in_reply_to_account_id.nil? # Filter out if it's a reply + should_filter = !Follow.where(account_id: receiver_id, target_account_id: status.in_reply_to_account_id).exists? # and I'm not following the person it's a reply to + should_filter &&= !(receiver_id == status.in_reply_to_account_id) # and it's not a reply to me + should_filter &&= !(status.account_id == status.in_reply_to_account_id) # and it's not a self-reply + return should_filter + elsif status.reblog? # Filter out a reblog + return Block.where(account_id: status.reblog.account_id, target_account_id: receiver_id).exists? # or if the author of the reblogged status is blocking me end + false + end + + def filter_from_mentions?(status, receiver_id) + check_for_blocks = [status.account_id] + check_for_blocks.concat(status.mentions.pluck(:account_id)) + check_for_blocks.concat([status.in_reply_to_account]) if status.reply? && !status.in_reply_to_account_id.nil? + + should_filter = receiver_id == status.account_id # Filter if I'm mentioning myself + should_filter ||= Block.where(account_id: receiver_id, target_account_id: check_for_blocks).any? # or it's from someone I blocked, in reply to someone I blocked, or mentioning someone I blocked + should_filter ||= (status.account.silenced? && !Follow.where(account_id: receiver_id, target_account_id: status.account_id).exists?) # of if the account is silenced and I'm not following them + should_filter end end diff --git a/app/lib/inline_rabl_scope.rb b/app/lib/inline_rabl_scope.rb new file mode 100644 index 000000000..26adcb03a --- /dev/null +++ b/app/lib/inline_rabl_scope.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class InlineRablScope + include RoutingHelper + + def initialize(account) + @account = account + end + + def current_user + @account.try(:user) + end + + def current_account + @account + end +end |