From 216b85b053d091306e3311a21f5b050f70a75130 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 14 Dec 2020 09:06:34 +0100 Subject: Fix performance on instances list in admin UI (#15282) - Reduce duplicate queries - Remove n+1 queries - Add accounts count to detailed view - Add separate action log entry for updating existing domain blocks --- lib/mastodon/domains_cli.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/mastodon/domains_cli.rb b/lib/mastodon/domains_cli.rb index 5433ddd9d..3c2dfd4ec 100644 --- a/lib/mastodon/domains_cli.rb +++ b/lib/mastodon/domains_cli.rb @@ -53,6 +53,8 @@ module Mastodon custom_emojis_count = custom_emojis.count custom_emojis.destroy_all unless options[:dry_run] + Instance.refresh unless options[:dry_run] + say("Removed #{custom_emojis_count} custom emojis", :green) end @@ -83,7 +85,7 @@ module Mastodon processed = Concurrent::AtomicFixnum.new(0) failed = Concurrent::AtomicFixnum.new(0) start_at = Time.now.to_f - seed = start ? [start] : Account.remote.domains + seed = start ? [start] : Instance.pluck(:domain) blocked_domains = Regexp.new('\\.?' + DomainBlock.where(severity: 1).pluck(:domain).join('|') + '$') progress = create_progress_bar -- cgit From 1045549f85041b13002801808b30a332c3a68c61 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 15 Dec 2020 12:55:29 +0100 Subject: Add stoplight for object storage failures, return HTTP 503 (#13043) --- app/controllers/api/base_controller.rb | 2 +- app/controllers/application_controller.rb | 2 +- app/lib/activitypub/activity/create.rb | 4 ++++ config/initializers/paperclip.rb | 11 +++++++++++ lib/paperclip/attachment_extensions.rb | 17 +++++++++++++++++ 5 files changed, 34 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index fe199e689..85f4cc768 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -40,7 +40,7 @@ class Api::BaseController < ApplicationController render json: { error: 'This action is not allowed' }, status: 403 end - rescue_from Mastodon::RaceConditionError do + rescue_from Mastodon::RaceConditionError, Seahorse::Client::NetworkingError, Stoplight::Error::RedLight do render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503 end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2201e463e..44616d6e5 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -28,7 +28,7 @@ class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordNotFound, with: :not_found rescue_from Mastodon::NotPermittedError, with: :forbidden rescue_from HTTP::Error, OpenSSL::SSL::SSLError, with: :internal_server_error - rescue_from Mastodon::RaceConditionError, with: :service_unavailable + rescue_from Mastodon::RaceConditionError, Seahorse::Client::NetworkingError, Stoplight::Error::RedLight, with: :service_unavailable rescue_from Mastodon::RateLimitExceededError, with: :too_many_requests before_action :store_current_location, except: :raise_not_found, unless: :devise_controller? diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index c77f237f9..612744676 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -228,6 +228,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity emoji ||= CustomEmoji.new(domain: @account.domain, shortcode: shortcode, uri: uri) emoji.image_remote_url = image_url emoji.save + rescue Seahorse::Client::NetworkingError + nil end def process_attachments @@ -250,6 +252,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity media_attachment.save rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError RedownloadMediaWorker.perform_in(rand(30..600).seconds, media_attachment.id) + rescue Seahorse::Client::NetworkingError + nil end end diff --git a/config/initializers/paperclip.rb b/config/initializers/paperclip.rb index 25adcd8d6..9ad7fd814 100644 --- a/config/initializers/paperclip.rb +++ b/config/initializers/paperclip.rb @@ -113,3 +113,14 @@ else end Paperclip.options[:content_type_mappings] = { csv: Import::FILE_TYPES } + +# In some places in the code, we rescue this exception, but we don't always +# load the S3 library, so it may be an undefined constant: + +unless defined?(Seahorse) + module Seahorse + module Client + class NetworkingError < StandardError; end + end + end +end diff --git a/lib/paperclip/attachment_extensions.rb b/lib/paperclip/attachment_extensions.rb index 752e79e65..94f7769b6 100644 --- a/lib/paperclip/attachment_extensions.rb +++ b/lib/paperclip/attachment_extensions.rb @@ -39,6 +39,23 @@ module Paperclip def default_url(style_name = default_style) @url_generator.for_as_default(style_name) end + + STOPLIGHT_THRESHOLD = 10 + STOPLIGHT_COOLDOWN = 30 + + # We overwrite this method to put a circuit breaker around + # calls to object storage, to stop hitting APIs that are slow + # to respond or don't respond at all and as such minimize the + # impact of object storage outages on application throughput + def save + Stoplight('object-storage') { super }.with_threshold(STOPLIGHT_THRESHOLD).with_cool_off_time(STOPLIGHT_COOLDOWN).with_error_handler do |error, handle| + if error.is_a?(Seahorse::Client::NetworkingError) + handle.call(error) + else + raise error + end + end.run + end end end -- cgit From 8485c436d5d083c28df8c942fe521bfb46edfc9f Mon Sep 17 00:00:00 2001 From: ThibG Date: Tue, 15 Dec 2020 12:57:24 +0100 Subject: Bump max supported schema version in maintenance script (#15349) Co-authored-by: Claire --- lib/mastodon/maintenance_cli.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/mastodon/maintenance_cli.rb b/lib/mastodon/maintenance_cli.rb index 99d13f43d..ffad3f6a2 100644 --- a/lib/mastodon/maintenance_cli.rb +++ b/lib/mastodon/maintenance_cli.rb @@ -14,7 +14,7 @@ module Mastodon end MIN_SUPPORTED_VERSION = 2019_10_01_213028 - MAX_SUPPORTED_VERSION = 2020_10_17_234926 + MAX_SUPPORTED_VERSION = 2020_12_06_004238 # Stubs to enjoy ActiveRecord queries while not depending on a particular # version of the code/database -- cgit From 75944a2f2dc83d1b12fa7cbddb18f673513fbb45 Mon Sep 17 00:00:00 2001 From: ThibG Date: Fri, 18 Dec 2020 09:46:59 +0100 Subject: Bump max supported schema version in maintenance script (#15359) Co-authored-by: Claire --- lib/mastodon/maintenance_cli.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/mastodon/maintenance_cli.rb b/lib/mastodon/maintenance_cli.rb index ffad3f6a2..822051ceb 100644 --- a/lib/mastodon/maintenance_cli.rb +++ b/lib/mastodon/maintenance_cli.rb @@ -14,7 +14,7 @@ module Mastodon end MIN_SUPPORTED_VERSION = 2019_10_01_213028 - MAX_SUPPORTED_VERSION = 2020_12_06_004238 + MAX_SUPPORTED_VERSION = 2020_12_18_054746 # Stubs to enjoy ActiveRecord queries while not depending on a particular # version of the code/database -- cgit From a60d9335d8e7c4aa070f081719ee2a438b0e0202 Mon Sep 17 00:00:00 2001 From: ThibG Date: Fri, 18 Dec 2020 23:26:26 +0100 Subject: Fix resolving accounts sometimes creating duplicate records for a given AP id (#15364) * Fix ResolveAccountService accepting mismatching acct: URI * Set attributes that should be updated regardless of suspension * Fix key fetching * Automatically merge remote accounts with duplicate `uri` * Add tests * Add "tootctl accounts fix-duplicates" Finds duplicate accounts sharing a same ActivityPub `id`, re-fetch them and merge them under the canonical `acct:` URI. Co-authored-by: Claire --- .../activitypub/fetch_remote_account_service.rb | 2 +- .../activitypub/process_account_service.rb | 28 ++++++++--- app/services/resolve_account_service.rb | 17 ++----- app/workers/account_merging_worker.rb | 18 +++++++ lib/mastodon/accounts_cli.rb | 19 ++++++++ spec/services/resolve_account_service_spec.rb | 56 ++++++++++++++++++++-- 6 files changed, 116 insertions(+), 24 deletions(-) create mode 100644 app/workers/account_merging_worker.rb (limited to 'lib') diff --git a/app/services/activitypub/fetch_remote_account_service.rb b/app/services/activitypub/fetch_remote_account_service.rb index e5bd0c47c..9d01f5386 100644 --- a/app/services/activitypub/fetch_remote_account_service.rb +++ b/app/services/activitypub/fetch_remote_account_service.rb @@ -28,7 +28,7 @@ class ActivityPub::FetchRemoteAccountService < BaseService return unless only_key || verified_webfinger? - ActivityPub::ProcessAccountService.new.call(@username, @domain, @json, only_key: only_key) + ActivityPub::ProcessAccountService.new.call(@username, @domain, @json, only_key: only_key, verified_webfinger: !only_key) rescue Oj::ParseError nil end diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index 4cb8e09db..6afeb92d6 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -28,6 +28,8 @@ class ActivityPub::ProcessAccountService < BaseService update_account process_tags process_attachments + + process_duplicate_accounts! if @options[:verified_webfinger] else raise Mastodon::RaceConditionError end @@ -69,34 +71,42 @@ class ActivityPub::ProcessAccountService < BaseService @account.protocol = :activitypub set_suspension! + set_immediate_protocol_attributes! + set_fetchable_key! unless @account.suspended? && @account.suspension_origin_local? set_immediate_attributes! unless @account.suspended? - set_fetchable_attributes! unless @options[:only_keys] || @account.suspended? + set_fetchable_attributes! unless @options[:only_key] || @account.suspended? @account.save_with_optional_media! end - def set_immediate_attributes! + def set_immediate_protocol_attributes! @account.inbox_url = @json['inbox'] || '' @account.outbox_url = @json['outbox'] || '' @account.shared_inbox_url = (@json['endpoints'].is_a?(Hash) ? @json['endpoints']['sharedInbox'] : @json['sharedInbox']) || '' @account.followers_url = @json['followers'] || '' - @account.featured_collection_url = @json['featured'] || '' - @account.devices_url = @json['devices'] || '' @account.url = url || @uri @account.uri = @uri + @account.actor_type = actor_type + end + + def set_immediate_attributes! + @account.featured_collection_url = @json['featured'] || '' + @account.devices_url = @json['devices'] || '' @account.display_name = @json['name'] || '' @account.note = @json['summary'] || '' @account.locked = @json['manuallyApprovesFollowers'] || false @account.fields = property_values || {} @account.also_known_as = as_array(@json['alsoKnownAs'] || []).map { |item| value_or_id(item) } - @account.actor_type = actor_type @account.discoverable = @json['discoverable'] || false end + def set_fetchable_key! + @account.public_key = public_key || '' + end + def set_fetchable_attributes! @account.avatar_remote_url = image_url('icon') || '' unless skip_download? @account.header_remote_url = image_url('image') || '' unless skip_download? - @account.public_key = public_key || '' @account.statuses_count = outbox_total_items if outbox_total_items.present? @account.following_count = following_total_items if following_total_items.present? @account.followers_count = followers_total_items if followers_total_items.present? @@ -140,6 +150,12 @@ class ActivityPub::ProcessAccountService < BaseService VerifyAccountLinksWorker.perform_async(@account.id) end + def process_duplicate_accounts! + return unless Account.where(uri: @account.uri).where.not(id: @account.id).exists? + + AccountMergingWorker.perform_async(@account.id) + end + def actor_type if @json['type'].is_a?(Array) @json['type'].find { |type| ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES.include?(type) } diff --git a/app/services/resolve_account_service.rb b/app/services/resolve_account_service.rb index 74b0b82d0..3301aaf51 100644 --- a/app/services/resolve_account_service.rb +++ b/app/services/resolve_account_service.rb @@ -49,7 +49,7 @@ class ResolveAccountService < BaseService # Now it is certain, it is definitely a remote account, and it # either needs to be created, or updated from fresh data - process_account! + fetch_account! rescue Webfinger::Error, Oj::ParseError => e Rails.logger.debug "Webfinger query for #{@uri} failed: #{e}" nil @@ -104,16 +104,12 @@ class ResolveAccountService < BaseService acct.gsub(/\Aacct:/, '').split('@') end - def process_account! + def fetch_account! return unless activitypub_ready? RedisLock.acquire(lock_options) do |lock| if lock.acquired? - @account = Account.find_remote(@username, @domain) - - next if actor_json.nil? - - @account = ActivityPub::ProcessAccountService.new.call(@username, @domain, actor_json) + @account = ActivityPub::FetchRemoteAccountService.new.call(actor_url) else raise Mastodon::RaceConditionError end @@ -136,13 +132,6 @@ class ResolveAccountService < BaseService @actor_url ||= @webfinger.link('self', 'href') end - def actor_json - return @actor_json if defined?(@actor_json) - - json = fetch_resource(actor_url, false) - @actor_json = supported_context?(json) && equals_or_includes_any?(json['type'], ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) ? json : nil - end - def gone_from_origin? @gone end diff --git a/app/workers/account_merging_worker.rb b/app/workers/account_merging_worker.rb new file mode 100644 index 000000000..8c234e7ac --- /dev/null +++ b/app/workers/account_merging_worker.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class AccountMergingWorker + include Sidekiq::Worker + + sidekiq_options queue: 'pull' + + def perform(account_id) + account = Account.find(account_id) + + return true if account.nil? || account.local? + + Account.where(uri: account.uri).where.not(id: account.id).find_each do |duplicate| + account.merge_with!(duplicate) + duplicate.destroy + end + end +end diff --git a/lib/mastodon/accounts_cli.rb b/lib/mastodon/accounts_cli.rb index bef4093a8..474643869 100644 --- a/lib/mastodon/accounts_cli.rb +++ b/lib/mastodon/accounts_cli.rb @@ -236,6 +236,25 @@ module Mastodon say('OK', :green) end + desc 'fix-duplicates', 'Find duplicate remote accounts and merge them' + option :dry_run, type: :boolean + long_desc <<-LONG_DESC + Merge known remote accounts sharing an ActivityPub actor identifier. + + Such duplicates can occur when a remote server admin misconfigures their + domain configuration. + LONG_DESC + def fix_duplicates + Account.remote.select(:uri, 'count(*)').group(:uri).having('count(*) > 1').pluck_each(:uri) do |uri| + say("Duplicates found for #{uri}") + begin + ActivityPub::FetchRemotAccountService.new.call(uri) unless options[:dry_run] + rescue => e + say("Error processing #{uri}: #{e}", :red) + end + end + end + desc 'backup USERNAME', 'Request a backup for a user' long_desc <<-LONG_DESC Request a new backup for an account with a given USERNAME. diff --git a/spec/services/resolve_account_service_spec.rb b/spec/services/resolve_account_service_spec.rb index 5bd0ec264..a604e90b5 100644 --- a/spec/services/resolve_account_service_spec.rb +++ b/spec/services/resolve_account_service_spec.rb @@ -60,7 +60,22 @@ RSpec.describe ResolveAccountService, type: :service do context 'with a legitimate webfinger redirection' do before do - webfinger = { subject: 'acct:foo@ap.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo' }] } + webfinger = { subject: 'acct:foo@ap.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo', type: 'application/activity+json' }] } + stub_request(:get, 'https://redirected.example.com/.well-known/webfinger?resource=acct:Foo@redirected.example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + end + + it 'returns new remote account' do + account = subject.call('Foo@redirected.example.com') + + expect(account.activitypub?).to eq true + expect(account.acct).to eq 'foo@ap.example.com' + expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox' + end + end + + context 'with a misconfigured redirection' do + before do + webfinger = { subject: 'acct:Foo@redirected.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo', type: 'application/activity+json' }] } stub_request(:get, 'https://redirected.example.com/.well-known/webfinger?resource=acct:Foo@redirected.example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) end @@ -75,9 +90,9 @@ RSpec.describe ResolveAccountService, type: :service do context 'with too many webfinger redirections' do before do - webfinger = { subject: 'acct:foo@evil.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo' }] } + webfinger = { subject: 'acct:foo@evil.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo', type: 'application/activity+json' }] } stub_request(:get, 'https://redirected.example.com/.well-known/webfinger?resource=acct:Foo@redirected.example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) - webfinger2 = { subject: 'acct:foo@ap.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo' }] } + webfinger2 = { subject: 'acct:foo@ap.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo', type: 'application/activity+json' }] } stub_request(:get, 'https://evil.example.com/.well-known/webfinger?resource=acct:foo@evil.example.com').to_return(body: Oj.dump(webfinger2), headers: { 'Content-Type': 'application/jrd+json' }) end @@ -111,6 +126,41 @@ RSpec.describe ResolveAccountService, type: :service do end end + context 'with an already-known actor changing acct: URI' do + let!(:duplicate) { Fabricate(:account, username: 'foo', domain: 'old.example.com', uri: 'https://ap.example.com/users/foo') } + let!(:status) { Fabricate(:status, account: duplicate, text: 'foo') } + + it 'returns new remote account' do + account = subject.call('foo@ap.example.com') + + expect(account.activitypub?).to eq true + expect(account.domain).to eq 'ap.example.com' + expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox' + expect(account.uri).to eq 'https://ap.example.com/users/foo' + end + + it 'merges accounts' do + account = subject.call('foo@ap.example.com') + + expect(status.reload.account_id).to eq account.id + expect(Account.where(uri: account.uri).count).to eq 1 + end + end + + context 'with an already-known acct: URI changing ActivityPub id' do + let!(:old_account) { Fabricate(:account, username: 'foo', domain: 'ap.example.com', uri: 'https://old.example.com/users/foo', last_webfingered_at: nil) } + let!(:status) { Fabricate(:status, account: old_account, text: 'foo') } + + it 'returns new remote account' do + account = subject.call('foo@ap.example.com') + + expect(account.activitypub?).to eq true + expect(account.domain).to eq 'ap.example.com' + expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox' + expect(account.uri).to eq 'https://ap.example.com/users/foo' + end + end + it 'processes one remote account at a time using locks' do wait_for_start = true fail_occurred = false -- cgit From c6598b17d9212c2d48395da73fa9529025d704cc Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 19 Dec 2020 00:28:38 +0100 Subject: Bump version to 3.3.0rc2 (#15365) --- AUTHORS.md | 81 ++++++++++++++++++++++++++++--------------------- CHANGELOG.md | 42 +++++++++++++++++++------ lib/mastodon/version.rb | 2 +- 3 files changed, 80 insertions(+), 45 deletions(-) (limited to 'lib') diff --git a/AUTHORS.md b/AUTHORS.md index 12d0736bd..a2913bfe9 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -5,38 +5,39 @@ Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon) and provided thanks to the work of the following contributors: * [Gargron](https://github.com/Gargron) -* [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) * [ThibG](https://github.com/ThibG) -* [ykzts](https://github.com/ykzts) +* [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) * [dependabot[bot]](https://github.com/apps/dependabot) +* [ykzts](https://github.com/ykzts) * [akihikodaki](https://github.com/akihikodaki) * [mjankowski](https://github.com/mjankowski) * [unarist](https://github.com/unarist) * [yiskah](https://github.com/yiskah) * [nolanlawson](https://github.com/nolanlawson) * [abcang](https://github.com/abcang) -* [ysksn](https://github.com/ysksn) * [mayaeh](https://github.com/mayaeh) +* [ysksn](https://github.com/ysksn) * [sorin-davidoi](https://github.com/sorin-davidoi) +* [noellabo](https://github.com/noellabo) * [lynlynlynx](https://github.com/lynlynlynx) * [m4sk1n](mailto:me@m4sk.in) * [Marcin Mikołajczak](mailto:me@m4sk.in) * [Kjwon15](https://github.com/Kjwon15) -* [noellabo](https://github.com/noellabo) * [renatolond](https://github.com/renatolond) * [alpaca-tc](https://github.com/alpaca-tc) * [jeroenpraat](https://github.com/jeroenpraat) * [nclm](https://github.com/nclm) * [ineffyble](https://github.com/ineffyble) -* [shleeable](https://github.com/shleeable) * [zunda](https://github.com/zunda) +* [shleeable](https://github.com/shleeable) * [Masoud Abkenar](mailto:ampbox@gmail.com) * [blackle](https://github.com/blackle) * [Quent-in](https://github.com/Quent-in) * [JantsoP](https://github.com/JantsoP) * [nullkal](https://github.com/nullkal) * [yookoala](https://github.com/yookoala) -* [Sasha-Sorokin](https://github.com/Sasha-Sorokin) +* [Brawaru](https://github.com/Brawaru) +* [ariasuni](https://github.com/ariasuni) * [Aditoo17](https://github.com/Aditoo17) * [Quenty31](https://github.com/Quenty31) * [marek-lach](https://github.com/marek-lach) @@ -45,7 +46,6 @@ and provided thanks to the work of the following contributors: * [danhunsaker](https://github.com/danhunsaker) * [eramdam](https://github.com/eramdam) * [takayamaki](https://github.com/takayamaki) -* [ariasuni](https://github.com/ariasuni) * [masarakki](https://github.com/masarakki) * [ticky](https://github.com/ticky) * [ThisIsMissEm](https://github.com/ThisIsMissEm) @@ -53,14 +53,15 @@ and provided thanks to the work of the following contributors: * [hcmiya](https://github.com/hcmiya) * [stephenburgess8](https://github.com/stephenburgess8) * [Wonderfall](mailto:wonderfall@targaryen.house) +* [trwnh](https://github.com/trwnh) * [matteoaquila](https://github.com/matteoaquila) * [yukimochi](https://github.com/yukimochi) * [palindromordnilap](https://github.com/palindromordnilap) * [rkarabut](https://github.com/rkarabut) -* [trwnh](https://github.com/trwnh) * [nightpool](https://github.com/nightpool) * [Artoria2e5](https://github.com/Artoria2e5) * [marrus-sh](https://github.com/marrus-sh) +* [dunn](https://github.com/dunn) * [krainboltgreene](https://github.com/krainboltgreene) * [pfigel](https://github.com/pfigel) * [BoFFire](https://github.com/BoFFire) @@ -84,25 +85,25 @@ and provided thanks to the work of the following contributors: * [ashleyhull-versent](https://github.com/ashleyhull-versent) * [yhirano55](https://github.com/yhirano55) * [rinsuki](https://github.com/rinsuki) -* [dunn](https://github.com/dunn) * [devkral](https://github.com/devkral) * [camponez](https://github.com/camponez) * [hugogameiro](https://github.com/hugogameiro) * [SerCom_KC](mailto:szescxz@gmail.com) * [aschmitz](https://github.com/aschmitz) +* [mfmfuyu](https://github.com/mfmfuyu) +* [kedamaDQ](https://github.com/kedamaDQ) * [fpiesche](https://github.com/fpiesche) * [gandaro](https://github.com/gandaro) * [johnsudaar](https://github.com/johnsudaar) * [trebmuh](https://github.com/trebmuh) * [rmhasan](https://github.com/rmhasan) -* [kedamaDQ](https://github.com/kedamaDQ) * [lindwurm](https://github.com/lindwurm) * [victorhck](mailto:victorhck@geeko.site) * [voidsatisfaction](https://github.com/voidsatisfaction) +* [mkljczk](https://github.com/mkljczk) * [hikari-no-yume](https://github.com/hikari-no-yume) * [seefood](https://github.com/seefood) * [jackjennings](https://github.com/jackjennings) -* [mfmfuyu](https://github.com/mfmfuyu) * [puckipedia](https://github.com/puckipedia) * [spla](mailto:spla@mastodont.cat) * [walf443](https://github.com/walf443) @@ -111,14 +112,15 @@ and provided thanks to the work of the following contributors: * [Ashley](mailto:expenses@airmail.cc) * [xqus](https://github.com/xqus) * [pfm-eyesightjp](https://github.com/pfm-eyesightjp) -* [Samy KACIMI](mailto:samy.kacimi@gmail.com) +* [fakenine](https://github.com/fakenine) * [tsuwatch](https://github.com/tsuwatch) * [victorhck](https://github.com/victorhck) -* [mkljczk](https://github.com/mkljczk) * [manuelviens](https://github.com/manuelviens) +* [tateisu](https://github.com/tateisu) * [fvh-P](https://github.com/fvh-P) * [rtucker](https://github.com/rtucker) * [Anna e só](mailto:contraexemplos@gmail.com) +* [dariusk](https://github.com/dariusk) * [kazu9su](https://github.com/kazu9su) * [Komic](https://github.com/Komic) * [lmorchard](https://github.com/lmorchard) @@ -145,9 +147,9 @@ and provided thanks to the work of the following contributors: * [fhemberger](https://github.com/fhemberger) * [Gomasy](https://github.com/Gomasy) * [greysteil](https://github.com/greysteil) -* [hencatsmith](https://github.com/hencatsmith) +* [hendotcat](https://github.com/hendotcat) * [d6rkaiz](https://github.com/d6rkaiz) -* [Reverite](https://github.com/Reverite) +* [ladyisatis](https://github.com/ladyisatis) * [JohnD28](https://github.com/JohnD28) * [znz](https://github.com/znz) * [saper](https://github.com/saper) @@ -160,14 +162,14 @@ and provided thanks to the work of the following contributors: * [leopku](https://github.com/leopku) * [SansPseudoFix](https://github.com/SansPseudoFix) * [spla](mailto:sp@mastodont.cat) -* [tateisu](https://github.com/tateisu) * [tomfhowe](https://github.com/tomfhowe) * [noraworld](https://github.com/noraworld) * [lfuelling](https://github.com/lfuelling) -* [theboss](https://github.com/theboss) +* [aji-su](https://github.com/aji-su) * [nzws](https://github.com/nzws) * [duxovni](https://github.com/duxovni) * [smorimoto](https://github.com/smorimoto) +* [mashirozx](https://github.com/mashirozx) * [178inaba](https://github.com/178inaba) * [acid-chicken](https://github.com/acid-chicken) * [xgess](https://github.com/xgess) @@ -175,7 +177,6 @@ and provided thanks to the work of the following contributors: * [aablinov](https://github.com/aablinov) * [stalker314314](https://github.com/stalker314314) * [cutls](https://github.com/cutls) -* [dariusk](https://github.com/dariusk) * [huertanix](https://github.com/huertanix) * [eleboucher](https://github.com/eleboucher) * [halkeye](https://github.com/halkeye) @@ -183,7 +184,7 @@ and provided thanks to the work of the following contributors: * [treby](https://github.com/treby) * [jpdevries](https://github.com/jpdevries) * [gdpelican](https://github.com/gdpelican) -* [kmichl](https://github.com/kmichl) +* [Korbinian](mailto:kontakt@korbinian-michl.de) * [Kurtis Rainbolt-Greene](mailto:me@kurtisrainboltgreene.name) * [panarom](https://github.com/panarom) * [Dar13](https://github.com/Dar13) @@ -225,6 +226,7 @@ and provided thanks to the work of the following contributors: * [aaribaud](https://github.com/aaribaud) * [pointlessone](https://github.com/pointlessone) * [Andrew](mailto:andrewlchronister@gmail.com) +* [arielrodrigues](https://github.com/arielrodrigues) * [aurelien-reeves](https://github.com/aurelien-reeves) * [elegaanz](https://github.com/elegaanz) * [estuans](https://github.com/estuans) @@ -238,6 +240,7 @@ and provided thanks to the work of the following contributors: * [muffinista](https://github.com/muffinista) * [cdutson](https://github.com/cdutson) * [farlistener](https://github.com/farlistener) +* [divergentdave](https://github.com/divergentdave) * [DavidLibeau](https://github.com/DavidLibeau) * [dmerejkowsky](https://github.com/dmerejkowsky) * [ddevault](https://github.com/ddevault) @@ -276,7 +279,7 @@ and provided thanks to the work of the following contributors: * [xPaw](https://github.com/xPaw) * [petzah](https://github.com/petzah) * [ignisf](https://github.com/ignisf) -* [raymestalez](https://github.com/raymestalez) +* [lumenwrites](https://github.com/lumenwrites) * [remram44](https://github.com/remram44) * [sts10](https://github.com/sts10) * [SuperSandro2000](https://github.com/SuperSandro2000) @@ -286,8 +289,9 @@ and provided thanks to the work of the following contributors: * [Sir-Boops](https://github.com/Sir-Boops) * [stemid](https://github.com/stemid) * [sumdog](https://github.com/sumdog) +* [OmmyZhang](https://github.com/OmmyZhang) * [ThomasLeister](https://github.com/ThomasLeister) -* [mcat-ee](https://github.com/mcat-ee) +* [Tom McAtee](mailto:a1608768@student.adelaide.edu.au) * [tototoshi](https://github.com/tototoshi) * [TrashMacNugget](https://github.com/TrashMacNugget) * [VirtuBox](https://github.com/VirtuBox) @@ -314,11 +318,13 @@ and provided thanks to the work of the following contributors: * [matsurai25](https://github.com/matsurai25) * [mecab](https://github.com/mecab) * [nicobz25](https://github.com/nicobz25) +* [niwatori24](https://github.com/niwatori24) * [oliverkeeble](https://github.com/oliverkeeble) * [partev](https://github.com/partev) * [pinfort](https://github.com/pinfort) * [rbaumert](https://github.com/rbaumert) * [rhoio](https://github.com/rhoio) +* [santiagorodriguez96](https://github.com/santiagorodriguez96) * [sclaire-1](https://github.com/sclaire-1) * [umonaca](https://github.com/umonaca) * [usagi-f](https://github.com/usagi-f) @@ -327,7 +333,7 @@ and provided thanks to the work of the following contributors: * [wxcafe](https://github.com/wxcafe) * [Grawl](https://github.com/Grawl) * [新都心(Neet Shin)](mailto:nucx@dio-vox.com) -* [clarfon](https://github.com/clarfon) +* [clarfonthey](https://github.com/clarfonthey) * [cygnan](https://github.com/cygnan) * [Awea](https://github.com/Awea) * [eai04191](https://github.com/eai04191) @@ -358,11 +364,11 @@ and provided thanks to the work of the following contributors: * [schas002](https://github.com/schas002) * [contraexemplo](https://github.com/contraexemplo) * [abackstrom](https://github.com/abackstrom) -* [arielrodrigues](https://github.com/arielrodrigues) * [orlea](https://github.com/orlea) * [armandfardeau](https://github.com/armandfardeau) * [raboof](https://github.com/raboof) * [jumbosushi](https://github.com/jumbosushi) +* [acuteaura](https://github.com/acuteaura) * [ayumin](https://github.com/ayumin) * [bzg](https://github.com/bzg) * [BastienDurel](https://github.com/BastienDurel) @@ -389,7 +395,7 @@ and provided thanks to the work of the following contributors: * [colindean](https://github.com/colindean) * [DeeUnderscore](https://github.com/DeeUnderscore) * [dachinat](https://github.com/dachinat) -* [shapeshifter-system](https://github.com/shapeshifter-system) +* [monsterpit-firedemon](https://github.com/monsterpit-firedemon) * [watilde](https://github.com/watilde) * [daprice](https://github.com/daprice) * [da2x](https://github.com/da2x) @@ -400,14 +406,13 @@ and provided thanks to the work of the following contributors: * [singingwolfboy](https://github.com/singingwolfboy) * [caldwell](https://github.com/caldwell) * [davidcelis](https://github.com/davidcelis) -* [divergentdave](https://github.com/divergentdave) * [davefp](https://github.com/davefp) * [yipdw](https://github.com/yipdw) * [debanshuk](https://github.com/debanshuk) * [mascali33](https://github.com/mascali33) * [DerekNonGeneric](https://github.com/DerekNonGeneric) * [dblandin](https://github.com/dblandin) -* [Drew Gates](mailto:aranaur@users.noreply.github.com) +* [Aranaur](https://github.com/Aranaur) * [dtschust](https://github.com/dtschust) * [Dryusdan](https://github.com/Dryusdan) * [d3vgru](https://github.com/d3vgru) @@ -451,22 +456,25 @@ and provided thanks to the work of the following contributors: * [J Yeary](mailto:usbsnowcrash@users.noreply.github.com) * [jack-michaud](https://github.com/jack-michaud) * [Floppy](https://github.com/Floppy) -* [loomchild](https://github.com/loomchild) -* [jglauche](https://github.com/jglauche) -* [jenkr55](https://github.com/jenkr55) -* [hyenagirl64](https://github.com/hyenagirl64) -* [press5](https://github.com/press5) -* [TrollDecker](https://github.com/TrollDecker) -* [jmontane](https://github.com/jmontane) +* [Jarek Lipski](mailto:pub@loomchild.net) +* [Jennifer Glauche](mailto:=^.^=@github19.jglauche.de) +* [Jennifer Kruse](mailto:jenkr55@gmail.com) +* [Jeremy Rose](mailto:nornagon@nornagon.net) +* [Jessica](mailto:46502909+hyenagirl64@users.noreply.github.com) +* [Jessica K. Litwin](mailto:jessica@litw.in) +* [Jo Decker](mailto:trolldecker@users.noreply.github.com) +* [Joan Montané](mailto:jmontane@users.noreply.github.com) * [Jonathan Klee](mailto:klee.jonathan@gmail.com) * [Jordan Guerder](mailto:jguerder@fr.pulseheberg.net) * [Joseph Mingrone](mailto:jehops@users.noreply.github.com) +* [Josh Leeb-du Toit](mailto:mail@joshleeb.com) * [Joshua Wood](mailto:josh@joshuawood.net) * [Julien](mailto:tiwy57@users.noreply.github.com) * [Julien Deswaef](mailto:juego@requiem4tv.com) * [June Sallou](mailto:jnsll@users.noreply.github.com) * [Jérémy Benoist](mailto:j0k3r@users.noreply.github.com) * [KEINOS](mailto:github@keinos.com) +* [Kairui Song | 宋恺睿](mailto:ryncsn@gmail.com) * [Keiji Matsuzaki](mailto:futoase@gmail.com) * [Kevin Liu](mailto:kevin@potatofrom.space) * [Kit Redgrave](mailto:qwertyitis@gmail.com) @@ -482,7 +490,6 @@ and provided thanks to the work of the following contributors: * [Lukas Burk](mailto:jemus42@users.noreply.github.com) * [Manato Kameya](mailto:grabacr07+github@gmail.com) * [Mantas](mailto:mistermantas@users.noreply.github.com) -* [Marcin Mikołajczak](mailto:me@mkljczk.pl) * [Mareena Kunjachan](mailto:mareenakunjachan@gmail.com) * [Marek Lach](mailto:marek.brohatwack.lach@gmail.com) * [Markus R](mailto:wirehack7@users.noreply.github.com) @@ -529,10 +536,12 @@ and provided thanks to the work of the following contributors: * [Norayr Chilingarian](mailto:norayr@arnet.am) * [Noëlle Anthony](mailto:noelle.d.anthony@gmail.com) * [N氏](mailto:uenok.htc@gmail.com) +* [OSAMU SATO](mailto:satosamu@gmail.com) * [Olivier Nicole](mailto:olivierthnicole@gmail.com) * [Oskari Noppa](mailto:noppa@users.noreply.github.com) * [Otakan](mailto:otakan951@gmail.com) * [Padraig Fahy](mailto:tech@padraigfahy.com) +* [Patrice Ferlet](mailto:metal3d@gmail.com) * [PatrickRWells](mailto:32802366+patrickrwells@users.noreply.github.com) * [Paul](mailto:naydex.mc+github@gmail.com) * [Pete Keen](mailto:pete@petekeen.net) @@ -574,7 +583,6 @@ and provided thanks to the work of the following contributors: * [TakesxiSximada](mailto:takesxi.sximada@gmail.com) * [Tao Bror Bojlén](mailto:brortao@users.noreply.github.com) * [Taras Gogol](mailto:taras2358@gmail.com) -* [Tdxdxoz](mailto:tdxdxoz@gmail.com) * [TheInventrix](mailto:theinventrix@users.noreply.github.com) * [TheMainOne](mailto:50847364+theevilskeleton@users.noreply.github.com) * [Thomas Alberola](mailto:thomas@needacoffee.fr) @@ -594,6 +602,7 @@ and provided thanks to the work of the following contributors: * [Wesley Ellis](mailto:tahnok@gmail.com) * [Wiktor](mailto:wiktor@metacode.biz) * [Wonderfall](mailto:wonderfall@schrodinger.io) +* [Y.Yamashiro](mailto:shukukei@mojizuri.jp) * [YDrogen](mailto:ydrogen45@gmail.com) * [YMHuang](mailto:ymhuang@fmbase.tw) * [YOSHIOKA Eiichiro](mailto:yoshioka.eiichiro@gmail.com) @@ -638,6 +647,7 @@ and provided thanks to the work of the following contributors: * [jumoru](mailto:jumoru@mailbox.org) * [kaiyou](mailto:pierre@jaury.eu) * [karlyeurl](mailto:karl.yeurl@gmail.com) +* [kawaguchi](mailto:jiikko@users.noreply.github.com) * [kedama](mailto:32974885+kedamadq@users.noreply.github.com) * [kuro5hin](mailto:rusty@kuro5hin.org) * [leo60228](mailto:leo@60228.dev) @@ -655,6 +665,7 @@ and provided thanks to the work of the following contributors: * [notozeki](mailto:notozeki@users.noreply.github.com) * [ntl-purism](mailto:57806346+ntl-purism@users.noreply.github.com) * [nzws](mailto:git-yuzu@svk.jp) +* [proxy](mailto:51172302+3n-k1@users.noreply.github.com) * [rch850](mailto:rich850@gmail.com) * [roikale](mailto:roikale@users.noreply.github.com) * [rysiekpl](mailto:rysiek@hackerspace.pl) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe2c1b8b3..64bf6655c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ All notable changes to this project will be documented in this file. - Add border around 🐞 emoji in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14712)) - Add home link to the getting started column when home isn't mounted ([ThibG](https://github.com/tootsuite/mastodon/pull/14707)) - Add option to disable swiping motions across the web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13885)) -- **Add pop-out player for audio/video in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/14870), [Gargron](https://github.com/tootsuite/mastodon/pull/15157), [Gargron](https://github.com/tootsuite/mastodon/pull/14915)) +- **Add pop-out player for audio/video in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/14870), [Gargron](https://github.com/tootsuite/mastodon/pull/15157), [Gargron](https://github.com/tootsuite/mastodon/pull/14915), [noellabo](https://github.com/tootsuite/mastodon/pull/15309)) - Continue watching/listening when you scroll away - Action bar to interact with/open toot from the pop-out player - Add unread notification markers in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14818), [ThibG](https://github.com/tootsuite/mastodon/pull/14960), [ThibG](https://github.com/tootsuite/mastodon/pull/14954), [noellabo](https://github.com/tootsuite/mastodon/pull/14897), [noellabo](https://github.com/tootsuite/mastodon/pull/14907)) @@ -27,14 +27,16 @@ All notable changes to this project will be documented in this file. - If you have a proxy cache in front of object storage, deleted files will persist until the cache expires - If enabled, cache buster will make a special request to the proxy to signal a cache reset - Add duration option to the mute function ([aquarla](https://github.com/tootsuite/mastodon/pull/13831)) -- Add replies policy option to the list function ([ThibG](https://github.com/tootsuite/mastodon/pull/9205)) +- Add replies policy option to the list function ([ThibG](https://github.com/tootsuite/mastodon/pull/9205), [trwnh](https://github.com/tootsuite/mastodon/pull/15304)) - Add `og:published_time` OpenGraph tags on toots ([nornagon](https://github.com/tootsuite/mastodon/pull/14865)) - **Add option to be notified when a followed user posts** ([Gargron](https://github.com/tootsuite/mastodon/pull/13546), [ThibG](https://github.com/tootsuite/mastodon/pull/14896), [Gargron](https://github.com/tootsuite/mastodon/pull/14822)) - If you don't want to miss a toot, click the bell button! - Add client-side validation in password change forms ([ThibG](https://github.com/tootsuite/mastodon/pull/14564)) - Add client-side validation in the registration form ([ThibG](https://github.com/tootsuite/mastodon/pull/14560), [ThibG](https://github.com/tootsuite/mastodon/pull/14599)) - Add support for Gemini URLs ([joshleeb](https://github.com/tootsuite/mastodon/pull/15013)) +- Add app shortcuts to web app manifest ([mkljczk](https://github.com/tootsuite/mastodon/pull/15234)) - Add WebAuthn as an alternative 2FA method ([santiagorodriguez96](https://github.com/tootsuite/mastodon/pull/14466), [jiikko](https://github.com/tootsuite/mastodon/pull/14806)) +- Add honeypot fields and minimum fill-out time for sign-up form ([ThibG](https://github.com/tootsuite/mastodon/pull/15276)) - Add icon for mutual relationships in relationship manager ([noellabo](https://github.com/tootsuite/mastodon/pull/15149)) - Add follow selected followers button in relationship manager ([noellabo](https://github.com/tootsuite/mastodon/pull/15148)) - **Add subresource integrity for JS and CSS assets** ([Gargron](https://github.com/tootsuite/mastodon/pull/15096)) @@ -42,7 +44,7 @@ All notable changes to this project will be documented in this file. - Subresource integrity compares server-generated asset digests with what's actually served from the CDN and prevents such attacks - Add `ku`, `sa`, `sc`, `zgh` to available locales ([ykzts](https://github.com/tootsuite/mastodon/pull/15138)) - Add ability to force an account to mark media as sensitive ([noellabo](https://github.com/tootsuite/mastodon/pull/14361)) -- **Add ability to block access or limit sign-ups from chosen IPs** ([Gargron](https://github.com/tootsuite/mastodon/pull/14963)) +- **Add ability to block access or limit sign-ups from chosen IPs** ([Gargron](https://github.com/tootsuite/mastodon/pull/14963), [ThibG](https://github.com/tootsuite/mastodon/pull/15263)) - Add rules for IPs or CIDR ranges that automatically expire after a configurable amount of time - Choose the severity of the rule, either blocking all access or merely limiting sign-ups - **Add support for reversible suspensions through ActivityPub** ([Gargron](https://github.com/tootsuite/mastodon/pull/14989)) @@ -50,14 +52,18 @@ All notable changes to this project will be documented in this file. - During suspension, the account can only delete its own content - A reversal of the suspension can be signalled the same way - A local suspension always overrides a remote one +- Add indication to admin UI of whether a report has been forwarded ([ThibG](https://github.com/tootsuite/mastodon/pull/13237)) +- Add display of reasons for joining of an account in admin UI ([mashirozx](https://github.com/tootsuite/mastodon/pull/15265)) +- Add option to obfuscate domain name in public list of domain blocks ([Gargron](https://github.com/tootsuite/mastodon/pull/15355)) +- Add option to make reasons for joining required on sign-up ([ThibG](https://github.com/tootsuite/mastodon/pull/15326), [ThibG](https://github.com/tootsuite/mastodon/pull/15358)) - Add ActivityPub follower synchronization mechanism ([ThibG](https://github.com/tootsuite/mastodon/pull/14510), [ThibG](https://github.com/tootsuite/mastodon/pull/15026)) - Add outbox attribute to instance actor ([ThibG](https://github.com/tootsuite/mastodon/pull/14721)) -- Add featured hashtags as an ActivityPub collection ([Gargron](https://github.com/tootsuite/mastodon/pull/11595)) +- Add featured hashtags as an ActivityPub collection ([Gargron](https://github.com/tootsuite/mastodon/pull/11595), [noellabo](https://github.com/tootsuite/mastodon/pull/15277)) - Add support for dereferencing objects through bearcaps ([Gargron](https://github.com/tootsuite/mastodon/pull/14683), [noellabo](https://github.com/tootsuite/mastodon/pull/14981)) - Add `S3_READ_TIMEOUT` environment variable ([tateisu](https://github.com/tootsuite/mastodon/pull/14952)) - Add `ALLOWED_PRIVATE_ADDRESSES` environment variable ([ThibG](https://github.com/tootsuite/mastodon/pull/14722)) - Add `--fix-permissions` option to `tootctl media remove-orphans` ([Gargron](https://github.com/tootsuite/mastodon/pull/14383), [uist1idrju3i](https://github.com/tootsuite/mastodon/pull/14715)) -- Add `tootctl accounts merge` ([Gargron](https://github.com/tootsuite/mastodon/pull/15201)) +- Add `tootctl accounts merge` ([Gargron](https://github.com/tootsuite/mastodon/pull/15201), [ThibG](https://github.com/tootsuite/mastodon/pull/15264), [ThibG](https://github.com/tootsuite/mastodon/pull/15256)) - Has someone changed their domain or subdomain thereby creating two accounts where there should be one? - This command will fix it on your end - Add `tootctl maintenance fix-duplicates` ([ThibG](https://github.com/tootsuite/mastodon/pull/14860), [Gargron](https://github.com/tootsuite/mastodon/pull/15223)) @@ -68,12 +74,13 @@ All notable changes to this project will be documented in this file. - More connections means more resource consumption on both ends, not to mention the (ever so slight) delay when establishing a new connection - Now, with just a single WebSocket connection you can subscribe and unsubscribe to and from multiple streams - Add support for limiting results by both `min_id` and `max_id` at the same time in REST API ([tateisu](https://github.com/tootsuite/mastodon/pull/14776)) -- Add `GET /api/v1/accounts/:id/featured_tags` to REST API ([noellabo](https://github.com/tootsuite/mastodon/pull/11817)) +- Add `GET /api/v1/accounts/:id/featured_tags` to REST API ([noellabo](https://github.com/tootsuite/mastodon/pull/11817), [noellabo](https://github.com/tootsuite/mastodon/pull/15270)) +- Add stoplight for object storage failures, return HTTP 503 in REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/13043)) - Add optional `tootctl remove media` cronjob in Helm chart ([dunn](https://github.com/tootsuite/mastodon/pull/14396)) ### Changed -- **Change media modals look in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/15217), [Gargron](https://github.com/tootsuite/mastodon/pull/15221)) +- **Change media modals look in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/15217), [Gargron](https://github.com/tootsuite/mastodon/pull/15221), [Gargron](https://github.com/tootsuite/mastodon/pull/15284), [Gargron](https://github.com/tootsuite/mastodon/pull/15283), [Kjwon15](https://github.com/tootsuite/mastodon/pull/15308), [noellabo](https://github.com/tootsuite/mastodon/pull/15305)) - Background of the overlay matches the color of the image - Action bar to interact with or open the toot from the modal - Change order of announcements in admin UI to be newest-first ([ThibG](https://github.com/tootsuite/mastodon/pull/15091)) @@ -83,7 +90,7 @@ All notable changes to this project will be documented in this file. - Immediate deletion of data is still available as an explicit option - Suspended accounts can request an archive of their data through the UI - Change REST API to return empty data for suspended accounts (14765) -- Change web UI to show empty profile for suspended accounts ([Gargron](https://github.com/tootsuite/mastodon/pull/14766)) +- Change web UI to show empty profile for suspended accounts ([Gargron](https://github.com/tootsuite/mastodon/pull/14766), [Gargron](https://github.com/tootsuite/mastodon/pull/15345)) - Change featured hashtag suggestions to be recently used instead of most used ([abcang](https://github.com/tootsuite/mastodon/pull/14760)) - Change direct toots to appear in the home feed again ([Gargron](https://github.com/tootsuite/mastodon/pull/14711), [ThibG](https://github.com/tootsuite/mastodon/pull/15182), [noellabo](https://github.com/tootsuite/mastodon/pull/14727)) - Return to treating all toots the same instead of trying to retrofit direct visibility into an instant messaging model @@ -96,7 +103,14 @@ All notable changes to this project will be documented in this file. - Change string "Boost to original audience" to "Boost with original visibility" in web UI ([3n-k1](https://github.com/tootsuite/mastodon/pull/14598)) - Change string "Show more" to "Show newer" and "Show older" on public pages ([ariasuni](https://github.com/tootsuite/mastodon/pull/15052)) - Change order of announcements to be reverse chronological in web UI ([dariusk](https://github.com/tootsuite/mastodon/pull/15065), [dariusk](https://github.com/tootsuite/mastodon/pull/15070)) +- Change RTL detection to rely on unicode-bidi paragraph by paragraph in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14573)) - Change visibility icon next to timestamp to be clickable in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/15053), [mayaeh](https://github.com/tootsuite/mastodon/pull/15055)) +- Change public thread view to hide "Show thread" link ([ThibG](https://github.com/tootsuite/mastodon/pull/15266)) +- Change number format on about page from full to shortened ([Gargron](https://github.com/tootsuite/mastodon/pull/15327)) +- Change how scheduled tasks run in multi-process environments ([noellabo](https://github.com/tootsuite/mastodon/pull/15314)) + - New dedicated queue `scheduler` + - Runs by default when Sidekiq is executed with no options + - Has to be added manually in a multi-process environment ### Removed @@ -107,6 +121,8 @@ All notable changes to this project will be documented in this file. ### Fixed +- Fix layout on about page when contact account has a long username ([ThibG](https://github.com/tootsuite/mastodon/pull/15357)) +- Fix follow limit preventing re-following of a moved account ([Gargron](https://github.com/tootsuite/mastodon/pull/14207)) - **Fix deletes not reaching every server that interacted with toot** ([Gargron](https://github.com/tootsuite/mastodon/pull/15200)) - Previously, delete of a toot would be primarily sent to the followers of its author, people mentioned in the toot, and people who reblogged the toot - Now, additionally, it is ensured that it is sent to people who replied to it, favourited it, and to the person it replies to even if that person is not mentioned @@ -123,7 +139,7 @@ All notable changes to this project will be documented in this file. - Fix poll ending notifications being created for each vote ([ThibG](https://github.com/tootsuite/mastodon/pull/15071)) - Fix multiple boosts of a same toot erroneously appearing in TL ([ThibG](https://github.com/tootsuite/mastodon/pull/14759)) - Fix asset builds not picking up `CDN_HOST` change ([ThibG](https://github.com/tootsuite/mastodon/pull/14381)) -- Fix desktop notifications permission prompt in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14985), [Gargron](https://github.com/tootsuite/mastodon/pull/15141), [ThibG](https://github.com/tootsuite/mastodon/pull/13543)) +- Fix desktop notifications permission prompt in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14985), [Gargron](https://github.com/tootsuite/mastodon/pull/15141), [ThibG](https://github.com/tootsuite/mastodon/pull/13543), [ThibG](https://github.com/tootsuite/mastodon/pull/15176)) - Some time ago, browsers added a requirement that desktop notification prompts could only be displayed in response to a user-generated event (such as a click) - This means that for some time, users who haven't already given the permission before were not getting a prompt and as such were not receiving desktop notifications - Fix "Mark media as sensitive" string not supporting pluralizations in other languages in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/15051)) @@ -139,11 +155,18 @@ All notable changes to this project will be documented in this file. - Fix disabled boost icon being replaced by private boost icon on hover in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14456)) - Fix hashtag detection in compose form being different to server-side in web UI ([kedamaDQ](https://github.com/tootsuite/mastodon/pull/14484), [ThibG](https://github.com/tootsuite/mastodon/pull/14513)) - Fix home last read marker mishandling gaps in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14809)) +- Fix unnecessary re-rendering of various components when typing in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/15286)) +- Fix notifications being unnecessarily re-rendered in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15312)) +- Fix column swiping animation logic in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15301)) - Fix inefficiency when fetching hashtag timeline ([noellabo](https://github.com/tootsuite/mastodon/pull/14861), [akihikodaki](https://github.com/tootsuite/mastodon/pull/14662)) - Fix inefficiency when fetching bookmarks ([akihikodaki](https://github.com/tootsuite/mastodon/pull/14674)) - Fix inefficiency when fetching favourites ([akihikodaki](https://github.com/tootsuite/mastodon/pull/14673)) - Fix inefficiency when fetching media-only account timeline ([akihikodaki](https://github.com/tootsuite/mastodon/pull/14675)) - Fix redundant query when processing batch actions on custom emojis ([niwatori24](https://github.com/tootsuite/mastodon/pull/14534)) +- Fix slow distinct queries where grouped queries are faster ([Gargron](https://github.com/tootsuite/mastodon/pull/15287)) +- Fix performance on instances list in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/15282)) +- Fix server actor appearing in list of accounts in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14567)) +- Fix "bootstrap timeline accounts" toggle in site settings in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15325)) - Fix PostgreSQL secret name for cronjob in Helm chart ([metal3d](https://github.com/tootsuite/mastodon/pull/15072)) - Fix Procfile not being compatible with herokuish ([acuteaura](https://github.com/tootsuite/mastodon/pull/12685)) - Fix installation of tini being split into multiple steps in Dockerfile ([ryncsn](https://github.com/tootsuite/mastodon/pull/14686)) @@ -152,6 +175,7 @@ All notable changes to this project will be documented in this file. - Fix streaming API allowing connections to persist after access token invalidation ([Gargron](https://github.com/tootsuite/mastodon/pull/15111)) - Fix 2FA/sign-in token sessions being valid after password change ([Gargron](https://github.com/tootsuite/mastodon/pull/14802)) +- Fix resolving accounts sometimes creating duplicate records for a given ActivityPub identifier ([ThibG](https://github.com/tootsuite/mastodon/pull/15364)) ## [3.2.1] - 2020-10-19 ### Added diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 46aed7d8d..7282ea7b5 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -17,7 +17,7 @@ module Mastodon end def flags - 'rc1' + 'rc2' end def suffix -- cgit From c4e860277dbc391a0c6ec0311f66a8484cca862b Mon Sep 17 00:00:00 2001 From: ThibG Date: Sat, 19 Dec 2020 13:34:16 +0100 Subject: Fix "tootctl accounts fix-duplicates" (#15373) - `pluck_each` cannot be used this way with `group` - typo Co-authored-by: Claire --- lib/mastodon/accounts_cli.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/mastodon/accounts_cli.rb b/lib/mastodon/accounts_cli.rb index 474643869..5275f04cf 100644 --- a/lib/mastodon/accounts_cli.rb +++ b/lib/mastodon/accounts_cli.rb @@ -245,10 +245,10 @@ module Mastodon domain configuration. LONG_DESC def fix_duplicates - Account.remote.select(:uri, 'count(*)').group(:uri).having('count(*) > 1').pluck_each(:uri) do |uri| + Account.remote.select(:uri, 'count(*)').group(:uri).having('count(*) > 1').pluck(:uri).each do |uri| say("Duplicates found for #{uri}") begin - ActivityPub::FetchRemotAccountService.new.call(uri) unless options[:dry_run] + ActivityPub::FetchRemoteAccountService.new.call(uri) unless options[:dry_run] rescue => e say("Error processing #{uri}: #{e}", :red) end -- cgit From 9915d11c0d7a15b6775af8e78fcc4d836368f88d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 22 Dec 2020 17:13:55 +0100 Subject: Fix unnecessary queries when batch-removing statuses, 100x faster (#15387) --- app/models/favourite.rb | 2 +- app/models/status.rb | 12 +- app/services/batched_remove_status_service.rb | 91 ++++++++------- app/services/delete_account_service.rb | 126 ++++++++++++++++----- config/initializers/chewy.rb | 5 + lib/chewy/strategy/custom_sidekiq.rb | 25 +--- .../services/batched_remove_status_service_spec.rb | 5 + 7 files changed, 167 insertions(+), 99 deletions(-) (limited to 'lib') diff --git a/app/models/favourite.rb b/app/models/favourite.rb index bf0ec4449..35028b7dd 100644 --- a/app/models/favourite.rb +++ b/app/models/favourite.rb @@ -36,7 +36,7 @@ class Favourite < ApplicationRecord end def decrement_cache_counters - return if association(:status).loaded? && (status.marked_for_destruction? || status.marked_for_mass_destruction?) + return if association(:status).loaded? && status.marked_for_destruction? status&.decrement_count!(:favourites_count) end end diff --git a/app/models/status.rb b/app/models/status.rb index 96d90e1c2..b426f9d5b 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -228,14 +228,6 @@ class Status < ApplicationRecord @emojis = CustomEmoji.from_text(fields.join(' '), account.domain) end - def mark_for_mass_destruction! - @marked_for_mass_destruction = true - end - - def marked_for_mass_destruction? - @marked_for_mass_destruction - end - def replies_count status_stat&.replies_count || 0 end @@ -430,7 +422,7 @@ class Status < ApplicationRecord end def decrement_counter_caches - return if direct_visibility? || marked_for_mass_destruction? + return if direct_visibility? account&.decrement_count!(:statuses_count) reblog&.decrement_count!(:reblogs_count) if reblog? @@ -440,7 +432,7 @@ class Status < ApplicationRecord def unlink_from_conversations return unless direct_visibility? - mentioned_accounts = mentions.includes(:account).map(&:account) + mentioned_accounts = (association(:mentions).loaded? ? mentions : mentions.includes(:account)).map(&:account) inbox_owners = mentioned_accounts.select(&:local?) + (account.local? ? [account] : []) inbox_owners.each do |inbox_owner| diff --git a/app/services/batched_remove_status_service.rb b/app/services/batched_remove_status_service.rb index 28e5468b3..63ab89f2d 100644 --- a/app/services/batched_remove_status_service.rb +++ b/app/services/batched_remove_status_service.rb @@ -3,29 +3,45 @@ class BatchedRemoveStatusService < BaseService include Redisable - # Delete given statuses and reblogs of them - # Remove statuses from home feeds - # Push delete events to streaming API for home feeds and public feeds - # @param [Enumerable] statuses A preferably batched array of statuses + # Delete multiple statuses and reblogs of them as efficiently as possible + # @param [Enumerable] statuses An array of statuses # @param [Hash] options - # @option [Boolean] :skip_side_effects + # @option [Boolean] :skip_side_effects Do not modify feeds and send updates to streaming API def call(statuses, **options) - statuses = Status.where(id: statuses.map(&:id)).includes(:account).flat_map { |status| [status] + status.reblogs.includes(:account).to_a } + ActiveRecord::Associations::Preloader.new.preload(statuses, options[:skip_side_effects] ? :reblogs : [:account, reblogs: :account]) - @mentions = statuses.each_with_object({}) { |s, h| h[s.id] = s.active_mentions.includes(:account).to_a } - @tags = statuses.each_with_object({}) { |s, h| h[s.id] = s.tags.pluck(:name) } + statuses_and_reblogs = statuses.flat_map { |status| [status] + status.reblogs } - @json_payloads = statuses.each_with_object({}) { |s, h| h[s.id] = Oj.dump(event: :delete, payload: s.id.to_s) } + # The conversations for direct visibility statuses also need + # to be manually updated. This part is not efficient but we + # rely on direct visibility statuses being relatively rare. + statuses_with_account_conversations = statuses.select(&:direct_visibility?) - statuses.each do |status| - status.mark_for_mass_destruction! - status.destroy + ActiveRecord::Associations::Preloader.new.preload(statuses_with_account_conversations, [mentions: :account]) + + statuses_with_account_conversations.each do |status| + status.send(:unlink_from_conversations) end + # We do not batch all deletes into one to avoid having a long-running + # transaction lock the database, but we use the delete method instead + # of destroy to avoid all callbacks. We rely on foreign keys to + # cascade the delete faster without loading the associations. + statuses_and_reblogs.each(&:delete) + + # Since we skipped all callbacks, we also need to manually + # deindex the statuses + Chewy.strategy.current.update(StatusesIndex, statuses_and_reblogs) + return if options[:skip_side_effects] + ActiveRecord::Associations::Preloader.new.preload(statuses_and_reblogs, :tags) + + @tags = statuses_and_reblogs.each_with_object({}) { |s, h| h[s.id] = s.tags.map { |tag| tag.name.mb_chars.downcase } } + @json_payloads = statuses_and_reblogs.each_with_object({}) { |s, h| h[s.id] = Oj.dump(event: :delete, payload: s.id.to_s) } + # Batch by source account - statuses.group_by(&:account_id).each_value do |account_statuses| + statuses_and_reblogs.group_by(&:account_id).each_value do |account_statuses| account = account_statuses.first.account next unless account @@ -35,27 +51,31 @@ class BatchedRemoveStatusService < BaseService end # Cannot be batched - statuses.each do |status| - unpush_from_public_timelines(status) + redis.pipelined do + statuses_and_reblogs.each do |status| + unpush_from_public_timelines(status) + end end end private def unpush_from_home_timelines(account, statuses) - recipients = account.followers_for_local_distribution.to_a - - recipients << account if account.local? - - recipients.each do |follower| + account.followers_for_local_distribution.includes(:user).find_each do |follower| statuses.each do |status| FeedManager.instance.unpush_from_home(follower, status) end end + + return unless account.local? + + statuses.each do |status| + FeedManager.instance.unpush_from_home(account, status) + end end def unpush_from_list_timelines(account, statuses) - account.lists_for_local_distribution.select(:id, :account_id).each do |list| + account.lists_for_local_distribution.select(:id, :account_id).includes(account: :user).find_each do |list| statuses.each do |status| FeedManager.instance.unpush_from_list(list, status) end @@ -67,26 +87,17 @@ class BatchedRemoveStatusService < BaseService payload = @json_payloads[status.id] - redis.pipelined do - redis.publish('timeline:public', payload) - if status.local? - redis.publish('timeline:public:local', payload) - else - redis.publish('timeline:public:remote', payload) - end - if status.media_attachments.any? - redis.publish('timeline:public:media', payload) - if status.local? - redis.publish('timeline:public:local:media', payload) - else - redis.publish('timeline:public:remote:media', payload) - end - end + redis.publish('timeline:public', payload) + redis.publish(status.local? ? 'timeline:public:local' : 'timeline:public:remote', payload) - @tags[status.id].each do |hashtag| - redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}", payload) - redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}:local", payload) if status.local? - end + if status.media_attachments.any? + redis.publish('timeline:public:media', payload) + redis.publish(status.local? ? 'timeline:public:local:media' : 'timeline:public:remote:media', payload) + end + + @tags[status.id].each do |hashtag| + redis.publish("timeline:hashtag:#{hashtag}", payload) + redis.publish("timeline:hashtag:#{hashtag}:local", payload) if status.local? end end end diff --git a/app/services/delete_account_service.rb b/app/services/delete_account_service.rb index fa834e775..5123a4697 100644 --- a/app/services/delete_account_service.rb +++ b/app/services/delete_account_service.rb @@ -6,15 +6,21 @@ class DeleteAccountService < BaseService ASSOCIATIONS_ON_SUSPEND = %w( account_pins active_relationships + aliases block_relationships blocked_by_relationships + bookmarks conversation_mutes conversations custom_filters + devices domain_blocks favourites + featured_tags follow_requests + identity_proofs list_accounts + migrations mute_relationships muted_by_relationships notifications @@ -25,6 +31,29 @@ class DeleteAccountService < BaseService status_pins ).freeze + # The following associations have no important side-effects + # in callbacks and all of their own associations are secured + # by foreign keys, making them safe to delete without loading + # into memory + ASSOCIATIONS_WITHOUT_SIDE_EFFECTS = %w( + account_pins + aliases + conversation_mutes + conversations + custom_filters + devices + domain_blocks + featured_tags + follow_requests + identity_proofs + migrations + mute_relationships + muted_by_relationships + notifications + scheduled_statuses + status_pins + ) + ASSOCIATIONS_ON_DESTROY = %w( reports targeted_moderation_notes @@ -55,19 +84,25 @@ class DeleteAccountService < BaseService @options[:skip_activitypub] = true if @options[:skip_side_effects] - reject_follows! - undo_follows! - purge_user! - purge_profile! + distribute_activities! purge_content! fulfill_deletion_request! end private - def reject_follows! - return if @account.local? || !@account.activitypub? || @options[:skip_activitypub] + def distribute_activities! + return if skip_activitypub? + if @account.local? + delete_actor! + elsif @account.activitypub? + reject_follows! + undo_follows! + end + end + + def reject_follows! # When deleting a remote account, the account obviously doesn't # actually become deleted on its origin server, i.e. unlike a # locally deleted account it continues to have access to its home @@ -81,8 +116,6 @@ class DeleteAccountService < BaseService end def undo_follows! - return if @account.local? || !@account.activitypub? || @options[:skip_activitypub] - # When deleting a remote account, the account obviously doesn't # actually become deleted on its origin server, but following relationships # are severed on our end. Therefore, make the remote server aware that the @@ -97,7 +130,7 @@ class DeleteAccountService < BaseService def purge_user! return if !@account.local? || @account.user.nil? - if @options[:reserve_email] + if keep_user_record? @account.user.disable! @account.user.invites.where(uses: 0).destroy_all else @@ -106,34 +139,52 @@ class DeleteAccountService < BaseService end def purge_content! - distribute_delete_actor! if @account.local? && !@options[:skip_side_effects] + purge_user! + purge_profile! + purge_statuses! + purge_media_attachments! + purge_polls! + purge_generated_notifications! + purge_other_associations! + @account.destroy unless keep_account_record? + end + + def purge_statuses! @account.statuses.reorder(nil).find_in_batches do |statuses| - statuses.reject! { |status| reported_status_ids.include?(status.id) } if @options[:reserve_username] - BatchedRemoveStatusService.new.call(statuses, skip_side_effects: @options[:skip_side_effects]) + statuses.reject! { |status| reported_status_ids.include?(status.id) } if keep_account_record? + + BatchedRemoveStatusService.new.call(statuses, skip_side_effects: skip_side_effects?) end + end + def purge_media_attachments! @account.media_attachments.reorder(nil).find_each do |media_attachment| - next if @options[:reserve_username] && reported_status_ids.include?(media_attachment.status_id) + next if keep_account_record? && reported_status_ids.include?(media_attachment.status_id) media_attachment.destroy end + end + def purge_polls! @account.polls.reorder(nil).find_each do |poll| - next if @options[:reserve_username] && reported_status_ids.include?(poll.status_id) + next if keep_account_record? && reported_status_ids.include?(poll.status_id) - # We can safely delete the poll rather than destroy it, as any non-reported - # status should have been deleted already, as long as we take care of - # notifications. - Notification.where(poll: poll).delete_all poll.delete end + end + def purge_generated_notifications! + # By deleting polls and statuses without callbacks, we've left behind + # polymorphically associated notifications generated by this account + + Notification.where(from_account: @account).in_batches.delete_all + end + + def purge_other_associations! associations_for_destruction.each do |association_name| - destroy_all(@account.public_send(association_name)) + purge_association(association_name) end - - @account.destroy unless @options[:reserve_username] end def purge_profile! @@ -141,7 +192,7 @@ class DeleteAccountService < BaseService # there is no point wasting time updating # its values first - return unless @options[:reserve_username] + return unless keep_account_record? @account.silenced_at = nil @account.suspended_at = @options[:suspended_at] || Time.now.utc @@ -156,6 +207,7 @@ class DeleteAccountService < BaseService @account.followers_count = 0 @account.following_count = 0 @account.moved_to_account = nil + @account.also_known_as = [] @account.trust_level = :untrusted @account.avatar.destroy @account.header.destroy @@ -166,11 +218,17 @@ class DeleteAccountService < BaseService @account.deletion_request&.destroy end - def destroy_all(association) - association.in_batches.destroy_all + def purge_association(association_name) + association = @account.public_send(association_name) + + if ASSOCIATIONS_WITHOUT_SIDE_EFFECTS.include?(association_name) + association.in_batches.delete_all + else + association.in_batches.destroy_all + end end - def distribute_delete_actor! + def delete_actor! ActivityPub::DeliveryWorker.push_bulk(delivery_inboxes) do |inbox_url| [delete_actor_json, @account.id, inbox_url] end @@ -197,10 +255,26 @@ class DeleteAccountService < BaseService end def associations_for_destruction - if @options[:reserve_username] + if keep_account_record? ASSOCIATIONS_ON_SUSPEND else ASSOCIATIONS_ON_SUSPEND + ASSOCIATIONS_ON_DESTROY end end + + def keep_user_record? + @options[:reserve_email] + end + + def keep_account_record? + @options[:reserve_username] + end + + def skip_side_effects? + @options[:skip_side_effects] + end + + def skip_activitypub? + @options[:skip_activitypub] + end end diff --git a/config/initializers/chewy.rb b/config/initializers/chewy.rb index 8f54abf77..9fc9b2f1a 100644 --- a/config/initializers/chewy.rb +++ b/config/initializers/chewy.rb @@ -12,6 +12,10 @@ Chewy.settings = { sidekiq: { queue: 'pull' }, } +# We use our own async strategy even outside the request-response +# cycle, which takes care of checking if ElasticSearch is enabled +# or not. However, mind that for the Rails console, the :urgent +# strategy is set automatically with no way to override it. Chewy.root_strategy = :custom_sidekiq Chewy.request_strategy = :custom_sidekiq Chewy.use_after_commit_callbacks = false @@ -37,6 +41,7 @@ Elasticsearch::Transport::Client.prepend Module.new { super arguments end } + Elasticsearch::API::Indices::IndicesClient.prepend Module.new { def create(arguments = {}) arguments[:include_type_name] = true diff --git a/lib/chewy/strategy/custom_sidekiq.rb b/lib/chewy/strategy/custom_sidekiq.rb index 3e54326ba..794ae4ed4 100644 --- a/lib/chewy/strategy/custom_sidekiq.rb +++ b/lib/chewy/strategy/custom_sidekiq.rb @@ -2,29 +2,10 @@ module Chewy class Strategy - class CustomSidekiq < Base - class Worker - include ::Sidekiq::Worker - - sidekiq_options queue: 'pull' - - def perform(type, ids, options = {}) - options[:refresh] = !Chewy.disable_refresh_async if Chewy.disable_refresh_async - type.constantize.import!(ids, options) - end - end - - def update(type, objects, _options = {}) - return unless Chewy.enabled? - - ids = type.root.id ? Array.wrap(objects) : type.adapter.identify(objects) - - return if ids.empty? - - Worker.perform_async(type.name, ids) + class CustomSidekiq < Sidekiq + def update(_type, _objects, _options = {}) + super if Chewy.enabled? end - - def leave; end end end end diff --git a/spec/services/batched_remove_status_service_spec.rb b/spec/services/batched_remove_status_service_spec.rb index f84256f18..239859f06 100644 --- a/spec/services/batched_remove_status_service_spec.rb +++ b/spec/services/batched_remove_status_service_spec.rb @@ -26,6 +26,11 @@ RSpec.describe BatchedRemoveStatusService, type: :service do subject.call([status1, status2]) end + it 'removes statuses' do + expect { Status.find(status1.id) }.to raise_error ActiveRecord::RecordNotFound + expect { Status.find(status2.id) }.to raise_error ActiveRecord::RecordNotFound + end + it 'removes statuses from author\'s home feed' do expect(HomeFeed.new(alice).get(10)).to_not include([status1.id, status2.id]) end -- cgit From 1cf2c3a810bba937c7702c342a3ff37c3d37391a Mon Sep 17 00:00:00 2001 From: ThibG Date: Tue, 22 Dec 2020 17:14:32 +0100 Subject: Fix external user creation failing when invite request text is required (#15405) * Fix external user creation failing when invite request text is required Also fixes tootctl-based user creation. * Add test about invites when invite request text is otherwise required Co-authored-by: Claire --- app/models/user.rb | 12 ++++++++++-- lib/mastodon/accounts_cli.rb | 2 +- lib/tasks/mastodon.rake | 2 +- spec/controllers/auth/registrations_controller_spec.rb | 5 ++++- 4 files changed, 16 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/app/models/user.rb b/app/models/user.rb index dd96bbf8c..f8c8a6ab5 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -83,7 +83,7 @@ class User < ApplicationRecord has_one :invite_request, class_name: 'UserInviteRequest', inverse_of: :user, dependent: :destroy accepts_nested_attributes_for :invite_request, reject_if: ->(attributes) { attributes['text'].blank? && !Setting.require_invite_text } - validates :invite_request, presence: true, on: :create, if: -> { Setting.require_invite_text && !invited? } + validates :invite_request, presence: true, on: :create, if: :invite_text_required? validates :locale, inclusion: I18n.available_locales.map(&:to_s), if: :locale? validates_with BlacklistedEmailValidator, on: :create @@ -128,7 +128,7 @@ class User < ApplicationRecord to: :settings, prefix: :setting, allow_nil: false attr_reader :invite_code, :sign_in_token_attempt - attr_writer :external + attr_writer :external, :bypass_invite_request_check def confirmed? confirmed_at.present? @@ -429,6 +429,10 @@ class User < ApplicationRecord !!@external end + def bypass_invite_request_check? + @bypass_invite_request_check + end + def sanitize_languages return if chosen_languages.nil? chosen_languages.reject!(&:blank?) @@ -466,4 +470,8 @@ class User < ApplicationRecord def validate_email_dns? email_changed? && !(Rails.env.test? || Rails.env.development?) end + + def invite_text_required? + Setting.require_invite_text && !invited? && !external? && !bypass_invite_request_check? + end end diff --git a/lib/mastodon/accounts_cli.rb b/lib/mastodon/accounts_cli.rb index 5275f04cf..cdd1db995 100644 --- a/lib/mastodon/accounts_cli.rb +++ b/lib/mastodon/accounts_cli.rb @@ -77,7 +77,7 @@ module Mastodon def create(username) account = Account.new(username: username) password = SecureRandom.hex - user = User.new(email: options[:email], password: password, agreement: true, approved: true, admin: options[:role] == 'admin', moderator: options[:role] == 'moderator', confirmed_at: options[:confirmed] ? Time.now.utc : nil) + user = User.new(email: options[:email], password: password, agreement: true, approved: true, admin: options[:role] == 'admin', moderator: options[:role] == 'moderator', confirmed_at: options[:confirmed] ? Time.now.utc : nil, bypass_invite_request_check: true) if options[:reattach] account = Account.find_local(username) || Account.new(username: username) diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index 9e80989ef..2ad1e778b 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -412,7 +412,7 @@ namespace :mastodon do password = SecureRandom.hex(16) - user = User.new(admin: true, email: email, password: password, confirmed_at: Time.now.utc, account_attributes: { username: username }) + user = User.new(admin: true, email: email, password: password, confirmed_at: Time.now.utc, account_attributes: { username: username }, bypass_invite_request_check: true) user.save(validate: false) prompt.ok "You can login with the password: #{password}" diff --git a/spec/controllers/auth/registrations_controller_spec.rb b/spec/controllers/auth/registrations_controller_spec.rb index c701a3b8b..ccf304a93 100644 --- a/spec/controllers/auth/registrations_controller_spec.rb +++ b/spec/controllers/auth/registrations_controller_spec.rb @@ -195,16 +195,19 @@ RSpec.describe Auth::RegistrationsController, type: :controller do end end - context 'approval-based registrations with valid invite' do + context 'approval-based registrations with valid invite and required invite text' do around do |example| registrations_mode = Setting.registrations_mode + require_invite_text = Setting.require_invite_text example.run + Setting.require_invite_text = require_invite_text Setting.registrations_mode = registrations_mode end subject do inviter = Fabricate(:user, confirmed_at: 2.days.ago) Setting.registrations_mode = 'approved' + Setting.require_invite_text = true request.headers["Accept-Language"] = accept_language invite = Fabricate(:invite, user: inviter, max_uses: nil, expires_at: 1.hour.from_now) post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', 'invite_code': invite.code, agreement: 'true' } } -- cgit From 444b21b55ff5768e4cbbaf7cfa8285c65a4b54f9 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 23 Dec 2020 01:36:13 +0100 Subject: Bump version to 3.3.0rc3 (#15411) --- CHANGELOG.md | 8 +++++--- lib/mastodon/version.rb | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/CHANGELOG.md b/CHANGELOG.md index 4396dc96c..12c996641 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,7 +55,7 @@ All notable changes to this project will be documented in this file. - Add indication to admin UI of whether a report has been forwarded ([ThibG](https://github.com/tootsuite/mastodon/pull/13237)) - Add display of reasons for joining of an account in admin UI ([mashirozx](https://github.com/tootsuite/mastodon/pull/15265)) - Add option to obfuscate domain name in public list of domain blocks ([Gargron](https://github.com/tootsuite/mastodon/pull/15355)) -- Add option to make reasons for joining required on sign-up ([ThibG](https://github.com/tootsuite/mastodon/pull/15326), [ThibG](https://github.com/tootsuite/mastodon/pull/15358)) +- Add option to make reasons for joining required on sign-up ([ThibG](https://github.com/tootsuite/mastodon/pull/15326), [ThibG](https://github.com/tootsuite/mastodon/pull/15358), [ThibG](https://github.com/tootsuite/mastodon/pull/15385), [ThibG](https://github.com/tootsuite/mastodon/pull/15405)) - Add ActivityPub follower synchronization mechanism ([ThibG](https://github.com/tootsuite/mastodon/pull/14510), [ThibG](https://github.com/tootsuite/mastodon/pull/15026)) - Add outbox attribute to instance actor ([ThibG](https://github.com/tootsuite/mastodon/pull/14721)) - Add featured hashtags as an ActivityPub collection ([Gargron](https://github.com/tootsuite/mastodon/pull/11595), [noellabo](https://github.com/tootsuite/mastodon/pull/15277)) @@ -66,7 +66,7 @@ All notable changes to this project will be documented in this file. - Add `tootctl accounts merge` ([Gargron](https://github.com/tootsuite/mastodon/pull/15201), [ThibG](https://github.com/tootsuite/mastodon/pull/15264), [ThibG](https://github.com/tootsuite/mastodon/pull/15256)) - Has someone changed their domain or subdomain thereby creating two accounts where there should be one? - This command will fix it on your end -- Add `tootctl maintenance fix-duplicates` ([ThibG](https://github.com/tootsuite/mastodon/pull/14860), [Gargron](https://github.com/tootsuite/mastodon/pull/15223)) +- Add `tootctl maintenance fix-duplicates` ([ThibG](https://github.com/tootsuite/mastodon/pull/14860), [Gargron](https://github.com/tootsuite/mastodon/pull/15223), [ThibG](https://github.com/tootsuite/mastodon/pull/15373)) - Index corruption in the database? - This command is for you - **Add support for managing multiple stream subscriptions in a single connection** ([Gargron](https://github.com/tootsuite/mastodon/pull/14524), [Gargron](https://github.com/tootsuite/mastodon/pull/14566), [mfmfuyu](https://github.com/tootsuite/mastodon/pull/14859), [zunda](https://github.com/tootsuite/mastodon/pull/14608)) @@ -77,6 +77,7 @@ All notable changes to this project will be documented in this file. - Add `GET /api/v1/accounts/:id/featured_tags` to REST API ([noellabo](https://github.com/tootsuite/mastodon/pull/11817), [noellabo](https://github.com/tootsuite/mastodon/pull/15270)) - Add stoplight for object storage failures, return HTTP 503 in REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/13043)) - Add optional `tootctl remove media` cronjob in Helm chart ([dunn](https://github.com/tootsuite/mastodon/pull/14396)) +- Add clean error message when `RAILS_ENV` is unset ([ThibG](https://github.com/tootsuite/mastodon/pull/15381)) ### Changed @@ -84,7 +85,7 @@ All notable changes to this project will be documented in this file. - Background of the overlay matches the color of the image - Action bar to interact with or open the toot from the modal - Change order of announcements in admin UI to be newest-first ([ThibG](https://github.com/tootsuite/mastodon/pull/15091)) -- **Change account suspensions to be reversible by default** ([Gargron](https://github.com/tootsuite/mastodon/pull/14726), [ThibG](https://github.com/tootsuite/mastodon/pull/15152), [ThibG](https://github.com/tootsuite/mastodon/pull/15106), [ThibG](https://github.com/tootsuite/mastodon/pull/15100), [ThibG](https://github.com/tootsuite/mastodon/pull/15099), [noellabo](https://github.com/tootsuite/mastodon/pull/14855)) +- **Change account suspensions to be reversible by default** ([Gargron](https://github.com/tootsuite/mastodon/pull/14726), [ThibG](https://github.com/tootsuite/mastodon/pull/15152), [ThibG](https://github.com/tootsuite/mastodon/pull/15106), [ThibG](https://github.com/tootsuite/mastodon/pull/15100), [ThibG](https://github.com/tootsuite/mastodon/pull/15099), [noellabo](https://github.com/tootsuite/mastodon/pull/14855), [ThibG](https://github.com/tootsuite/mastodon/pull/15380)) - Suspensions no longer equal deletions - A suspended account can be unsuspended with minimal consequences for 30 days - Immediate deletion of data is still available as an explicit option @@ -162,6 +163,7 @@ All notable changes to this project will be documented in this file. - Fix inefficiency when fetching bookmarks ([akihikodaki](https://github.com/tootsuite/mastodon/pull/14674)) - Fix inefficiency when fetching favourites ([akihikodaki](https://github.com/tootsuite/mastodon/pull/14673)) - Fix inefficiency when fetching media-only account timeline ([akihikodaki](https://github.com/tootsuite/mastodon/pull/14675)) +- Fix inefficieny when deleting accounts ([Gargron](https://github.com/tootsuite/mastodon/pull/15387), [ThibG](https://github.com/tootsuite/mastodon/pull/15409), [ThibG](https://github.com/tootsuite/mastodon/pull/15407), [ThibG](https://github.com/tootsuite/mastodon/pull/15408), [ThibG](https://github.com/tootsuite/mastodon/pull/15402)) - Fix redundant query when processing batch actions on custom emojis ([niwatori24](https://github.com/tootsuite/mastodon/pull/14534)) - Fix slow distinct queries where grouped queries are faster ([Gargron](https://github.com/tootsuite/mastodon/pull/15287)) - Fix performance on instances list in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/15282)) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 7282ea7b5..95ae777f2 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -17,7 +17,7 @@ module Mastodon end def flags - 'rc2' + 'rc3' end def suffix -- cgit