diff --git a/app/models/account.rb b/app/models/account.rb index 0f3d0dda2..105b77e04 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -50,15 +50,14 @@ class Account < ApplicationRecord # PuSH subscriptions has_many :subscriptions, dependent: :destroy - pg_search_scope :search_for, against: { username: 'A', domain: 'B' }, using: { tsearch: { prefix: true } } + pg_search_scope :search_for, against: { username: 'A', domain: 'B' }, + using: { tsearch: { prefix: true } } scope :remote, -> { where.not(domain: nil) } scope :local, -> { where(domain: nil) } scope :without_followers, -> { where('(select count(f.id) from follows as f where f.target_account_id = accounts.id) = 0') } scope :with_followers, -> { where('(select count(f.id) from follows as f where f.target_account_id = accounts.id) > 0') } - scope :expiring, -> (time) { where(subscription_expires_at: nil).or(where('subscription_expires_at < ?', time)).remote.with_followers } - - scope :with_counters, -> { select('accounts.*, (select count(f.id) from follows as f where f.target_account_id = accounts.id) as followers_count, (select count(f.id) from follows as f where f.account_id = accounts.id) as following_count, (select count(s.id) from statuses as s where s.account_id = accounts.id) as statuses_count') } + scope :expiring, ->(time) { where(subscription_expires_at: nil).or(where('subscription_expires_at < ?', time)).remote.with_followers } def follow!(other_account) active_relationships.where(target_account: other_account).first_or_create!(target_account: other_account) @@ -114,9 +113,15 @@ class Account < ApplicationRecord OStatus2::Subscription.new(remote_url, secret: secret, lease_seconds: 86_400 * 30, webhook: webhook_url, hub: hub_url) end - def ping!(atom_url, hubs) - return unless local? && !Rails.env.development? - OStatus2::Publication.new(atom_url, hubs).publish + def save_with_optional_avatar! + save! + rescue ActiveRecord::RecordInvalid => invalid + if invalid.record.errors[:avatar_file_size] || invalid[:avatar_content_type] + self.avatar = nil + retry + end + + raise invalid end def avatar_remote_url=(url) diff --git a/app/models/status.rb b/app/models/status.rb index 87d8249b1..1a37ea86f 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -29,7 +29,8 @@ class Status < ApplicationRecord default_scope { order('id desc') } - scope :with_counters, -> { select('statuses.*, (select count(r.id) from statuses as r where r.reblog_of_id = statuses.id) as reblogs_count, (select count(f.id) from favourites as f where f.status_id = statuses.id) as favourites_count') } + scope :remote, -> { where.not(uri: nil) } + scope :local, -> { where(uri: nil) } cache_associated :account, :media_attachments, :tags, :stream_entry, mentions: :account, reblog: [:account, :stream_entry, :tags, :media_attachments, mentions: :account], thread: :account diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 1ae1d5a80..e9a27f136 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -6,13 +6,16 @@ class SearchService < BaseService username, domain = query.gsub(/\A@/, '').split('@') - results = if domain.nil? - Account.search_for(username) - else - Account.search_for("#{username} #{domain}") - end + if domain.nil? + exact_match = Account.find_local(username) + results = Account.search_for(username) + else + exact_match = Account.find_remote(username, domain) + results = Account.search_for("#{username} #{domain}") + end - results = results.limit(limit) + results = results.limit(limit).to_a + results = [exact_match] + results.reject { |a| a.id == exact_match.id } if exact_match if resolve && results.empty? && !domain.nil? results = [FollowRemoteAccountService.new.call("#{username}@#{domain}")] diff --git a/app/services/unsubscribe_service.rb b/app/services/unsubscribe_service.rb new file mode 100644 index 000000000..1a951d1b4 --- /dev/null +++ b/app/services/unsubscribe_service.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class UnsubscribeService < BaseService + def call(account) + subscription = account.subscription(api_subscription_url(account.id)) + response = subscription.unsubscribe + + unless response.successful? + Rails.logger.debug "PuSH unsubscribe for #{account.acct} failed: #{response.message}" + end + + account.secret = '' + account.subscription_expires_at = nil + account.save! + rescue HTTP::Error, OpenSSL::SSL::SSLError + Rails.logger.debug "PuSH subscription request for #{account.acct} could not be made due to HTTP or SSL error" + end +end diff --git a/app/services/update_remote_profile_service.rb b/app/services/update_remote_profile_service.rb index 66d25dfeb..d961eda39 100644 --- a/app/services/update_remote_profile_service.rb +++ b/app/services/update_remote_profile_service.rb @@ -15,7 +15,8 @@ class UpdateRemoteProfileService < BaseService old_hub_url = account.hub_url account.hub_url = hub_link['href'] if !hub_link.nil? && !hub_link['href'].blank? && (hub_link['href'] != old_hub_url) - account.save! + + account.save_with_optional_avatar! SubscribeService.new.call(account) if resubscribe && (account.hub_url != old_hub_url) end diff --git a/app/workers/hub_ping_worker.rb b/app/workers/hub_ping_worker.rb index d9a9ff427..14a151ba0 100644 --- a/app/workers/hub_ping_worker.rb +++ b/app/workers/hub_ping_worker.rb @@ -6,6 +6,7 @@ class HubPingWorker def perform(account_id) account = Account.find(account_id) - account.ping!(account_url(account, format: 'atom'), [Rails.configuration.x.hub_url]) + return unless account.local? + OStatus2::Publication.new(account_url(account, format: 'atom'), [Rails.configuration.x.hub_url]).publish end end diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index 8730a64cd..a95a7258f 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -11,18 +11,9 @@ namespace :mastodon do namespace :push do desc 'Unsubscribes from PuSH updates of feeds nobody follows locally' task clear: :environment do - include RoutingHelper - Account.remote.without_followers.where.not(subscription_expires_at: nil).find_each do |a| Rails.logger.debug "PuSH unsubscribing from #{a.acct}" - - begin - a.subscription(api_subscription_url(a.id)).unsubscribe - rescue HTTP::Error, OpenSSL::SSL::SSLError - Rails.logger.debug "PuSH unsubscribing from #{a.acct} failed due to an HTTP or SSL error" - ensure - a.update!(secret: '', subscription_expires_at: nil) - end + UnsubscribeService.new.call(a) end end