Merge branch 'master' into glitch-soc/merge-upstream
This commit is contained in:
		
						commit
						43a9ed3de8
					
				
					 20 changed files with 139 additions and 54 deletions
				
			
		| 
						 | 
					@ -49,7 +49,7 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
 | 
				
			||||||
      ResolveAccountWorker.perform_async(signed_request_account.acct)
 | 
					      ResolveAccountWorker.perform_async(signed_request_account.acct)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    DeliveryFailureTracker.track_inverse_success!(signed_request_account)
 | 
					    DeliveryFailureTracker.reset!(signed_request_account.inbox_url)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def process_payload
 | 
					  def process_payload
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,7 @@ module Admin
 | 
				
			||||||
      @followers_count = Follow.where(target_account: Account.where(domain: params[:id])).count
 | 
					      @followers_count = Follow.where(target_account: Account.where(domain: params[:id])).count
 | 
				
			||||||
      @reports_count   = Report.where(target_account: Account.where(domain: params[:id])).count
 | 
					      @reports_count   = Report.where(target_account: Account.where(domain: params[:id])).count
 | 
				
			||||||
      @blocks_count    = Block.where(target_account: Account.where(domain: params[:id])).count
 | 
					      @blocks_count    = Block.where(target_account: Account.where(domain: params[:id])).count
 | 
				
			||||||
      @available       = DeliveryFailureTracker.available?(Account.select(:shared_inbox_url).where(domain: params[:id]).first&.shared_inbox_url)
 | 
					      @available       = DeliveryFailureTracker.available?(params[:id])
 | 
				
			||||||
      @media_storage   = MediaAttachment.where(account: Account.where(domain: params[:id])).sum(:file_file_size)
 | 
					      @media_storage   = MediaAttachment.where(account: Account.where(domain: params[:id])).sum(:file_file_size)
 | 
				
			||||||
      @private_comment = @domain_block&.private_comment
 | 
					      @private_comment = @domain_block&.private_comment
 | 
				
			||||||
      @public_comment  = @domain_block&.public_comment
 | 
					      @public_comment  = @domain_block&.public_comment
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,7 +47,7 @@ module Admin::ActionLogsHelper
 | 
				
			||||||
        I18n.t('admin.action_logs.deleted_status')
 | 
					        I18n.t('admin.action_logs.deleted_status')
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    when 'Announcement'
 | 
					    when 'Announcement'
 | 
				
			||||||
      truncate(attributes['text'])
 | 
					      truncate(attributes['text'].is_a?(Array) ? attributes['text'].last : attributes['text'])
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
					import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
				
			||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 | 
					import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 | 
				
			||||||
import classNames from 'classnames';
 | 
					import classNames from 'classnames';
 | 
				
			||||||
import { vote, fetchPoll } from 'mastodon/actions/polls';
 | 
					import { vote } from 'mastodon/actions/polls';
 | 
				
			||||||
import Motion from 'mastodon/features/ui/util/optional_motion';
 | 
					import Motion from 'mastodon/features/ui/util/optional_motion';
 | 
				
			||||||
import spring from 'react-motion/lib/spring';
 | 
					import spring from 'react-motion/lib/spring';
 | 
				
			||||||
import escapeTextContentForBrowser from 'escape-html';
 | 
					import escapeTextContentForBrowser from 'escape-html';
 | 
				
			||||||
| 
						 | 
					@ -30,6 +30,7 @@ class Poll extends ImmutablePureComponent {
 | 
				
			||||||
    intl: PropTypes.object.isRequired,
 | 
					    intl: PropTypes.object.isRequired,
 | 
				
			||||||
    dispatch: PropTypes.func,
 | 
					    dispatch: PropTypes.func,
 | 
				
			||||||
    disabled: PropTypes.bool,
 | 
					    disabled: PropTypes.bool,
 | 
				
			||||||
 | 
					    refresh: PropTypes.func,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  state = {
 | 
					  state = {
 | 
				
			||||||
| 
						 | 
					@ -108,7 +109,7 @@ class Poll extends ImmutablePureComponent {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.props.dispatch(fetchPoll(this.props.poll.get('id')));
 | 
					    this.props.refresh();
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  renderOption (option, optionIndex, showResults) {
 | 
					  renderOption (option, optionIndex, showResults) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,21 @@
 | 
				
			||||||
import { connect } from 'react-redux';
 | 
					import { connect } from 'react-redux';
 | 
				
			||||||
 | 
					import { debounce } from 'lodash';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Poll from 'mastodon/components/poll';
 | 
					import Poll from 'mastodon/components/poll';
 | 
				
			||||||
 | 
					import { fetchPoll } from 'mastodon/actions/polls';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const mapDispatchToProps = (dispatch, { pollId }) => ({
 | 
				
			||||||
 | 
					  refresh: debounce(
 | 
				
			||||||
 | 
					    () => {
 | 
				
			||||||
 | 
					      dispatch(fetchPoll(pollId));
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    1000,
 | 
				
			||||||
 | 
					    { leading: true },
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mapStateToProps = (state, { pollId }) => ({
 | 
					const mapStateToProps = (state, { pollId }) => ({
 | 
				
			||||||
  poll: state.getIn(['polls', pollId]),
 | 
					  poll: state.getIn(['polls', pollId]),
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default connect(mapStateToProps)(Poll);
 | 
					export default connect(mapStateToProps, mapDispatchToProps)(Poll);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,47 +3,53 @@
 | 
				
			||||||
class DeliveryFailureTracker
 | 
					class DeliveryFailureTracker
 | 
				
			||||||
  FAILURE_DAYS_THRESHOLD = 7
 | 
					  FAILURE_DAYS_THRESHOLD = 7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def initialize(inbox_url)
 | 
					  def initialize(url_or_host)
 | 
				
			||||||
    @inbox_url = inbox_url
 | 
					    @host = url_or_host.start_with?('https://') || url_or_host.start_with?('http://') ? Addressable::URI.parse(url_or_host).normalized_host : url_or_host
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def track_failure!
 | 
					  def track_failure!
 | 
				
			||||||
    Redis.current.sadd(exhausted_deliveries_key, today)
 | 
					    Redis.current.sadd(exhausted_deliveries_key, today)
 | 
				
			||||||
    Redis.current.sadd('unavailable_inboxes', @inbox_url) if reached_failure_threshold?
 | 
					    UnavailableDomain.create(domain: @host) if reached_failure_threshold?
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def track_success!
 | 
					  def track_success!
 | 
				
			||||||
    Redis.current.del(exhausted_deliveries_key)
 | 
					    Redis.current.del(exhausted_deliveries_key)
 | 
				
			||||||
    Redis.current.srem('unavailable_inboxes', @inbox_url)
 | 
					    UnavailableDomain.find_by(domain: @host)&.destroy
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def days
 | 
					  def days
 | 
				
			||||||
    Redis.current.scard(exhausted_deliveries_key) || 0
 | 
					    Redis.current.scard(exhausted_deliveries_key) || 0
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  class << self
 | 
					  def available?
 | 
				
			||||||
    def filter(arr)
 | 
					    !UnavailableDomain.where(domain: @host).exists?
 | 
				
			||||||
      arr.reject(&method(:unavailable?))
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def unavailable?(url)
 | 
					  alias reset! track_success!
 | 
				
			||||||
      Redis.current.sismember('unavailable_inboxes', url)
 | 
					
 | 
				
			||||||
 | 
					  class << self
 | 
				
			||||||
 | 
					    def without_unavailable(urls)
 | 
				
			||||||
 | 
					      unavailable_domains_map = Rails.cache.fetch('unavailable_domains') { UnavailableDomain.pluck(:domain).each_with_object({}) { |domain, hash| hash[domain] = true } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      urls.reject do |url|
 | 
				
			||||||
 | 
					        host = Addressable::URI.parse(url).normalized_host
 | 
				
			||||||
 | 
					        unavailable_domains_map[host]
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def available?(url)
 | 
					    def available?(url)
 | 
				
			||||||
      !unavailable?(url)
 | 
					      new(url).available?
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def track_inverse_success!(from_account)
 | 
					    def reset!(url)
 | 
				
			||||||
      new(from_account.inbox_url).track_success! if from_account.inbox_url.present?
 | 
					      new(url).reset!
 | 
				
			||||||
      new(from_account.shared_inbox_url).track_success! if from_account.shared_inbox_url.present?
 | 
					 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def exhausted_deliveries_key
 | 
					  def exhausted_deliveries_key
 | 
				
			||||||
    "exhausted_deliveries:#{@inbox_url}"
 | 
					    "exhausted_deliveries:#{@host}"
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def today
 | 
					  def today
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -415,7 +415,7 @@ class Account < ApplicationRecord
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def inboxes
 | 
					    def inboxes
 | 
				
			||||||
      urls = reorder(nil).where(protocol: :activitypub).pluck(Arel.sql("distinct coalesce(nullif(accounts.shared_inbox_url, ''), accounts.inbox_url)"))
 | 
					      urls = reorder(nil).where(protocol: :activitypub).pluck(Arel.sql("distinct coalesce(nullif(accounts.shared_inbox_url, ''), accounts.inbox_url)"))
 | 
				
			||||||
      DeliveryFailureTracker.filter(urls)
 | 
					      DeliveryFailureTracker.without_unavailable(urls)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def search_for(terms, limit = 10, offset = 0)
 | 
					    def search_for(terms, limit = 10, offset = 0)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,6 @@ class AccountAlias < ApplicationRecord
 | 
				
			||||||
  belongs_to :account
 | 
					  belongs_to :account
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  validates :acct, presence: true, domain: { acct: true }
 | 
					  validates :acct, presence: true, domain: { acct: true }
 | 
				
			||||||
  validates :uri, presence: true
 | 
					 | 
				
			||||||
  validates :uri, uniqueness: { scope: :account_id }
 | 
					  validates :uri, uniqueness: { scope: :account_id }
 | 
				
			||||||
  validate :validate_target_account
 | 
					  validate :validate_target_account
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,7 +46,7 @@ class AccountAlias < ApplicationRecord
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def validate_target_account
 | 
					  def validate_target_account
 | 
				
			||||||
    if uri.nil?
 | 
					    if uri.blank?
 | 
				
			||||||
      errors.add(:acct, I18n.t('migrations.errors.not_found'))
 | 
					      errors.add(:acct, I18n.t('migrations.errors.not_found'))
 | 
				
			||||||
    elsif ActivityPub::TagManager.instance.uri_for(account) == uri
 | 
					    elsif ActivityPub::TagManager.instance.uri_for(account) == uri
 | 
				
			||||||
      errors.add(:acct, I18n.t('migrations.errors.move_to_self'))
 | 
					      errors.add(:acct, I18n.t('migrations.errors.move_to_self'))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
#  created_at   :datetime         not null
 | 
					#  created_at   :datetime         not null
 | 
				
			||||||
#  updated_at   :datetime         not null
 | 
					#  updated_at   :datetime         not null
 | 
				
			||||||
#  published_at :datetime
 | 
					#  published_at :datetime
 | 
				
			||||||
#  status_ids   :bigint           is an Array
 | 
					#  status_ids   :bigint(8)        is an Array
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Announcement < ApplicationRecord
 | 
					class Announcement < ApplicationRecord
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,7 @@ class Relay < ApplicationRecord
 | 
				
			||||||
    payload     = Oj.dump(follow_activity(activity_id))
 | 
					    payload     = Oj.dump(follow_activity(activity_id))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    update!(state: :pending, follow_activity_id: activity_id)
 | 
					    update!(state: :pending, follow_activity_id: activity_id)
 | 
				
			||||||
    DeliveryFailureTracker.new(inbox_url).track_success!
 | 
					    DeliveryFailureTracker.track_success!(inbox_url)
 | 
				
			||||||
    ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url)
 | 
					    ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,7 +36,7 @@ class Relay < ApplicationRecord
 | 
				
			||||||
    payload     = Oj.dump(unfollow_activity(activity_id))
 | 
					    payload     = Oj.dump(unfollow_activity(activity_id))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    update!(state: :idle, follow_activity_id: nil)
 | 
					    update!(state: :idle, follow_activity_id: nil)
 | 
				
			||||||
    DeliveryFailureTracker.new(inbox_url).track_success!
 | 
					    DeliveryFailureTracker.track_success!(inbox_url)
 | 
				
			||||||
    ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url)
 | 
					    ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										22
									
								
								app/models/unavailable_domain.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								app/models/unavailable_domain.rb
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					# == Schema Information
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Table name: unavailable_domains
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#  id         :bigint(8)        not null, primary key
 | 
				
			||||||
 | 
					#  domain     :string           default(""), not null
 | 
				
			||||||
 | 
					#  created_at :datetime         not null
 | 
				
			||||||
 | 
					#  updated_at :datetime         not null
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UnavailableDomain < ApplicationRecord
 | 
				
			||||||
 | 
					  include DomainNormalizable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  after_commit :reset_cache!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def reset_cache!
 | 
				
			||||||
 | 
					    Rails.cache.delete('unavailable_domains')
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -171,12 +171,12 @@
 | 
				
			||||||
            %th= t('admin.accounts.inbox_url')
 | 
					            %th= t('admin.accounts.inbox_url')
 | 
				
			||||||
            %td
 | 
					            %td
 | 
				
			||||||
              = @account.inbox_url
 | 
					              = @account.inbox_url
 | 
				
			||||||
              = fa_icon DeliveryFailureTracker.unavailable?(@account.inbox_url) ? 'times' : 'check'
 | 
					              = fa_icon DeliveryFailureTracker.available?(@account.inbox_url) ? 'check' : 'times'
 | 
				
			||||||
          %tr
 | 
					          %tr
 | 
				
			||||||
            %th= t('admin.accounts.shared_inbox_url')
 | 
					            %th= t('admin.accounts.shared_inbox_url')
 | 
				
			||||||
            %td
 | 
					            %td
 | 
				
			||||||
              = @account.shared_inbox_url
 | 
					              = @account.shared_inbox_url
 | 
				
			||||||
              = fa_icon DeliveryFailureTracker.unavailable?(@account.shared_inbox_url) ? 'times' : 'check'
 | 
					              = fa_icon DeliveryFailureTracker.available?(@account.shared_inbox_url) ? 'check': 'times'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  %div{ style: 'overflow: hidden' }
 | 
					  %div{ style: 'overflow: hidden' }
 | 
				
			||||||
    %div{ style: 'float: right' }
 | 
					    %div{ style: 'float: right' }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,7 @@ class ActivityPub::DeliveryWorker
 | 
				
			||||||
  HEADERS = { 'Content-Type' => 'application/activity+json' }.freeze
 | 
					  HEADERS = { 'Content-Type' => 'application/activity+json' }.freeze
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def perform(json, source_account_id, inbox_url, options = {})
 | 
					  def perform(json, source_account_id, inbox_url, options = {})
 | 
				
			||||||
    return if DeliveryFailureTracker.unavailable?(inbox_url)
 | 
					    return unless DeliveryFailureTracker.available?(inbox_url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @options        = options.with_indifferent_access
 | 
					    @options        = options.with_indifferent_access
 | 
				
			||||||
    @json           = json
 | 
					    @json           = json
 | 
				
			||||||
| 
						 | 
					@ -23,12 +23,14 @@ class ActivityPub::DeliveryWorker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    perform_request
 | 
					    perform_request
 | 
				
			||||||
  ensure
 | 
					  ensure
 | 
				
			||||||
 | 
					    if @inbox_url.present?
 | 
				
			||||||
      if @performed
 | 
					      if @performed
 | 
				
			||||||
        failure_tracker.track_success!
 | 
					        failure_tracker.track_success!
 | 
				
			||||||
      else
 | 
					      else
 | 
				
			||||||
        failure_tracker.track_failure!
 | 
					        failure_tracker.track_failure!
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,3 +23,9 @@ module Chewy
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ElasticSearch uses Faraday internally. Faraday interprets the
 | 
				
			||||||
 | 
					# http_proxy env variable by default which leads to issues when
 | 
				
			||||||
 | 
					# Mastodon is run with hidden services enabled, because
 | 
				
			||||||
 | 
					# ElasticSearch is *not* supposed to be accessed through a proxy
 | 
				
			||||||
 | 
					Faraday.ignore_env_proxy = true
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										9
									
								
								db/migrate/20200407201300_create_unavailable_domains.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								db/migrate/20200407201300_create_unavailable_domains.rb
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					class CreateUnavailableDomains < ActiveRecord::Migration[5.2]
 | 
				
			||||||
 | 
					  def change
 | 
				
			||||||
 | 
					    create_table :unavailable_domains do |t|
 | 
				
			||||||
 | 
					      t.string :domain, default: '', null: false, index: { unique: true }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      t.timestamps
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										21
									
								
								db/migrate/20200407202420_migrate_unavailable_inboxes.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								db/migrate/20200407202420_migrate_unavailable_inboxes.rb
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,21 @@
 | 
				
			||||||
 | 
					class MigrateUnavailableInboxes < ActiveRecord::Migration[5.2]
 | 
				
			||||||
 | 
					  disable_ddl_transaction!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def up
 | 
				
			||||||
 | 
					    urls = Redis.current.smembers('unavailable_inboxes')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    hosts = urls.map do |url|
 | 
				
			||||||
 | 
					      Addressable::URI.parse(url).normalized_host
 | 
				
			||||||
 | 
					    end.compact.uniq
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    UnavailableDomain.delete_all
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    hosts.each do |host|
 | 
				
			||||||
 | 
					      UnavailableDomain.create(domain: host)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Redis.current.del(*(['unavailable_inboxes'] + Redis.current.keys('exhausted_deliveries:*')))
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def down; end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# It's strongly recommended that you check this file into your version control system.
 | 
					# It's strongly recommended that you check this file into your version control system.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ActiveRecord::Schema.define(version: 2020_03_12_185443) do
 | 
					ActiveRecord::Schema.define(version: 2020_04_07_202420) do
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # These are extensions that must be enabled in order to support this database
 | 
					  # These are extensions that must be enabled in order to support this database
 | 
				
			||||||
  enable_extension "plpgsql"
 | 
					  enable_extension "plpgsql"
 | 
				
			||||||
| 
						 | 
					@ -772,6 +772,13 @@ ActiveRecord::Schema.define(version: 2020_03_12_185443) do
 | 
				
			||||||
    t.index ["uri"], name: "index_tombstones_on_uri"
 | 
					    t.index ["uri"], name: "index_tombstones_on_uri"
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  create_table "unavailable_domains", force: :cascade do |t|
 | 
				
			||||||
 | 
					    t.string "domain", default: "", null: false
 | 
				
			||||||
 | 
					    t.datetime "created_at", null: false
 | 
				
			||||||
 | 
					    t.datetime "updated_at", null: false
 | 
				
			||||||
 | 
					    t.index ["domain"], name: "index_unavailable_domains_on_domain", unique: true
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  create_table "user_invite_requests", force: :cascade do |t|
 | 
					  create_table "user_invite_requests", force: :cascade do |t|
 | 
				
			||||||
    t.bigint "user_id"
 | 
					    t.bigint "user_id"
 | 
				
			||||||
    t.text "text"
 | 
					    t.text "text"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								spec/fabricators/unavailable_domain_fabricator.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								spec/fabricators/unavailable_domain_fabricator.rb
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					Fabricator(:unavailable_domain) do
 | 
				
			||||||
 | 
					  domain { Faker::Internet.domain }
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -22,11 +22,11 @@ describe DeliveryFailureTracker do
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe '#track_failure!' do
 | 
					  describe '#track_failure!' do
 | 
				
			||||||
    it 'marks URL as unavailable after 7 days of being called' do
 | 
					    it 'marks URL as unavailable after 7 days of being called' do
 | 
				
			||||||
      6.times { |i| Redis.current.sadd('exhausted_deliveries:http://example.com/inbox', i) }
 | 
					      6.times { |i| Redis.current.sadd('exhausted_deliveries:example.com', i) }
 | 
				
			||||||
      subject.track_failure!
 | 
					      subject.track_failure!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      expect(subject.days).to eq 7
 | 
					      expect(subject.days).to eq 7
 | 
				
			||||||
      expect(described_class.unavailable?('http://example.com/inbox')).to be true
 | 
					      expect(described_class.available?('http://example.com/inbox')).to be false
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it 'repeated calls on the same day do not count' do
 | 
					    it 'repeated calls on the same day do not count' do
 | 
				
			||||||
| 
						 | 
					@ -37,35 +37,27 @@ describe DeliveryFailureTracker do
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe '.filter' do
 | 
					  describe '.without_unavailable' do
 | 
				
			||||||
    before do
 | 
					    before do
 | 
				
			||||||
      Redis.current.sadd('unavailable_inboxes', 'http://example.com/unavailable/inbox')
 | 
					      Fabricate(:unavailable_domain, domain: 'foo.bar')
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it 'removes URLs that are unavailable' do
 | 
					    it 'removes URLs that are unavailable' do
 | 
				
			||||||
      result = described_class.filter(['http://example.com/good/inbox', 'http://example.com/unavailable/inbox'])
 | 
					      results = described_class.without_unavailable(['http://example.com/good/inbox', 'http://foo.bar/unavailable/inbox'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      expect(result).to include('http://example.com/good/inbox')
 | 
					      expect(results).to include('http://example.com/good/inbox')
 | 
				
			||||||
      expect(result).to_not include('http://example.com/unavailable/inbox')
 | 
					      expect(results).to_not include('http://foo.bar/unavailable/inbox')
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe '.track_inverse_success!' do
 | 
					  describe '.reset!' do
 | 
				
			||||||
    let(:from_account) { Fabricate(:account, inbox_url: 'http://example.com/inbox', shared_inbox_url: 'http://example.com/shared/inbox') }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    before do
 | 
					    before do
 | 
				
			||||||
      Redis.current.sadd('unavailable_inboxes', 'http://example.com/inbox')
 | 
					      Fabricate(:unavailable_domain, domain: 'foo.bar')
 | 
				
			||||||
      Redis.current.sadd('unavailable_inboxes', 'http://example.com/shared/inbox')
 | 
					      described_class.reset!('https://foo.bar/inbox')
 | 
				
			||||||
 | 
					 | 
				
			||||||
      described_class.track_inverse_success!(from_account)
 | 
					 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it 'marks inbox URL as available again' do
 | 
					    it 'marks inbox URL as available again' do
 | 
				
			||||||
      expect(described_class.available?('http://example.com/inbox')).to be true
 | 
					      expect(described_class.available?('http://foo.bar/inbox')).to be true
 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it 'marks shared inbox URL as available again' do
 | 
					 | 
				
			||||||
      expect(described_class.available?('http://example.com/shared/inbox')).to be true
 | 
					 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										4
									
								
								spec/models/unavailable_domain_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								spec/models/unavailable_domain_spec.rb
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					require 'rails_helper'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RSpec.describe UnavailableDomain, type: :model do
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
		Loading…
	
		Reference in a new issue