From 30c191aaa03beb8bc4de4bf33db671fb98bafc96 Mon Sep 17 00:00:00 2001 From: jsgoldstein Date: Thu, 24 Aug 2023 10:40:04 -0400 Subject: [PATCH 01/81] Add new public status index (#26344) Co-authored-by: Eugen Rochko Co-authored-by: Claire --- app/chewy/accounts_index.rb | 2 +- app/chewy/public_statuses_index.rb | 50 +++++++++++++ app/chewy/statuses_index.rb | 22 +++--- .../api/v1/accounts/credentials_controller.rb | 1 + .../settings/privacy_controller.rb | 2 +- .../public_statuses_index_importer.rb | 41 ++++++++++ app/lib/search_query_transformer.rb | 49 +++++++++--- app/lib/vacuum/statuses_vacuum.rb | 9 ++- app/models/account.rb | 1 + .../concerns/account_statuses_search.rb | 44 +++++++++++ app/models/concerns/status_search_concern.rb | 54 +++++++++++++ app/models/status.rb | 37 ++------- .../activitypub/actor_serializer.rb | 8 +- app/services/batched_remove_status_service.rb | 5 +- app/services/search_service.rb | 32 +++----- app/services/statuses_search_service.rb | 75 +++++++++++++++++++ app/views/settings/privacy/show.html.haml | 3 + .../add_to_public_statuses_index_worker.rb | 15 ++++ ...emove_from_public_statuses_index_worker.rb | 15 ++++ app/workers/scheduler/indexing_scheduler.rb | 2 +- config/locales/simple_form.en.yml | 2 + lib/mastodon/cli/search.rb | 1 + spec/chewy/public_statuses_index_spec.rb | 31 ++++++++ .../public_statuses_index_importer_spec.rb | 16 ++++ spec/lib/search_query_transformer_spec.rb | 4 +- .../concerns/account_statuses_search_spec.rb | 66 ++++++++++++++++ ...dd_to_public_statuses_index_worker_spec.rb | 42 +++++++++++ ..._from_public_statuses_index_worker_spec.rb | 42 +++++++++++ 28 files changed, 584 insertions(+), 87 deletions(-) create mode 100644 app/chewy/public_statuses_index.rb create mode 100644 app/lib/importer/public_statuses_index_importer.rb create mode 100644 app/models/concerns/account_statuses_search.rb create mode 100644 app/models/concerns/status_search_concern.rb create mode 100644 app/services/statuses_search_service.rb create mode 100644 app/workers/add_to_public_statuses_index_worker.rb create mode 100644 app/workers/remove_from_public_statuses_index_worker.rb create mode 100644 spec/chewy/public_statuses_index_spec.rb create mode 100644 spec/lib/importer/public_statuses_index_importer_spec.rb create mode 100644 spec/models/concerns/account_statuses_search_spec.rb create mode 100644 spec/workers/add_to_public_statuses_index_worker_spec.rb create mode 100644 spec/workers/remove_from_public_statuses_index_worker_spec.rb diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb index 1f8571c09..61e3399aa 100644 --- a/app/chewy/accounts_index.rb +++ b/app/chewy/accounts_index.rb @@ -62,6 +62,6 @@ class AccountsIndex < Chewy::Index field(:last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at }) field(:display_name, type: 'text', analyzer: 'verbatim') { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' } field(:username, type: 'text', analyzer: 'verbatim', value: ->(account) { [account.username, account.domain].compact.join('@') }) { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' } - field(:text, type: 'text', value: ->(account) { account.searchable_text }) { field :stemmed, type: 'text', analyzer: 'natural' } + field(:text, type: 'text', analyzer: 'whitespace', value: ->(account) { account.searchable_text }) { field :stemmed, type: 'text', analyzer: 'natural' } end end diff --git a/app/chewy/public_statuses_index.rb b/app/chewy/public_statuses_index.rb new file mode 100644 index 000000000..1fad5de3a --- /dev/null +++ b/app/chewy/public_statuses_index.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +class PublicStatusesIndex < Chewy::Index + settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: { + filter: { + english_stop: { + type: 'stop', + stopwords: '_english_', + }, + + english_stemmer: { + type: 'stemmer', + language: 'english', + }, + + english_possessive_stemmer: { + type: 'stemmer', + language: 'possessive_english', + }, + }, + + analyzer: { + content: { + tokenizer: 'uax_url_email', + filter: %w( + english_possessive_stemmer + lowercase + asciifolding + cjk_width + english_stop + english_stemmer + ), + }, + }, + } + + index_scope ::Status.unscoped + .kept + .indexable + .includes(:media_attachments, :preloadable_poll, :preview_cards) + + root date_detection: false do + field(:id, type: 'keyword') + field(:account_id, type: 'long') + field(:text, type: 'text', analyzer: 'whitespace', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } + field(:language, type: 'keyword') + field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) + field(:created_at, type: 'date') + end +end diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index 9f680efa5..130f8801d 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -1,23 +1,24 @@ # frozen_string_literal: true class StatusesIndex < Chewy::Index - include FormattingHelper - settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: { filter: { english_stop: { type: 'stop', stopwords: '_english_', }, + english_stemmer: { type: 'stemmer', language: 'english', }, + english_possessive_stemmer: { type: 'stemmer', language: 'possessive_english', }, }, + analyzer: { content: { tokenizer: 'uax_url_email', @@ -35,7 +36,7 @@ class StatusesIndex < Chewy::Index # We do not use delete_if option here because it would call a method that we # expect to be called with crutches without crutches, causing n+1 queries - index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preloadable_poll) + index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preloadable_poll, :preview_cards) crutch :mentions do |collection| data = ::Mention.where(status_id: collection.map(&:id)).where(account: Account.local, silent: false).pluck(:status_id, :account_id) @@ -63,13 +64,12 @@ class StatusesIndex < Chewy::Index end root date_detection: false do - field :id, type: 'long' - field :account_id, type: 'long' - - field :text, type: 'text', value: ->(status) { status.searchable_text } do - field :stemmed, type: 'text', analyzer: 'content' - end - - field :searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) } + field(:id, type: 'keyword') + field(:account_id, type: 'long') + field(:text, type: 'text', analyzer: 'whitespace', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } + field(:searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) }) + field(:language, type: 'keyword') + field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) + field(:created_at, type: 'date') end end diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb index 7c7d70fd3..76ba75824 100644 --- a/app/controllers/api/v1/accounts/credentials_controller.rb +++ b/app/controllers/api/v1/accounts/credentials_controller.rb @@ -30,6 +30,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController :bot, :discoverable, :hide_collections, + :indexable, fields_attributes: [:name, :value] ) end diff --git a/app/controllers/settings/privacy_controller.rb b/app/controllers/settings/privacy_controller.rb index c2648eedd..1102c89fa 100644 --- a/app/controllers/settings/privacy_controller.rb +++ b/app/controllers/settings/privacy_controller.rb @@ -18,7 +18,7 @@ class Settings::PrivacyController < Settings::BaseController private def account_params - params.require(:account).permit(:discoverable, :unlocked, :show_collections, settings: UserSettings.keys) + params.require(:account).permit(:discoverable, :unlocked, :indexable, :show_collections, settings: UserSettings.keys) end def set_account diff --git a/app/lib/importer/public_statuses_index_importer.rb b/app/lib/importer/public_statuses_index_importer.rb new file mode 100644 index 000000000..8e36e36f9 --- /dev/null +++ b/app/lib/importer/public_statuses_index_importer.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +class Importer::PublicStatusesIndexImporter < Importer::BaseImporter + def import! + indexable_statuses_scope.find_in_batches(batch_size: @batch_size) do |batch| + in_work_unit(batch.map(&:status_id)) do |status_ids| + bulk = ActiveRecord::Base.connection_pool.with_connection do + Chewy::Index::Import::BulkBuilder.new(index, to_index: Status.includes(:media_attachments, :preloadable_poll).where(id: status_ids)).bulk_body + end + + indexed = 0 + deleted = 0 + + bulk.map! do |entry| + if entry[:index] + indexed += 1 + else + deleted += 1 + end + entry + end + + Chewy::Index::Import::BulkRequest.new(index).perform(bulk) + + [indexed, deleted] + end + end + + wait! + end + + private + + def index + PublicStatusesIndex + end + + def indexable_statuses_scope + Status.indexable.select('"statuses"."id", COALESCE("statuses"."reblog_of_id", "statuses"."id") AS status_id') + end +end diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index aef05e9d9..dad99cbd2 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -36,7 +36,7 @@ class SearchQueryTransformer < Parslet::Transform def clause_to_filter(clause) case clause when PrefixClause - { term: { clause.filter => clause.term } } + { clause.type => { clause.filter => clause.term } } else raise "Unexpected clause type: #{clause}" end @@ -47,12 +47,10 @@ class SearchQueryTransformer < Parslet::Transform class << self def symbol(str) case str - when '+' + when '+', nil :must when '-' :must_not - when nil - :should else raise "Unknown operator: #{str}" end @@ -81,23 +79,52 @@ class SearchQueryTransformer < Parslet::Transform end class PrefixClause - attr_reader :filter, :operator, :term + attr_reader :type, :filter, :operator, :term def initialize(prefix, term) @operator = :filter + case prefix + when 'has', 'is' + @filter = :properties + @type = :term + @term = term + when 'language' + @filter = :language + @type = :term + @term = term when 'from' @filter = :account_id - - username, domain = term.gsub(/\A@/, '').split('@') - domain = nil if TagManager.instance.local_domain?(domain) - account = Account.find_remote!(username, domain) - - @term = account.id + @type = :term + @term = account_id_from_term(term) + when 'before' + @filter = :created_at + @type = :range + @term = { lt: term } + when 'after' + @filter = :created_at + @type = :range + @term = { gt: term } + when 'during' + @filter = :created_at + @type = :range + @term = { gte: term, lte: term } else raise Mastodon::SyntaxError end end + + private + + def account_id_from_term(term) + username, domain = term.gsub(/\A@/, '').split('@') + domain = nil if TagManager.instance.local_domain?(domain) + account = Account.find_remote(username, domain) + + # If the account is not found, we want to return empty results, so return + # an ID that does not exist + account&.id || -1 + end end rule(clause: subtree(:clause)) do diff --git a/app/lib/vacuum/statuses_vacuum.rb b/app/lib/vacuum/statuses_vacuum.rb index 28c087b1c..ad1de0738 100644 --- a/app/lib/vacuum/statuses_vacuum.rb +++ b/app/lib/vacuum/statuses_vacuum.rb @@ -20,7 +20,10 @@ class Vacuum::StatusesVacuum statuses.direct_visibility .includes(mentions: :account) .find_each(&:unlink_from_conversations!) - remove_from_search_index(statuses.ids) if Chewy.enabled? + if Chewy.enabled? + remove_from_index(statuses.ids, 'chewy:queue:StatusesIndex') + remove_from_index(statuses.ids, 'chewy:queue:PublicStatusesIndex') + end # Foreign keys take care of most associated records for us. # Media attachments will be orphaned. @@ -38,7 +41,7 @@ class Vacuum::StatusesVacuum Mastodon::Snowflake.id_at(@retention_period.ago, with_random: false) end - def remove_from_search_index(status_ids) - with_redis { |redis| redis.sadd('chewy:queue:StatusesIndex', status_ids) } + def remove_from_index(status_ids, index) + with_redis { |redis| redis.sadd(index, status_ids) } end end diff --git a/app/models/account.rb b/app/models/account.rb index b1cb9eb5d..244f3da83 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -82,6 +82,7 @@ class Account < ApplicationRecord include DomainMaterializable include AccountMerging include AccountSearch + include AccountStatusesSearch enum protocol: { ostatus: 0, activitypub: 1 } enum suspension_origin: { local: 0, remote: 1 }, _prefix: true diff --git a/app/models/concerns/account_statuses_search.rb b/app/models/concerns/account_statuses_search.rb new file mode 100644 index 000000000..563a87105 --- /dev/null +++ b/app/models/concerns/account_statuses_search.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module AccountStatusesSearch + extend ActiveSupport::Concern + + included do + after_update_commit :enqueue_update_public_statuses_index, if: :saved_change_to_indexable? + after_destroy_commit :enqueue_remove_from_public_statuses_index, if: :indexable? + end + + def enqueue_update_public_statuses_index + if indexable? + enqueue_add_to_public_statuses_index + else + enqueue_remove_from_public_statuses_index + end + end + + def enqueue_add_to_public_statuses_index + return unless Chewy.enabled? + + AddToPublicStatusesIndexWorker.perform_async(id) + end + + def enqueue_remove_from_public_statuses_index + return unless Chewy.enabled? + + RemoveFromPublicStatusesIndexWorker.perform_async(id) + end + + def add_to_public_statuses_index! + return unless Chewy.enabled? + + statuses.indexable.find_in_batches do |batch| + PublicStatusesIndex.import(query: batch) + end + end + + def remove_from_public_statuses_index! + return unless Chewy.enabled? + + PublicStatusesIndex.filter(term: { account_id: id }).delete_all + end +end diff --git a/app/models/concerns/status_search_concern.rb b/app/models/concerns/status_search_concern.rb new file mode 100644 index 000000000..21048b568 --- /dev/null +++ b/app/models/concerns/status_search_concern.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module StatusSearchConcern + extend ActiveSupport::Concern + + included do + scope :indexable, -> { without_reblogs.where(visibility: :public).joins(:account).where(account: { indexable: true }) } + end + + def searchable_by(preloaded = nil) + ids = [] + + ids << account_id if local? + + if preloaded.nil? + ids += mentions.joins(:account).merge(Account.local).active.pluck(:account_id) + ids += favourites.joins(:account).merge(Account.local).pluck(:account_id) + ids += reblogs.joins(:account).merge(Account.local).pluck(:account_id) + ids += bookmarks.joins(:account).merge(Account.local).pluck(:account_id) + ids += poll.votes.joins(:account).merge(Account.local).pluck(:account_id) if poll.present? + else + ids += preloaded.mentions[id] || [] + ids += preloaded.favourites[id] || [] + ids += preloaded.reblogs[id] || [] + ids += preloaded.bookmarks[id] || [] + ids += preloaded.votes[id] || [] + end + + ids.uniq + end + + def searchable_text + [ + spoiler_text, + FormattingHelper.extract_status_plain_text(self), + preloadable_poll&.options&.join("\n\n"), + ordered_media_attachments.map(&:description).join("\n\n"), + ].compact.join("\n\n") + end + + def searchable_properties + [].tap do |properties| + properties << 'image' if ordered_media_attachments.any?(&:image?) + properties << 'video' if ordered_media_attachments.any?(&:video?) + properties << 'audio' if ordered_media_attachments.any?(&:audio?) + properties << 'media' if with_media? + properties << 'poll' if with_poll? + properties << 'link' if with_preview_card? + properties << 'embed' if preview_cards.any?(&:video?) + properties << 'sensitive' if sensitive? + properties << 'reply' if reply? + end + end +end diff --git a/app/models/status.rb b/app/models/status.rb index 86fd8334a..760b8ec33 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -37,6 +37,7 @@ class Status < ApplicationRecord include StatusSnapshotConcern include RateLimitable include StatusSafeReblogInsert + include StatusSearchConcern rate_limit by: :account, family: :statuses @@ -47,6 +48,7 @@ class Status < ApplicationRecord attr_accessor :override_timestamps update_index('statuses', :proper) + update_index('public_statuses', :proper) enum visibility: { public: 0, unlisted: 1, private: 2, direct: 3, limited: 4 }, _suffix: :visibility @@ -165,37 +167,6 @@ class Status < ApplicationRecord "v3:#{super}" end - def searchable_by(preloaded = nil) - ids = [] - - ids << account_id if local? - - if preloaded.nil? - ids += mentions.joins(:account).merge(Account.local).active.pluck(:account_id) - ids += favourites.joins(:account).merge(Account.local).pluck(:account_id) - ids += reblogs.joins(:account).merge(Account.local).pluck(:account_id) - ids += bookmarks.joins(:account).merge(Account.local).pluck(:account_id) - ids += poll.votes.joins(:account).merge(Account.local).pluck(:account_id) if poll.present? - else - ids += preloaded.mentions[id] || [] - ids += preloaded.favourites[id] || [] - ids += preloaded.reblogs[id] || [] - ids += preloaded.bookmarks[id] || [] - ids += preloaded.votes[id] || [] - end - - ids.uniq - end - - def searchable_text - [ - spoiler_text, - FormattingHelper.extract_status_plain_text(self), - preloadable_poll ? preloadable_poll.options.join("\n\n") : nil, - ordered_media_attachments.map(&:description).join("\n\n"), - ].compact.join("\n\n") - end - def to_log_human_identifier account.acct end @@ -270,6 +241,10 @@ class Status < ApplicationRecord preview_cards.any? end + def with_poll? + preloadable_poll.present? + end + def non_sensitive_with_media? !sensitive? && with_media? end diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb index 4998d0039..31f39954f 100644 --- a/app/serializers/activitypub/actor_serializer.rb +++ b/app/serializers/activitypub/actor_serializer.rb @@ -8,13 +8,13 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer context_extensions :manually_approves_followers, :featured, :also_known_as, :moved_to, :property_value, :discoverable, :olm, :suspended, - :memorial + :memorial, :indexable attributes :id, :type, :following, :followers, :inbox, :outbox, :featured, :featured_tags, :preferred_username, :name, :summary, :url, :manually_approves_followers, - :discoverable, :published, :memorial + :discoverable, :indexable, :published, :memorial has_one :public_key, serializer: ActivityPub::PublicKeySerializer @@ -99,6 +99,10 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer object.suspended? ? false : (object.discoverable || false) end + def indexable + object.suspended? ? false : (object.indexable || false) + end + def name object.suspended? ? object.username : (object.display_name.presence || object.username) end diff --git a/app/services/batched_remove_status_service.rb b/app/services/batched_remove_status_service.rb index f5cb339cd..c54cc1d35 100644 --- a/app/services/batched_remove_status_service.rb +++ b/app/services/batched_remove_status_service.rb @@ -35,7 +35,10 @@ class BatchedRemoveStatusService < BaseService # Since we skipped all callbacks, we also need to manually # deindex the statuses - Chewy.strategy.current.update(StatusesIndex, statuses_and_reblogs) if Chewy.enabled? + if Chewy.enabled? + Chewy.strategy.current.update(StatusesIndex, statuses_and_reblogs) + Chewy.strategy.current.update(PublicStatusesIndex, statuses_and_reblogs) + end return if options[:skip_side_effects] diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 30937471b..4e1e7ea26 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -39,25 +39,15 @@ class SearchService < BaseService end def perform_statuses_search! - definition = parsed_query.apply(StatusesIndex.filter(term: { searchable_by: @account.id })) - - definition = definition.filter(term: { account_id: @options[:account_id] }) if @options[:account_id].present? - - if @options[:min_id].present? || @options[:max_id].present? - range = {} - range[:gt] = @options[:min_id].to_i if @options[:min_id].present? - range[:lt] = @options[:max_id].to_i if @options[:max_id].present? - definition = definition.filter(range: { id: range }) - end - - results = definition.limit(@limit).offset(@offset).objects.compact - account_ids = results.map(&:account_id) - account_domains = results.map(&:account_domain) - preloaded_relations = @account.relations_map(account_ids, account_domains) - - results.reject { |status| StatusFilter.new(status, @account, preloaded_relations).filtered? } - rescue Faraday::ConnectionFailed, Parslet::ParseFailed - [] + StatusesSearchService.new.call( + @query, + @account, + limit: @limit, + offset: @offset, + account_id: @options[:account_id], + min_id: @options[:min_id], + max_id: @options[:max_id] + ) end def perform_hashtags_search! @@ -114,8 +104,4 @@ class SearchService < BaseService def statuses_search? @options[:type].blank? || @options[:type] == 'statuses' end - - def parsed_query - SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query)) - end end diff --git a/app/services/statuses_search_service.rb b/app/services/statuses_search_service.rb new file mode 100644 index 000000000..21d6b71b7 --- /dev/null +++ b/app/services/statuses_search_service.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +class StatusesSearchService < BaseService + def call(query, account = nil, options = {}) + @query = query&.strip + @account = account + @options = options + @limit = options[:limit].to_i + @offset = options[:offset].to_i + + status_search_results + end + + private + + def status_search_results + definition = parsed_query.apply( + StatusesIndex.filter( + bool: { + should: [ + publicly_searchable, + non_publicly_searchable, + ], + + minimum_should_match: 1, + } + ) + ) + + # This is the best way to submit identical queries to multi-indexes though chewy + definition.instance_variable_get(:@parameters)[:indices].value[:indices] << PublicStatusesIndex + + results = definition.collapse(field: :id).order(_id: { order: :desc }).limit(@limit).offset(@offset).objects.compact + account_ids = results.map(&:account_id) + account_domains = results.map(&:account_domain) + preloaded_relations = @account.relations_map(account_ids, account_domains) + + results.reject { |status| StatusFilter.new(status, @account, preloaded_relations).filtered? } + rescue Faraday::ConnectionFailed, Parslet::ParseFailed + [] + end + + def publicly_searchable + { + bool: { + must_not: { + exists: { + field: 'searchable_by', + }, + }, + }, + } + end + + def non_publicly_searchable + { + bool: { + must: [ + { + exists: { + field: 'searchable_by', + }, + }, + { + term: { searchable_by: @account.id }, + }, + ], + }, + } + end + + def parsed_query + SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query)) + end +end diff --git a/app/views/settings/privacy/show.html.haml b/app/views/settings/privacy/show.html.haml index ce31e60f0..3c1438258 100644 --- a/app/views/settings/privacy/show.html.haml +++ b/app/views/settings/privacy/show.html.haml @@ -24,6 +24,9 @@ %p.lead= t('privacy.search_hint_html') + .fields-group + = f.input :indexable, as: :boolean, wrapper: :with_label + = f.simple_fields_for :settings, current_user.settings do |ff| .fields-group = ff.input :indexable, wrapper: :with_label diff --git a/app/workers/add_to_public_statuses_index_worker.rb b/app/workers/add_to_public_statuses_index_worker.rb new file mode 100644 index 000000000..409e5e708 --- /dev/null +++ b/app/workers/add_to_public_statuses_index_worker.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class AddToPublicStatusesIndexWorker + include Sidekiq::Worker + + def perform(account_id) + account = Account.find(account_id) + + return unless account.indexable? + + account.add_to_public_statuses_index! + rescue ActiveRecord::RecordNotFound + true + end +end diff --git a/app/workers/remove_from_public_statuses_index_worker.rb b/app/workers/remove_from_public_statuses_index_worker.rb new file mode 100644 index 000000000..e108a5c20 --- /dev/null +++ b/app/workers/remove_from_public_statuses_index_worker.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class RemoveFromPublicStatusesIndexWorker + include Sidekiq::Worker + + def perform(account_id) + account = Account.find(account_id) + + return if account.indexable? + + account.remove_from_public_statuses_index! + rescue ActiveRecord::RecordNotFound + true + end +end diff --git a/app/workers/scheduler/indexing_scheduler.rb b/app/workers/scheduler/indexing_scheduler.rb index 2868a3b71..6c770d5a8 100644 --- a/app/workers/scheduler/indexing_scheduler.rb +++ b/app/workers/scheduler/indexing_scheduler.rb @@ -23,6 +23,6 @@ class Scheduler::IndexingScheduler end def indexes - [AccountsIndex, TagsIndex, StatusesIndex] + [AccountsIndex, TagsIndex, PublicStatusesIndex, StatusesIndex] end end diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 443b7617f..efda7b778 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -6,6 +6,7 @@ en: discoverable: Your public posts and profile may be featured or recommended in various areas of Mastodon and your profile may be suggested to other users. display_name: Your full name or your fun name. fields: Your homepage, pronouns, age, anything you want. + indexable: Your public posts may appear in search results on Mastodon. People who have interacted with your posts may be able to search them regardless. note: 'You can @mention other people or #hashtags.' show_collections: People will be able to browse through your follows and followers. People that you follow will see that you follow them regardless. unlocked: People will be able to follow you without requesting approval. Uncheck if you want to review follow requests and chose whether to accept or reject new followers. @@ -143,6 +144,7 @@ en: fields: name: Label value: Content + indexable: Include public posts in search results show_collections: Show follows and followers on profile unlocked: Automatically accept new followers account_alias: diff --git a/lib/mastodon/cli/search.rb b/lib/mastodon/cli/search.rb index 41862b5b6..481e01d8e 100644 --- a/lib/mastodon/cli/search.rb +++ b/lib/mastodon/cli/search.rb @@ -10,6 +10,7 @@ module Mastodon::CLI InstancesIndex, AccountsIndex, TagsIndex, + PublicStatusesIndex, StatusesIndex, ].freeze diff --git a/spec/chewy/public_statuses_index_spec.rb b/spec/chewy/public_statuses_index_spec.rb new file mode 100644 index 000000000..2f93d0ff0 --- /dev/null +++ b/spec/chewy/public_statuses_index_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe PublicStatusesIndex do + describe 'Searching the index' do + before do + mock_elasticsearch_response(described_class, raw_response) + end + + it 'returns results from a query' do + results = described_class.query(match: { name: 'status' }) + + expect(results).to eq [] + end + end + + def raw_response + { + took: 3, + hits: { + hits: [ + { + _id: '0', + _score: 1.6375021, + }, + ], + }, + } + end +end diff --git a/spec/lib/importer/public_statuses_index_importer_spec.rb b/spec/lib/importer/public_statuses_index_importer_spec.rb new file mode 100644 index 000000000..bc7c038a9 --- /dev/null +++ b/spec/lib/importer/public_statuses_index_importer_spec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Importer::PublicStatusesIndexImporter do + describe 'import!' do + let(:pool) { Concurrent::FixedThreadPool.new(5) } + let(:importer) { described_class.new(batch_size: 123, executor: pool) } + + before { Fabricate(:status, account: Fabricate(:account, indexable: true)) } + + it 'indexes relevant statuses' do + expect { importer.import! }.to update_index(PublicStatusesIndex) + end + end +end diff --git a/spec/lib/search_query_transformer_spec.rb b/spec/lib/search_query_transformer_spec.rb index 109533469..953f9acb2 100644 --- a/spec/lib/search_query_transformer_spec.rb +++ b/spec/lib/search_query_transformer_spec.rb @@ -9,8 +9,8 @@ describe SearchQueryTransformer do it 'sets attributes' do transformer = described_class.new.apply(parser) - expect(transformer.should_clauses.first).to be_a(SearchQueryTransformer::TermClause) - expect(transformer.must_clauses.first).to be_nil + expect(transformer.should_clauses.first).to be_nil + expect(transformer.must_clauses.first).to be_a(SearchQueryTransformer::TermClause) expect(transformer.must_not_clauses.first).to be_nil expect(transformer.filter_clauses.first).to be_nil end diff --git a/spec/models/concerns/account_statuses_search_spec.rb b/spec/models/concerns/account_statuses_search_spec.rb new file mode 100644 index 000000000..46362936f --- /dev/null +++ b/spec/models/concerns/account_statuses_search_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe AccountStatusesSearch do + let(:account) { Fabricate(:account, indexable: indexable) } + + before do + allow(Chewy).to receive(:enabled?).and_return(true) + end + + describe '#enqueue_update_public_statuses_index' do + before do + allow(account).to receive(:enqueue_add_to_public_statuses_index) + allow(account).to receive(:enqueue_remove_from_public_statuses_index) + end + + context 'when account is indexable' do + let(:indexable) { true } + + it 'enqueues add_to_public_statuses_index and not to remove_from_public_statuses_index' do + account.enqueue_update_public_statuses_index + expect(account).to have_received(:enqueue_add_to_public_statuses_index) + expect(account).to_not have_received(:enqueue_remove_from_public_statuses_index) + end + end + + context 'when account is not indexable' do + let(:indexable) { false } + + it 'enqueues remove_from_public_statuses_index and not to add_to_public_statuses_index' do + account.enqueue_update_public_statuses_index + expect(account).to have_received(:enqueue_remove_from_public_statuses_index) + expect(account).to_not have_received(:enqueue_add_to_public_statuses_index) + end + end + end + + describe '#enqueue_add_to_public_statuses_index' do + let(:indexable) { true } + let(:worker) { AddToPublicStatusesIndexWorker } + + before do + allow(worker).to receive(:perform_async) + end + + it 'enqueues AddToPublicStatusesIndexWorker' do + account.enqueue_add_to_public_statuses_index + expect(worker).to have_received(:perform_async).with(account.id) + end + end + + describe '#enqueue_remove_from_public_statuses_index' do + let(:indexable) { false } + let(:worker) { RemoveFromPublicStatusesIndexWorker } + + before do + allow(worker).to receive(:perform_async) + end + + it 'enqueues RemoveFromPublicStatusesIndexWorker' do + account.enqueue_remove_from_public_statuses_index + expect(worker).to have_received(:perform_async).with(account.id) + end + end +end diff --git a/spec/workers/add_to_public_statuses_index_worker_spec.rb b/spec/workers/add_to_public_statuses_index_worker_spec.rb new file mode 100644 index 000000000..fa1507224 --- /dev/null +++ b/spec/workers/add_to_public_statuses_index_worker_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe AddToPublicStatusesIndexWorker do + describe '#perform' do + let(:account) { Fabricate(:account, indexable: indexable) } + let(:account_id) { account.id } + + before do + allow(Account).to receive(:find).with(account_id).and_return(account) unless account.nil? + allow(account).to receive(:add_to_public_statuses_index!) unless account.nil? + end + + context 'when account is indexable' do + let(:indexable) { true } + + it 'adds the account to the public statuses index' do + subject.perform(account_id) + expect(account).to have_received(:add_to_public_statuses_index!) + end + end + + context 'when account is not indexable' do + let(:indexable) { false } + + it 'does not add the account to public statuses index' do + subject.perform(account_id) + expect(account).to_not have_received(:add_to_public_statuses_index!) + end + end + + context 'when account does not exist' do + let(:account) { nil } + let(:account_id) { 999 } + + it 'does not raise an error' do + expect { subject.perform(account_id) }.to_not raise_error + end + end + end +end diff --git a/spec/workers/remove_from_public_statuses_index_worker_spec.rb b/spec/workers/remove_from_public_statuses_index_worker_spec.rb new file mode 100644 index 000000000..43ff211ea --- /dev/null +++ b/spec/workers/remove_from_public_statuses_index_worker_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe RemoveFromPublicStatusesIndexWorker do + describe '#perform' do + let(:account) { Fabricate(:account, indexable: indexable) } + let(:account_id) { account.id } + + before do + allow(Account).to receive(:find).with(account_id).and_return(account) unless account.nil? + allow(account).to receive(:remove_from_public_statuses_index!) unless account.nil? + end + + context 'when account is not indexable' do + let(:indexable) { false } + + it 'removes the account from public statuses index' do + subject.perform(account_id) + expect(account).to have_received(:remove_from_public_statuses_index!) + end + end + + context 'when account is indexable' do + let(:indexable) { true } + + it 'does not remove the account from public statuses index' do + subject.perform(account_id) + expect(account).to_not have_received(:remove_from_public_statuses_index!) + end + end + + context 'when account does not exist' do + let(:account) { nil } + let(:account_id) { 999 } + + it 'does not raise an error' do + expect { subject.perform(account_id) }.to_not raise_error + end + end + end +end From 389b7d23dbd32fa0ab563083d76b8aaf0d6137da Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 24 Aug 2023 21:07:39 +0200 Subject: [PATCH 02/81] Fix changelog wording and missing items (#26638) --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 107dfaca3..fe66adc08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -101,7 +101,7 @@ The following changelog entries focus on changes visible to users, administrator - **Change translation feature to cover Content Warnings, poll options and media descriptions** ([c960657](https://github.com/mastodon/mastodon/pull/24175), [S-H-GAMELINKS](https://github.com/mastodon/mastodon/pull/25251), [c960657](https://github.com/mastodon/mastodon/pull/26168), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26452)) - **Change account search to match by text when opted-in** ([jsgoldstein](https://github.com/mastodon/mastodon/pull/25599), [Gargron](https://github.com/mastodon/mastodon/pull/26378)) - **Change import feature to be clearer, less error-prone and more reliable** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21054), [mgmn](https://github.com/mastodon/mastodon/pull/24874)) -- **Change local and federated timelines to be in a single “Live feeds” column** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25641), [Gargron](https://github.com/mastodon/mastodon/pull/25683), [mgmn](https://github.com/mastodon/mastodon/pull/25694), [Plastikmensch](https://github.com/mastodon/mastodon/pull/26247)) +- **Change local and federated timelines to be tabs of a single “Live feeds” column** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25641), [Gargron](https://github.com/mastodon/mastodon/pull/25683), [mgmn](https://github.com/mastodon/mastodon/pull/25694), [Plastikmensch](https://github.com/mastodon/mastodon/pull/26247)) - **Change user archive export to be faster and more reliable, and export `.zip` archives instead of `.tar.gz` ones** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23360), [TheEssem](https://github.com/mastodon/mastodon/pull/25034)) - **Change `mastodon-streaming` systemd unit files to be templated** ([e-nomem](https://github.com/mastodon/mastodon/pull/24751)) - **Change `statsd` integration to disable sidekiq metrics by default** ([mjankowski](https://github.com/mastodon/mastodon/pull/25265), [mjankowski](https://github.com/mastodon/mastodon/pull/25336), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26310)) @@ -189,6 +189,7 @@ The following changelog entries focus on changes visible to users, administrator - **Fix log-in flow when involving both OAuth and external authentication** ([CSDUMMI](https://github.com/mastodon/mastodon/pull/24073)) - **Fix broken links in account gallery** ([c960657](https://github.com/mastodon/mastodon/pull/24218)) - **Fix blocking subdomains of an already-blocked domain** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26392)) +- **Fix migration handler not updating lists** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24808)) - Fix uploading of video files for which `ffprobe` reports `0/0` average framerate ([NicolaiSoeborg](https://github.com/mastodon/mastodon/pull/26500)) - Fix cached posts including stale stats ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26409)) - Fix adding column with default value taking longer on Postgres >= 11 ([Gargron](https://github.com/mastodon/mastodon/pull/26375)) From c66f7565226e6ad37d23fd4b89f99f0f277d83a4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 25 Aug 2023 10:08:44 +0200 Subject: [PATCH 03/81] Update babel monorepo to v7.22.11 (#26640) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 129 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 86 insertions(+), 43 deletions(-) diff --git a/yarn.lock b/yarn.lock index 216e22f68..6894f4388 100644 --- a/yarn.lock +++ b/yarn.lock @@ -50,24 +50,24 @@ integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== "@babel/core@^7.10.4", "@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.22.1": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.10.tgz#aad442c7bcd1582252cb4576747ace35bc122f35" - integrity sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw== + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.11.tgz#8033acaa2aa24c3f814edaaa057f3ce0ba559c24" + integrity sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.22.10" "@babel/generator" "^7.22.10" "@babel/helper-compilation-targets" "^7.22.10" "@babel/helper-module-transforms" "^7.22.9" - "@babel/helpers" "^7.22.10" - "@babel/parser" "^7.22.10" + "@babel/helpers" "^7.22.11" + "@babel/parser" "^7.22.11" "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.10" - "@babel/types" "^7.22.10" + "@babel/traverse" "^7.22.11" + "@babel/types" "^7.22.11" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.2" + json5 "^2.2.3" semver "^6.3.1" "@babel/generator@^7.22.10", "@babel/generator@^7.22.5", "@babel/generator@^7.7.2": @@ -113,6 +113,21 @@ lru-cache "^5.1.1" semver "^6.3.1" +"@babel/helper-create-class-features-plugin@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.11.tgz#4078686740459eeb4af3494a273ac09148dfb213" + integrity sha512-y1grdYL4WzmUDBRGK0pDbIoFd7UZKoDurDzWEoNMYoj1EL+foGRQNyPWDcC+YyegN5y1DUsFFmzjGijB3nSVAQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + semver "^6.3.1" + "@babel/helper-create-class-features-plugin@^7.22.5": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz#dd2612d59eac45588021ac3d6fa976d08f4e95a3" @@ -268,14 +283,14 @@ "@babel/template" "^7.22.5" "@babel/types" "^7.22.10" -"@babel/helpers@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.10.tgz#ae6005c539dfbcb5cd71fb51bfc8a52ba63bc37a" - integrity sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw== +"@babel/helpers@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.11.tgz#b02f5d5f2d7abc21ab59eeed80de410ba70b056a" + integrity sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg== dependencies: "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.10" - "@babel/types" "^7.22.10" + "@babel/traverse" "^7.22.11" + "@babel/types" "^7.22.11" "@babel/highlight@^7.22.10", "@babel/highlight@^7.22.5": version "7.22.10" @@ -286,11 +301,16 @@ chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.10", "@babel/parser@^7.22.5": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== +"@babel/parser@^7.22.11", "@babel/parser@^7.22.5": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.11.tgz#becf8ee33aad2a35ed5607f521fe6e72a615f905" + integrity sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" @@ -640,6 +660,15 @@ "@babel/helper-module-transforms" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-modules-commonjs@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.11.tgz#d7991d3abad199c03b68ee66a64f216c47ffdfae" + integrity sha512-o2+bg7GDS60cJMgz9jWqRUsWkMzLCxp+jFDeDUT5sjRlAxcJWZ2ylNdI7QQ2+CH5hWu7OnN+Cv3htt7AkSf96g== + dependencies: + "@babel/helper-module-transforms" "^7.22.9" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/plugin-transform-modules-commonjs@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" @@ -683,9 +712,9 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-nullish-coalescing-operator@^7.22.3", "@babel/plugin-transform-nullish-coalescing-operator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz#f8872c65776e0b552e0849d7596cddd416c3e381" - integrity sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA== + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz#debef6c8ba795f5ac67cd861a81b744c5d38d9fc" + integrity sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" @@ -877,13 +906,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-typescript@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.5.tgz#5c0f7adfc1b5f38c4dbc8f79b1f0f8074134bd7d" - integrity sha512-SMubA9S7Cb5sGSFFUlqxyClTA9zWJ8qGQrppNUm05LtFuN1ELRFNndkix4zUJrC9F+YivWwa1dHMSyo0e0N9dA== +"@babel/plugin-transform-typescript@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.11.tgz#9f27fb5e51585729374bb767ab6a6d9005a23329" + integrity sha512-0E4/L+7gfvHub7wsbTv03oRtD69X31LByy44fGmFzbZScpupFByMcgCJ0VbBTkzyjSJKuRoGN8tcijOWKTmqOA== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.11" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-typescript" "^7.22.5" @@ -1026,15 +1055,15 @@ "@babel/plugin-transform-react-pure-annotations" "^7.22.5" "@babel/preset-typescript@^7.21.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.22.5.tgz#16367d8b01d640e9a507577ed4ee54e0101e51c8" - integrity sha512-YbPaal9LxztSGhmndR46FmAbkJ/1fAsw293tSU+I5E5h+cnJ3d4GTwyUgGYmOXJYdGA+uNePle4qbaRzj2NISQ== + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.22.11.tgz#f218cd0345524ac888aa3dc32f029de5b064b575" + integrity sha512-tWY5wyCZYBGY7IlalfKI1rLiGlIfnwsRHZqlky0HVv8qviwQ1Uo/05M6+s+TcTCVa6Bmoo2uJW5TMFX6Wa4qVg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-validator-option" "^7.22.5" "@babel/plugin-syntax-jsx" "^7.22.5" - "@babel/plugin-transform-modules-commonjs" "^7.22.5" - "@babel/plugin-transform-typescript" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.11" + "@babel/plugin-transform-typescript" "^7.22.11" "@babel/regjsgen@^0.8.0": version "0.8.0" @@ -1049,9 +1078,9 @@ regenerator-runtime "^0.12.0" "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.22.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.10.tgz#ae3e9631fd947cb7e3610d3e9d8fef5f76696682" - integrity sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ== + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.11.tgz#7a9ba3bbe406ad6f9e8dd4da2ece453eb23a77a4" + integrity sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA== dependencies: regenerator-runtime "^0.14.0" @@ -1080,10 +1109,10 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.10.tgz#20252acb240e746d27c2e82b4484f199cf8141aa" - integrity sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig== +"@babel/traverse@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.11.tgz#71ebb3af7a05ff97280b83f05f8865ac94b2027c" + integrity sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ== dependencies: "@babel/code-frame" "^7.22.10" "@babel/generator" "^7.22.10" @@ -1091,12 +1120,12 @@ "@babel/helper-function-name" "^7.22.5" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.22.10" - "@babel/types" "^7.22.10" + "@babel/parser" "^7.22.11" + "@babel/types" "^7.22.11" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4": +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03" integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg== @@ -1114,6 +1143,15 @@ "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" +"@babel/types@^7.22.10", "@babel/types@^7.22.11", "@babel/types@^7.22.5": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.11.tgz#0e65a6a1d4d9cbaa892b2213f6159485fe632ea2" + integrity sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -3901,7 +3939,12 @@ caniuse-lite@^1.0.30001502: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001515.tgz#418aefeed9d024cd3129bfae0ccc782d4cb8f12b" integrity sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA== -caniuse-lite@^1.0.30001517, caniuse-lite@^1.0.30001520: +caniuse-lite@^1.0.30001517: + version "1.0.30001522" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz#44b87a406c901269adcdb834713e23582dd71856" + integrity sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg== + +caniuse-lite@^1.0.30001520: version "1.0.30001520" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001520.tgz#62e2b7a1c7b35269594cf296a80bdf8cb9565006" integrity sha512-tahF5O9EiiTzwTUqAeFjIZbn4Dnqxzz7ktrgGlMYNLH43Ul26IgTMH/zvL3DG0lZxBYnlT04axvInszUsZULdA== @@ -5050,9 +5093,9 @@ electron-to-chromium@^1.4.428: integrity sha512-/g3UyNDmDd6ebeWapmAoiyy+Sy2HyJ+/X8KyvNeHfKRFfHaA2W8oF5fxD5F3tjBDcjpwo0iek6YNgxNXDBoEtA== electron-to-chromium@^1.4.477: - version "1.4.490" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.490.tgz#d99286f6e915667fa18ea4554def1aa60eb4d5f1" - integrity sha512-6s7NVJz+sATdYnIwhdshx/N/9O6rvMxmhVoDSDFdj6iA45gHR8EQje70+RYsF4GeB+k0IeNSBnP7yG9ZXJFr7A== + version "1.4.500" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.500.tgz#7dd05fdfbe02ed34b9f6099cfe01407b473d5af7" + integrity sha512-P38NO8eOuWOKY1sQk5yE0crNtrjgjJj6r3NrbIKtG18KzCHmHE2Bt+aQA7/y0w3uYsHWxDa6icOohzjLJ4vJ4A== elliptic@^6.5.3: version "6.5.4" @@ -7844,7 +7887,7 @@ json5@^1.0.1, json5@^1.0.2: dependencies: minimist "^1.2.0" -json5@^2.1.2, json5@^2.2.0, json5@^2.2.2: +json5@^2.1.2, json5@^2.2.0, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== From c3a42e1280759ea28bee11f241aef892b148bf6a Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 25 Aug 2023 11:31:20 +0200 Subject: [PATCH 04/81] Add `data-nosnippet` attribute to remote posts and local posts with `noindex` (#26648) --- app/javascript/mastodon/components/status.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx index 30692d1cd..e1728910e 100644 --- a/app/javascript/mastodon/components/status.jsx +++ b/app/javascript/mastodon/components/status.jsx @@ -550,7 +550,7 @@ class Status extends ImmutablePureComponent { return ( -
+
{prepend}
From e3825a13c124b9d2b2af221ef6bbe8ced3e8c3c3 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 25 Aug 2023 12:01:55 +0200 Subject: [PATCH 05/81] Add PublicStatusesCheck to Elasticsearch index check on admin dashboard (#26650) --- app/lib/admin/system_check/elasticsearch_check.rb | 1 + spec/lib/admin/system_check/elasticsearch_check_spec.rb | 2 ++ 2 files changed, 3 insertions(+) diff --git a/app/lib/admin/system_check/elasticsearch_check.rb b/app/lib/admin/system_check/elasticsearch_check.rb index 91070756c..2092ef0dd 100644 --- a/app/lib/admin/system_check/elasticsearch_check.rb +++ b/app/lib/admin/system_check/elasticsearch_check.rb @@ -6,6 +6,7 @@ class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck AccountsIndex, TagsIndex, StatusesIndex, + PublicStatusesIndex, ].freeze def skip? diff --git a/spec/lib/admin/system_check/elasticsearch_check_spec.rb b/spec/lib/admin/system_check/elasticsearch_check_spec.rb index f3918d403..a885640ce 100644 --- a/spec/lib/admin/system_check/elasticsearch_check_spec.rb +++ b/spec/lib/admin/system_check/elasticsearch_check_spec.rb @@ -17,6 +17,7 @@ describe Admin::SystemCheck::ElasticsearchCheck do allow(Chewy.client.indices).to receive_messages(get_mapping: { AccountsIndex.index_name => AccountsIndex.mappings_hash.deep_stringify_keys, StatusesIndex.index_name => StatusesIndex.mappings_hash.deep_stringify_keys, + PublicStatusesIndex.index_name => PublicStatusesIndex.mappings_hash.deep_stringify_keys, InstancesIndex.index_name => InstancesIndex.mappings_hash.deep_stringify_keys, TagsIndex.index_name => TagsIndex.mappings_hash.deep_stringify_keys, }, get_settings: { @@ -90,6 +91,7 @@ describe Admin::SystemCheck::ElasticsearchCheck do allow(Chewy.client.indices).to receive(:get_mapping).and_return({ AccountsIndex.index_name => AccountsIndex.mappings_hash.deep_stringify_keys, StatusesIndex.index_name => StatusesIndex.mappings_hash.deep_stringify_keys, + PublicStatusesIndex.index_name => PublicStatusesIndex.mappings_hash.deep_stringify_keys, InstancesIndex.index_name => InstancesIndex.mappings_hash.deep_stringify_keys, TagsIndex.index_name => TagsIndex.mappings_hash.deep_stringify_keys, }) From f2ec2876a45133668154a757d53283e02ecb20d5 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 25 Aug 2023 12:02:09 +0200 Subject: [PATCH 06/81] =?UTF-8?q?Add=20Elasticsearch/OpenSearch=20version?= =?UTF-8?q?=20to=20=E2=80=9CSoftware=E2=80=9D=20in=20admin=20dashboard=20(?= =?UTF-8?q?#26652)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dimension/software_versions_dimension.rb | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/lib/admin/metrics/dimension/software_versions_dimension.rb b/app/lib/admin/metrics/dimension/software_versions_dimension.rb index 9ab3776c9..72a98a88a 100644 --- a/app/lib/admin/metrics/dimension/software_versions_dimension.rb +++ b/app/lib/admin/metrics/dimension/software_versions_dimension.rb @@ -10,7 +10,7 @@ class Admin::Metrics::Dimension::SoftwareVersionsDimension < Admin::Metrics::Dim protected def perform_query - [mastodon_version, ruby_version, postgresql_version, redis_version] + [mastodon_version, ruby_version, postgresql_version, redis_version, elasticsearch_version].compact end def mastodon_version @@ -57,6 +57,22 @@ class Admin::Metrics::Dimension::SoftwareVersionsDimension < Admin::Metrics::Dim } end + def elasticsearch_version + return unless Chewy.enabled? + + client_info = Chewy.client.info + version = client_info.dig('version', 'number') + + { + key: 'elasticsearch', + human_key: client_info.dig('version', 'distribution') == 'opensearch' ? 'OpenSearch' : 'Elasticsearch', + value: version, + human_value: version, + } + rescue Faraday::ConnectionFailed, Elasticsearch::Transport::Transport::Error + nil + end + def redis_info @redis_info ||= if redis.is_a?(Redis::Namespace) redis.redis.info From 82ec6f162b617e9c46f23ebea1cf27e1e039121c Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 25 Aug 2023 17:01:56 +0200 Subject: [PATCH 07/81] Fix statuses search Elasticsearch query (#26657) --- app/services/statuses_search_service.rb | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/app/services/statuses_search_service.rb b/app/services/statuses_search_service.rb index 21d6b71b7..2a4e533cf 100644 --- a/app/services/statuses_search_service.rb +++ b/app/services/statuses_search_service.rb @@ -15,7 +15,7 @@ class StatusesSearchService < BaseService def status_search_results definition = parsed_query.apply( - StatusesIndex.filter( + Chewy::Search::Request.new(StatusesIndex, PublicStatusesIndex).filter( bool: { should: [ publicly_searchable, @@ -27,9 +27,6 @@ class StatusesSearchService < BaseService ) ) - # This is the best way to submit identical queries to multi-indexes though chewy - definition.instance_variable_get(:@parameters)[:indices].value[:indices] << PublicStatusesIndex - results = definition.collapse(field: :id).order(_id: { order: :desc }).limit(@limit).offset(@offset).objects.compact account_ids = results.map(&:account_id) account_domains = results.map(&:account_domain) @@ -42,13 +39,7 @@ class StatusesSearchService < BaseService def publicly_searchable { - bool: { - must_not: { - exists: { - field: 'searchable_by', - }, - }, - }, + term: { _index: PublicStatusesIndex.index_name }, } end @@ -57,9 +48,7 @@ class StatusesSearchService < BaseService bool: { must: [ { - exists: { - field: 'searchable_by', - }, + term: { _index: StatusesIndex.index_name }, }, { term: { searchable_by: @account.id }, From e4c0ce18a32cfc8a911a4fcd3fd0b310755f609c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 25 Aug 2023 17:02:33 +0200 Subject: [PATCH 08/81] Update dependency immutable to v4.3.4 (#26655) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6894f4388..a630a752e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6686,9 +6686,9 @@ immutable@^3.8.2: integrity sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg== immutable@^4.0.0, immutable@^4.0.0-rc.1, immutable@^4.3.0: - version "4.3.3" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.3.tgz#8934ff6826d996a7642c8dc4b46e694dd19561e3" - integrity sha512-808ZFYMsIRAjLAu5xkKo0TsbY9LBy9H5MazTKIEHerNkg0ymgilGfBPMR/3G7d/ihGmuK2Hw8S1izY2d3kd3wA== + version "4.3.4" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.4.tgz#2e07b33837b4bb7662f288c244d1ced1ef65a78f" + integrity sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA== import-fresh@^3.2.1: version "3.3.0" From 8e8747c564ce3f06a96fc031cd0da9297d631fe0 Mon Sep 17 00:00:00 2001 From: jsgoldstein Date: Fri, 25 Aug 2023 12:16:08 -0400 Subject: [PATCH 09/81] Update ordering to use `id` from body of document instead of deprecated `_id` (#26659) --- app/services/statuses_search_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/statuses_search_service.rb b/app/services/statuses_search_service.rb index 2a4e533cf..0d0de2a9d 100644 --- a/app/services/statuses_search_service.rb +++ b/app/services/statuses_search_service.rb @@ -27,7 +27,7 @@ class StatusesSearchService < BaseService ) ) - results = definition.collapse(field: :id).order(_id: { order: :desc }).limit(@limit).offset(@offset).objects.compact + results = definition.collapse(field: :id).order(id: { order: :desc }).limit(@limit).offset(@offset).objects.compact account_ids = results.map(&:account_id) account_domains = results.map(&:account_domain) preloaded_relations = @account.relations_map(account_ids, account_domains) From 072112867b9a3ec090ad2c92d6363b47b2265d74 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 25 Aug 2023 18:17:22 +0200 Subject: [PATCH 10/81] Fix dashboard check for Elasticsearch suggested command including incorrect names (#26658) --- app/lib/admin/system_check/elasticsearch_check.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/admin/system_check/elasticsearch_check.rb b/app/lib/admin/system_check/elasticsearch_check.rb index 2092ef0dd..c0a1a21e8 100644 --- a/app/lib/admin/system_check/elasticsearch_check.rb +++ b/app/lib/admin/system_check/elasticsearch_check.rb @@ -86,7 +86,7 @@ class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck def mismatched_indexes @mismatched_indexes ||= INDEXES.filter_map do |klass| - klass.index_name if Chewy.client.indices.get_mapping[klass.index_name]&.deep_symbolize_keys != klass.mappings_hash + klass.base_name if Chewy.client.indices.get_mapping[klass.index_name]&.deep_symbolize_keys != klass.mappings_hash end end From f39847476cbc24af6daf755dd51be885726cb631 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 25 Aug 2023 18:26:44 +0200 Subject: [PATCH 11/81] Change the pre-release versioning scheme and associated environment variables (#26653) --- .github/workflows/build-container-image.yml | 6 ++++-- .github/workflows/build-nightly.yml | 9 ++++----- .github/workflows/build-push-pr.yml | 6 +++--- Dockerfile | 8 ++++---- lib/mastodon/version.rb | 17 ++++++++++++----- 5 files changed, 27 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build-container-image.yml b/.github/workflows/build-container-image.yml index 1b15d1988..8e9c74766 100644 --- a/.github/workflows/build-container-image.yml +++ b/.github/workflows/build-container-image.yml @@ -8,7 +8,9 @@ on: type: boolean push_to_images: type: string - version_suffix: + version_prerelease: + type: string + version_metadata: type: string flavor: type: string @@ -83,7 +85,7 @@ jobs: - uses: docker/build-push-action@v4 with: context: . - build-args: MASTODON_VERSION_SUFFIX=${{ inputs.version_suffix }} + build-args: MASTODON_VERSION_PRERELEASE=${{ inputs.version_prerelease }} MASTODON_VERSION_METADATA=${{ inputs.version_metadata }} platforms: ${{ inputs.platforms }} provenance: false builder: ${{ steps.buildx.outputs.name || steps.buildx-native.outputs.name }} diff --git a/.github/workflows/build-nightly.yml b/.github/workflows/build-nightly.yml index 5b34c1c3c..5feb2ade3 100644 --- a/.github/workflows/build-nightly.yml +++ b/.github/workflows/build-nightly.yml @@ -16,9 +16,9 @@ jobs: env: TZ: Etc/UTC run: | - echo mastodon_version_suffix=nightly.$(date +'%Y-%m-%d')>> $GITHUB_OUTPUT + echo mastodon_version_prerelease=nightly.$(date +'%Y-%m-%d')>> $GITHUB_OUTPUT outputs: - suffix: ${{ steps.version_vars.outputs.mastodon_version_suffix }} + prerelease: ${{ steps.version_vars.outputs.mastodon_version_prerelease }} build-image: needs: compute-suffix @@ -29,8 +29,7 @@ jobs: push_to_images: | tootsuite/mastodon ghcr.io/mastodon/mastodon - # The `-` is important here, result will be v4.1.2-nightly.2022-03-05 - version_suffix: -${{ needs.compute-suffix.outputs.suffix }} + version_prerelease: ${{ needs.compute-suffix.outputs.prerelease }} labels: | org.opencontainers.image.description=Nightly build image used for testing purposes flavor: | @@ -38,5 +37,5 @@ jobs: tags: | type=raw,value=edge type=raw,value=nightly - type=schedule,pattern=${{ needs.compute-suffix.outputs.suffix }} + type=schedule,pattern=${{ needs.compute-suffix.outputs.prerelease }} secrets: inherit diff --git a/.github/workflows/build-push-pr.yml b/.github/workflows/build-push-pr.yml index 2d2026128..b95e3c14e 100644 --- a/.github/workflows/build-push-pr.yml +++ b/.github/workflows/build-push-pr.yml @@ -21,9 +21,9 @@ jobs: uses: actions/checkout@v3 - id: version_vars run: | - echo mastodon_version_suffix=+pr-${{ github.event.pull_request.number }}-$(git rev-parse --short HEAD) >> $GITHUB_OUTPUT + echo mastodon_version_metadata=pr-${{ github.event.pull_request.number }}-$(git rev-parse --short HEAD) >> $GITHUB_OUTPUT outputs: - suffix: ${{ steps.version_vars.outputs.mastodon_version_suffix }} + metadata: ${{ steps.version_vars.outputs.mastodon_version_metadata }} build-image: needs: compute-suffix @@ -33,7 +33,7 @@ jobs: use_native_arm64_builder: true push_to_images: | ghcr.io/mastodon/mastodon - version_suffix: ${{ needs.compute-suffix.outputs.suffix }} + version_metadata: ${{ needs.compute-suffix.outputs.metadata }} flavor: | latest=auto tags: | diff --git a/Dockerfile b/Dockerfile index cb5096581..cdabc4c7c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,8 +42,8 @@ RUN apt-get update && \ FROM node:${NODE_VERSION} # Use those args to specify your own version flags & suffixes -ARG MASTODON_VERSION_FLAGS="" -ARG MASTODON_VERSION_SUFFIX="" +ARG MASTODON_VERSION_PRERELEASE="" +ARG MASTODON_VERSION_METADATA="" ARG UID="991" ARG GID="991" @@ -89,8 +89,8 @@ ENV RAILS_ENV="production" \ NODE_ENV="production" \ RAILS_SERVE_STATIC_FILES="true" \ BIND="0.0.0.0" \ - MASTODON_VERSION_FLAGS="${MASTODON_VERSION_FLAGS}" \ - MASTODON_VERSION_SUFFIX="${MASTODON_VERSION_SUFFIX}" + MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \ + MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" # Set the run user USER mastodon diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index e3347ca17..c542d5d49 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -16,12 +16,16 @@ module Mastodon 0 end - def flags - ENV['MASTODON_VERSION_FLAGS'].presence || '-beta2' + def default_prerelease + 'beta2' end - def suffix - ENV.fetch('MASTODON_VERSION_SUFFIX', '') + def prerelease + ENV['MASTODON_VERSION_PRERELEASE'].presence || default_prerelease + end + + def build_metadata + ENV.fetch('MASTODON_VERSION_METADATA', nil) end def to_a @@ -29,7 +33,10 @@ module Mastodon end def to_s - [to_a.join('.'), flags, suffix].join + components = [to_a.join('.')] + components << "-#{prerelease}" if prerelease.present? + components << "+#{build_metadata}" if build_metadata.present? + components.join end def repository From 71641766f2ca6555fe19b309e9bd9f2455575bcc Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 25 Aug 2023 22:03:04 +0200 Subject: [PATCH 12/81] Fix toast saying "published" instead of "saved" after editing post in web UI (#26664) --- app/javascript/mastodon/actions/compose.js | 3 ++- app/javascript/mastodon/locales/en.json | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 260fb43f0..6abfd6157 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -84,6 +84,7 @@ const messages = defineMessages({ uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' }, open: { id: 'compose.published.open', defaultMessage: 'Open' }, published: { id: 'compose.published.body', defaultMessage: 'Post published.' }, + saved: { id: 'compose.saved.body', defaultMessage: 'Post saved.' }, }); export const ensureComposeIsVisible = (getState, routerHistory) => { @@ -244,7 +245,7 @@ export function submitCompose(routerHistory) { } dispatch(showAlert({ - message: messages.published, + message: statusId === null ? messages.published : messages.saved, action: messages.open, dismissAfter: 10000, onClick: () => routerHistory.push(`/@${response.data.account.username}/${response.data.id}`), diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index d4cd8c794..5eeaf8044 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -137,6 +137,7 @@ "compose.language.search": "Search languages...", "compose.published.body": "Post published.", "compose.published.open": "Open", + "compose.saved.body": "Post saved.", "compose_form.direct_message_warning_learn_more": "Learn more", "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any sensitive information over Mastodon.", "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is not public. Only public posts can be searched by hashtag.", From 925c16adea49adefdb202c19e8a7eaf439e1dada Mon Sep 17 00:00:00 2001 From: Claire Date: Sat, 26 Aug 2023 17:34:36 +0200 Subject: [PATCH 13/81] Fix nightly build version (#26676) --- .github/workflows/build-container-image.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-container-image.yml b/.github/workflows/build-container-image.yml index 8e9c74766..897bb9caa 100644 --- a/.github/workflows/build-container-image.yml +++ b/.github/workflows/build-container-image.yml @@ -85,7 +85,9 @@ jobs: - uses: docker/build-push-action@v4 with: context: . - build-args: MASTODON_VERSION_PRERELEASE=${{ inputs.version_prerelease }} MASTODON_VERSION_METADATA=${{ inputs.version_metadata }} + build-args: | + MASTODON_VERSION_PRERELEASE=${{ inputs.version_prerelease }} + MASTODON_VERSION_METADATA=${{ inputs.version_metadata }} platforms: ${{ inputs.platforms }} provenance: false builder: ${{ steps.buildx.outputs.name || steps.buildx-native.outputs.name }} From f8d2fea2e6f6eb38c62e9a484980fa5696dfa2a4 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 27 Aug 2023 18:58:20 +0200 Subject: [PATCH 14/81] Change queue of job when opting into search from `default` to `pull` (#26688) --- app/workers/add_to_public_statuses_index_worker.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/workers/add_to_public_statuses_index_worker.rb b/app/workers/add_to_public_statuses_index_worker.rb index 409e5e708..33529a4c1 100644 --- a/app/workers/add_to_public_statuses_index_worker.rb +++ b/app/workers/add_to_public_statuses_index_worker.rb @@ -3,6 +3,8 @@ class AddToPublicStatusesIndexWorker include Sidekiq::Worker + sidekiq_options queue: 'pull' + def perform(account_id) account = Account.find(account_id) From 5694e24bbf9afccbbf1b44d3a405a4e2bc0ff08d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 27 Aug 2023 22:37:35 +0200 Subject: [PATCH 15/81] Fix unnecessary condition causing seqscan when indexing (#26689) --- app/models/concerns/account_statuses_search.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/concerns/account_statuses_search.rb b/app/models/concerns/account_statuses_search.rb index 563a87105..626bf3890 100644 --- a/app/models/concerns/account_statuses_search.rb +++ b/app/models/concerns/account_statuses_search.rb @@ -31,7 +31,7 @@ module AccountStatusesSearch def add_to_public_statuses_index! return unless Chewy.enabled? - statuses.indexable.find_in_batches do |batch| + statuses.without_reblogs.where(visibility: :public).find_in_batches do |batch| PublicStatusesIndex.import(query: batch) end end From e263db276fdc05dab743400977606efe98f1e949 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 27 Aug 2023 22:38:01 +0200 Subject: [PATCH 16/81] Change indexing jobs to use database replica (#26692) --- app/workers/add_to_public_statuses_index_worker.rb | 11 ++++++++--- app/workers/scheduler/indexing_scheduler.rb | 6 +++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/workers/add_to_public_statuses_index_worker.rb b/app/workers/add_to_public_statuses_index_worker.rb index 33529a4c1..80d921eab 100644 --- a/app/workers/add_to_public_statuses_index_worker.rb +++ b/app/workers/add_to_public_statuses_index_worker.rb @@ -2,15 +2,20 @@ class AddToPublicStatusesIndexWorker include Sidekiq::Worker + include DatabaseHelper sidekiq_options queue: 'pull' def perform(account_id) - account = Account.find(account_id) + with_primary do + @account = Account.find(account_id) + end - return unless account.indexable? + return unless @account.indexable? - account.add_to_public_statuses_index! + with_read_replica do + @account.add_to_public_statuses_index! + end rescue ActiveRecord::RecordNotFound true end diff --git a/app/workers/scheduler/indexing_scheduler.rb b/app/workers/scheduler/indexing_scheduler.rb index 6c770d5a8..1b09730c7 100644 --- a/app/workers/scheduler/indexing_scheduler.rb +++ b/app/workers/scheduler/indexing_scheduler.rb @@ -3,6 +3,7 @@ class Scheduler::IndexingScheduler include Sidekiq::Worker include Redisable + include DatabaseHelper sidekiq_options retry: 0, lock: :until_executed, lock_ttl: 1.day.to_i @@ -15,7 +16,10 @@ class Scheduler::IndexingScheduler indexes.each do |type| with_redis do |redis| redis.sscan_each("chewy:queue:#{type.name}", count: SCAN_BATCH_SIZE).each_slice(IMPORT_BATCH_SIZE) do |ids| - type.import!(ids) + with_read_replica do + type.import!(ids) + end + redis.srem("chewy:queue:#{type.name}", ids) end end From fc14d1f3b02411510467928df56318554c62c11d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 10:20:02 +0200 Subject: [PATCH 17/81] Update eslint (non-major) (#26694) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 142 +++++++++++++++++++++++++++++------------------------- 1 file changed, 77 insertions(+), 65 deletions(-) diff --git a/yarn.lock b/yarn.lock index a630a752e..880421829 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1287,9 +1287,9 @@ eslint-visitor-keys "^3.3.0" "@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": - version "4.6.2" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.6.2.tgz#1816b5f6948029c5eaacb0703b850ee0cb37d8f8" - integrity sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw== + version "4.8.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.8.0.tgz#11195513186f68d42fbf449f9a7136b2c0c92005" + integrity sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg== "@eslint/eslintrc@^2.1.2": version "2.1.2" @@ -1306,10 +1306,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@^8.47.0": - version "8.47.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.47.0.tgz#5478fdf443ff8158f9de171c704ae45308696c7d" - integrity sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og== +"@eslint/js@8.48.0": + version "8.48.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.48.0.tgz#642633964e217905436033a2bd08bf322849b7fb" + integrity sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw== "@floating-ui/core@^1.3.1": version "1.3.1" @@ -2608,15 +2608,15 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^6.0.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.4.0.tgz#53428b616f7d80fe879f45a08f11cc0f0b62cf13" - integrity sha512-62o2Hmc7Gs3p8SLfbXcipjWAa6qk2wZGChXG2JbBtYpwSRmti/9KHLqfbLs9uDigOexG+3PaQ9G2g3201FWLKg== + version "6.4.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.4.1.tgz#bc0c6f000134b53c304ad0bec4ee4753cd3e89d2" + integrity sha512-3F5PtBzUW0dYlq77Lcqo13fv+58KDwUib3BddilE8ajPJT+faGgxmI9Sw+I8ZS22BYwoir9ZhNXcLi+S+I2bkw== dependencies: "@eslint-community/regexpp" "^4.5.1" - "@typescript-eslint/scope-manager" "6.4.0" - "@typescript-eslint/type-utils" "6.4.0" - "@typescript-eslint/utils" "6.4.0" - "@typescript-eslint/visitor-keys" "6.4.0" + "@typescript-eslint/scope-manager" "6.4.1" + "@typescript-eslint/type-utils" "6.4.1" + "@typescript-eslint/utils" "6.4.1" + "@typescript-eslint/visitor-keys" "6.4.1" debug "^4.3.4" graphemer "^1.4.0" ignore "^5.2.4" @@ -2625,31 +2625,31 @@ ts-api-utils "^1.0.1" "@typescript-eslint/parser@^6.0.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.4.0.tgz#47e7c6e22ff1248e8675d95f488890484de67600" - integrity sha512-I1Ah1irl033uxjxO9Xql7+biL3YD7w9IU8zF+xlzD/YxY6a4b7DYA08PXUUCbm2sEljwJF6ERFy2kTGAGcNilg== + version "6.4.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.4.1.tgz#85ad550bf4ac4aa227504f1becb828f8e46c44e3" + integrity sha512-610G6KHymg9V7EqOaNBMtD1GgpAmGROsmfHJPXNLCU9bfIuLrkdOygltK784F6Crboyd5tBFayPB7Sf0McrQwg== dependencies: - "@typescript-eslint/scope-manager" "6.4.0" - "@typescript-eslint/types" "6.4.0" - "@typescript-eslint/typescript-estree" "6.4.0" - "@typescript-eslint/visitor-keys" "6.4.0" + "@typescript-eslint/scope-manager" "6.4.1" + "@typescript-eslint/types" "6.4.1" + "@typescript-eslint/typescript-estree" "6.4.1" + "@typescript-eslint/visitor-keys" "6.4.1" debug "^4.3.4" -"@typescript-eslint/scope-manager@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz#3048e4262ba3eafa4e2e69b08912d9037ec646ae" - integrity sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig== +"@typescript-eslint/scope-manager@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.4.1.tgz#4b073a30be2dbe603e44e9ae0cff7e1d3ed19278" + integrity sha512-p/OavqOQfm4/Hdrr7kvacOSFjwQ2rrDVJRPxt/o0TOWdFnjJptnjnZ+sYDR7fi4OimvIuKp+2LCkc+rt9fIW+A== dependencies: - "@typescript-eslint/types" "6.4.0" - "@typescript-eslint/visitor-keys" "6.4.0" + "@typescript-eslint/types" "6.4.1" + "@typescript-eslint/visitor-keys" "6.4.1" -"@typescript-eslint/type-utils@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.4.0.tgz#c8ac92716ed6a9d5443aa3e342910355b0796ba0" - integrity sha512-TvqrUFFyGY0cX3WgDHcdl2/mMCWCDv/0thTtx/ODMY1QhEiyFtv/OlLaNIiYLwRpAxAtOLOY9SUf1H3Q3dlwAg== +"@typescript-eslint/type-utils@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.4.1.tgz#fa21cb13016c8d6f352fe9b2d6c9ab6edc2d1857" + integrity sha512-7ON8M8NXh73SGZ5XvIqWHjgX2f+vvaOarNliGhjrJnv1vdjG0LVIz+ToYfPirOoBi56jxAKLfsLm40+RvxVVXA== dependencies: - "@typescript-eslint/typescript-estree" "6.4.0" - "@typescript-eslint/utils" "6.4.0" + "@typescript-eslint/typescript-estree" "6.4.1" + "@typescript-eslint/utils" "6.4.1" debug "^4.3.4" ts-api-utils "^1.0.1" @@ -2658,10 +2658,10 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.0.tgz#3fcdac7dbf923ec5251545acdd9f1d42d7c4fe32" integrity sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA== -"@typescript-eslint/types@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.4.0.tgz#5b109a59a805f0d8d375895e42d9e5f0037f66ee" - integrity sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg== +"@typescript-eslint/types@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.4.1.tgz#b2c61159f46dda210fed9f117f5d027f65bb5c3b" + integrity sha512-zAAopbNuYu++ijY1GV2ylCsQsi3B8QvfPHVqhGdDcbx/NK5lkqMnCGU53amAjccSpk+LfeONxwzUhDzArSfZJg== "@typescript-eslint/typescript-estree@5.59.0": version "5.59.0" @@ -2676,30 +2676,30 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz#3c58d20632db93fec3d6ab902acbedf593d37276" - integrity sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA== +"@typescript-eslint/typescript-estree@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.1.tgz#91ff88101c710adb0f70a317f2f65efa9441da45" + integrity sha512-xF6Y7SatVE/OyV93h1xGgfOkHr2iXuo8ip0gbfzaKeGGuKiAnzS+HtVhSPx8Www243bwlW8IF7X0/B62SzFftg== dependencies: - "@typescript-eslint/types" "6.4.0" - "@typescript-eslint/visitor-keys" "6.4.0" + "@typescript-eslint/types" "6.4.1" + "@typescript-eslint/visitor-keys" "6.4.1" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/utils@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.4.0.tgz#23e996b693603c5924b1fbb733cc73196256baa5" - integrity sha512-BvvwryBQpECPGo8PwF/y/q+yacg8Hn/2XS+DqL/oRsOPK+RPt29h5Ui5dqOKHDlbXrAeHUTnyG3wZA0KTDxRZw== +"@typescript-eslint/utils@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.4.1.tgz#81bf62ff0c3119a26c19fab683582e29450717bc" + integrity sha512-F/6r2RieNeorU0zhqZNv89s9bDZSovv3bZQpUNOmmQK1L80/cV4KEu95YUJWi75u5PhboFoKUJBnZ4FQcoqhDw== dependencies: "@eslint-community/eslint-utils" "^4.4.0" "@types/json-schema" "^7.0.12" "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "6.4.0" - "@typescript-eslint/types" "6.4.0" - "@typescript-eslint/typescript-estree" "6.4.0" + "@typescript-eslint/scope-manager" "6.4.1" + "@typescript-eslint/types" "6.4.1" + "@typescript-eslint/typescript-estree" "6.4.1" semver "^7.5.4" "@typescript-eslint/visitor-keys@5.59.0": @@ -2710,12 +2710,12 @@ "@typescript-eslint/types" "5.59.0" eslint-visitor-keys "^3.3.0" -"@typescript-eslint/visitor-keys@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz#96a426cdb1add28274abd7a34aefe27f8b7d51ef" - integrity sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA== +"@typescript-eslint/visitor-keys@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.1.tgz#e3ccf7b8d42e625946ac5094ed92a405fb4115e0" + integrity sha512-y/TyRJsbZPkJIZQXrHfdnxVnxyKegnpEvnRGNam7s3TRR2ykGefEWOhaef00/UUN3IZxizS7BTO3svd3lCOJRQ== dependencies: - "@typescript-eslint/types" "6.4.0" + "@typescript-eslint/types" "6.4.1" eslint-visitor-keys "^3.4.1" "@webassemblyjs/ast@1.9.0": @@ -5528,14 +5528,14 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== eslint@^8.41.0: - version "8.47.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.47.0.tgz#c95f9b935463fb4fad7005e626c7621052e90806" - integrity sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q== + version "8.48.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.48.0.tgz#bf9998ba520063907ba7bfe4c480dc8be03c2155" + integrity sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.6.1" "@eslint/eslintrc" "^2.1.2" - "@eslint/js" "^8.47.0" + "@eslint/js" "8.48.0" "@humanwhocodes/config-array" "^0.11.10" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -5990,14 +5990,15 @@ findup-sync@^3.0.0: resolve-dir "^1.0.1" flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + version "3.1.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.1.0.tgz#0e54ab4a1a60fe87e2946b6b00657f1c99e1af3f" + integrity sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew== dependencies: - flatted "^3.1.0" + flatted "^3.2.7" + keyv "^4.5.3" rimraf "^3.0.2" -flatted@^3.1.0: +flatted@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== @@ -7843,6 +7844,11 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -7934,6 +7940,13 @@ keycode@^2.1.7: resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.1.tgz#09c23b2be0611d26117ea2501c2c391a01f39eff" integrity sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg== +keyv@^4.5.3: + version "4.5.3" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.3.tgz#00873d2b046df737963157bd04f294ca818c9c25" + integrity sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug== + dependencies: + json-buffer "3.0.1" + killable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" @@ -11329,7 +11342,6 @@ stringz@^2.1.0: char-regex "^1.0.2" "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: - name strip-ansi-cjs version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== From 822a35b9d577539a63984ca81d6668ca3b8435fd Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Aug 2023 10:31:51 +0200 Subject: [PATCH 18/81] Fix not being able to negate prefix clauses in search (#26672) --- app/lib/search_query_parser.rb | 2 +- app/lib/search_query_transformer.rb | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/lib/search_query_parser.rb b/app/lib/search_query_parser.rb index 15956d4cf..5d6ffbf29 100644 --- a/app/lib/search_query_parser.rb +++ b/app/lib/search_query_parser.rb @@ -9,7 +9,7 @@ class SearchQueryParser < Parslet::Parser rule(:prefix) { (term >> colon).as(:prefix) } rule(:shortcode) { (colon >> term >> colon.maybe).as(:shortcode) } rule(:phrase) { (quote >> (term >> space.maybe).repeat >> quote).as(:phrase) } - rule(:clause) { (prefix.maybe >> operator.maybe >> (phrase | term | shortcode)).as(:clause) } + rule(:clause) { (operator.maybe >> prefix.maybe >> (phrase | term | shortcode)).as(:clause) } rule(:query) { (clause >> space.maybe).repeat.as(:query) } root(:query) end diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index dad99cbd2..915f9c331 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -36,7 +36,11 @@ class SearchQueryTransformer < Parslet::Transform def clause_to_filter(clause) case clause when PrefixClause - { clause.type => { clause.filter => clause.term } } + if clause.negated? + { bool: { must_not: { clause.type => { clause.filter => clause.term } } } } + else + { clause.type => { clause.filter => clause.term } } + end else raise "Unexpected clause type: #{clause}" end @@ -81,7 +85,8 @@ class SearchQueryTransformer < Parslet::Transform class PrefixClause attr_reader :type, :filter, :operator, :term - def initialize(prefix, term) + def initialize(prefix, operator, term) + @negated = operator == '-' @operator = :filter case prefix @@ -114,6 +119,10 @@ class SearchQueryTransformer < Parslet::Transform end end + def negated? + @negated + end + private def account_id_from_term(term) @@ -132,7 +141,7 @@ class SearchQueryTransformer < Parslet::Transform operator = clause[:operator]&.to_s if clause[:prefix] - PrefixClause.new(prefix, clause[:term].to_s) + PrefixClause.new(prefix, operator, clause[:term].to_s) elsif clause[:term] TermClause.new(prefix, operator, clause[:term].to_s) elsif clause[:shortcode] From b42fe5e3382ef1415748030ed7842095dcefa6a9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 10:32:13 +0200 Subject: [PATCH 19/81] Update dependency haml_lint to v0.50.0 (#26665) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f801b1b56..7a68249c2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -324,7 +324,7 @@ GEM ruby-progressbar (~> 1.4) globalid (1.1.0) activesupport (>= 5.0) - haml (6.1.1) + haml (6.1.2) temple (>= 0.8.2) thor tilt @@ -333,7 +333,7 @@ GEM activesupport (>= 5.1) haml (>= 4.0.6) railties (>= 5.1) - haml_lint (0.49.3) + haml_lint (0.50.0) haml (>= 4.0, < 6.2) parallel (~> 1.10) rainbow From 4ea5db90da2d0fd0e888ea6c9f777835874c4302 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 11:04:06 +0200 Subject: [PATCH 20/81] Update DefinitelyTyped types (non-major) (#26693) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 135 +++++++++++++++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 62 deletions(-) diff --git a/yarn.lock b/yarn.lock index 880421829..4a6d642ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1538,12 +1538,12 @@ "@types/node" "*" jest-mock "^29.6.2" -"@jest/expect-utils@^29.6.2": - version "29.6.2" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.6.2.tgz#1b97f290d0185d264dd9fdec7567a14a38a90534" - integrity sha512-6zIhM8go3RV2IG4aIZaZbxwpOzz3ZiM23oxAlkquOIole+G6TrbeXnykxWYlqF7kz2HlBjdKtca20x9atkEQYg== +"@jest/expect-utils@^29.6.2", "@jest/expect-utils@^29.6.4": + version "29.6.4" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.6.4.tgz#17c7dfe6cec106441f218b0aff4b295f98346679" + integrity sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg== dependencies: - jest-get-type "^29.4.3" + jest-get-type "^29.6.3" "@jest/expect@^29.6.2": version "29.6.2" @@ -1605,10 +1605,10 @@ strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^29.6.0": - version "29.6.0" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040" - integrity sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ== +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== dependencies: "@sinclair/typebox" "^0.27.8" @@ -1662,12 +1662,12 @@ slash "^3.0.0" write-file-atomic "^4.0.2" -"@jest/types@^29.6.1": - version "29.6.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.1.tgz#ae79080278acff0a6af5eb49d063385aaa897bf2" - integrity sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw== +"@jest/types@^29.6.1", "@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== dependencies: - "@jest/schemas" "^29.6.0" + "@jest/schemas" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -2226,9 +2226,9 @@ "@types/istanbul-lib-report" "*" "@types/jest@*", "@types/jest@^29.5.2": - version "29.5.3" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.3.tgz#7a35dc0044ffb8b56325c6802a4781a626b05777" - integrity sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA== + version "29.5.4" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.4.tgz#9d0a16edaa009a71e6a71a999acd582514dab566" + integrity sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -2288,9 +2288,9 @@ integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== "@types/node@*": - version "20.4.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.9.tgz#c7164e0f8d3f12dfae336af0b1f7fdec8c6b204f" - integrity sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ== + version "20.5.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.5.7.tgz#4b8ecac87fbefbc92f431d09c30e176fc0a7c377" + integrity sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA== "@types/node@14 || 16 || 17": version "17.0.45" @@ -2466,9 +2466,9 @@ "@types/react" "*" "@types/react@*", "@types/react@16 || 17 || 18", "@types/react@>=16.9.11", "@types/react@^18.0.26", "@types/react@^18.2.7": - version "18.2.20" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.20.tgz#1605557a83df5c8a2cc4eeb743b3dfc0eb6aaeb2" - integrity sha512-WKNtmsLWJM/3D5mG4U84cysVY31ivmyw85dE84fOCk5Hx78wezB/XEjVPWl2JTZ5FkEeaTJf+VgUAUn3PE7Isw== + version "18.2.21" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.21.tgz#774c37fd01b522d0b91aed04811b58e4e0514ed9" + integrity sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -4922,10 +4922,10 @@ detect-passive-events@^2.0.3: dependencies: detect-it "^4.0.1" -diff-sequences@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" - integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== diffie-hellman@^5.0.0: version "5.0.3" @@ -5734,7 +5734,18 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -expect@^29.0.0, expect@^29.6.2: +expect@^29.0.0: + version "29.6.4" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.4.tgz#a6e6f66d4613717859b2fe3da98a739437b6f4b8" + integrity sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA== + dependencies: + "@jest/expect-utils" "^29.6.4" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.6.4" + jest-message-util "^29.6.3" + jest-util "^29.6.3" + +expect@^29.6.2: version "29.6.2" resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.2.tgz#7b08e83eba18ddc4a2cf62b5f2d1918f5cd84521" integrity sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA== @@ -7431,15 +7442,15 @@ jest-config@^29.6.2: slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.2.tgz#c36001e5543e82a0805051d3ceac32e6825c1c46" - integrity sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA== +jest-diff@^29.6.2, jest-diff@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.4.tgz#85aaa6c92a79ae8cd9a54ebae8d5b6d9a513314a" + integrity sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw== dependencies: chalk "^4.0.0" - diff-sequences "^29.4.3" - jest-get-type "^29.4.3" - pretty-format "^29.6.2" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.6.3" jest-docblock@^29.4.3: version "29.4.3" @@ -7485,10 +7496,10 @@ jest-environment-node@^29.6.2: jest-mock "^29.6.2" jest-util "^29.6.2" -jest-get-type@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" - integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== +jest-get-type@^29.4.3, jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== jest-haste-map@^29.6.2: version "29.6.2" @@ -7517,28 +7528,28 @@ jest-leak-detector@^29.6.2: jest-get-type "^29.4.3" pretty-format "^29.6.2" -jest-matcher-utils@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.6.2.tgz#39de0be2baca7a64eacb27291f0bd834fea3a535" - integrity sha512-4LiAk3hSSobtomeIAzFTe+N8kL6z0JtF3n6I4fg29iIW7tt99R7ZcIFW34QkX+DuVrf+CUe6wuVOpm7ZKFJzZQ== +jest-matcher-utils@^29.6.2, jest-matcher-utils@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz#327db7ababea49455df3b23e5d6109fe0c709d24" + integrity sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ== dependencies: chalk "^4.0.0" - jest-diff "^29.6.2" - jest-get-type "^29.4.3" - pretty-format "^29.6.2" + jest-diff "^29.6.4" + jest-get-type "^29.6.3" + pretty-format "^29.6.3" -jest-message-util@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.6.2.tgz#af7adc2209c552f3f5ae31e77cf0a261f23dc2bb" - integrity sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ== +jest-message-util@^29.6.2, jest-message-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.6.3.tgz#bce16050d86801b165f20cfde34dc01d3cf85fbf" + integrity sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.6.1" + "@jest/types" "^29.6.3" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^29.6.2" + pretty-format "^29.6.3" slash "^3.0.0" stack-utils "^2.0.3" @@ -7665,12 +7676,12 @@ jest-snapshot@^29.6.2: pretty-format "^29.6.2" semver "^7.5.3" -jest-util@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.2.tgz#8a052df8fff2eebe446769fd88814521a517664d" - integrity sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w== +jest-util@^29.6.2, jest-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.3.tgz#e15c3eac8716440d1ed076f09bc63ace1aebca63" + integrity sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA== dependencies: - "@jest/types" "^29.6.1" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" @@ -9710,12 +9721,12 @@ pretty-format@^27.0.2: ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-format@^29.0.0, pretty-format@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.2.tgz#3d5829261a8a4d89d8b9769064b29c50ed486a47" - integrity sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg== +pretty-format@^29.0.0, pretty-format@^29.6.2, pretty-format@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.3.tgz#d432bb4f1ca6f9463410c3fb25a0ba88e594ace7" + integrity sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw== dependencies: - "@jest/schemas" "^29.6.0" + "@jest/schemas" "^29.6.3" ansi-styles "^5.0.0" react-is "^18.0.0" From 7bd5ebb0c50bb481c85ef81816089a04746f762d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Aug 2023 11:36:17 +0200 Subject: [PATCH 21/81] Fix multiple issues with status index mappings (#26686) --- app/chewy/accounts_index.rb | 9 +++++---- app/chewy/public_statuses_index.rb | 14 ++++++++++---- app/chewy/statuses_index.rb | 14 ++++++++++---- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb index 61e3399aa..8881b08f6 100644 --- a/app/chewy/accounts_index.rb +++ b/app/chewy/accounts_index.rb @@ -21,19 +21,20 @@ class AccountsIndex < Chewy::Index analyzer: { natural: { - tokenizer: 'uax_url_email', + tokenizer: 'standard', filter: %w( - english_possessive_stemmer lowercase asciifolding cjk_width + elision + english_possessive_stemmer english_stop english_stemmer ), }, verbatim: { - tokenizer: 'standard', + tokenizer: 'uax_url_email', filter: %w(lowercase asciifolding cjk_width), }, @@ -62,6 +63,6 @@ class AccountsIndex < Chewy::Index field(:last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at }) field(:display_name, type: 'text', analyzer: 'verbatim') { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' } field(:username, type: 'text', analyzer: 'verbatim', value: ->(account) { [account.username, account.domain].compact.join('@') }) { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' } - field(:text, type: 'text', analyzer: 'whitespace', value: ->(account) { account.searchable_text }) { field :stemmed, type: 'text', analyzer: 'natural' } + field(:text, type: 'text', analyzer: 'verbatim', value: ->(account) { account.searchable_text }) { field :stemmed, type: 'text', analyzer: 'natural' } end end diff --git a/app/chewy/public_statuses_index.rb b/app/chewy/public_statuses_index.rb index 1fad5de3a..5c68a1365 100644 --- a/app/chewy/public_statuses_index.rb +++ b/app/chewy/public_statuses_index.rb @@ -20,13 +20,19 @@ class PublicStatusesIndex < Chewy::Index }, analyzer: { - content: { + verbatim: { tokenizer: 'uax_url_email', + filter: %w(lowercase), + }, + + content: { + tokenizer: 'standard', filter: %w( - english_possessive_stemmer lowercase asciifolding cjk_width + elision + english_possessive_stemmer english_stop english_stemmer ), @@ -40,9 +46,9 @@ class PublicStatusesIndex < Chewy::Index .includes(:media_attachments, :preloadable_poll, :preview_cards) root date_detection: false do - field(:id, type: 'keyword') + field(:id, type: 'long') field(:account_id, type: 'long') - field(:text, type: 'text', analyzer: 'whitespace', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } + field(:text, type: 'text', analyzer: 'verbatim', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } field(:language, type: 'keyword') field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) field(:created_at, type: 'date') diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index 130f8801d..6d3352105 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -20,13 +20,19 @@ class StatusesIndex < Chewy::Index }, analyzer: { - content: { + verbatim: { tokenizer: 'uax_url_email', + filter: %w(lowercase), + }, + + content: { + tokenizer: 'standard', filter: %w( - english_possessive_stemmer lowercase asciifolding cjk_width + elision + english_possessive_stemmer english_stop english_stemmer ), @@ -64,9 +70,9 @@ class StatusesIndex < Chewy::Index end root date_detection: false do - field(:id, type: 'keyword') + field(:id, type: 'long') field(:account_id, type: 'long') - field(:text, type: 'text', analyzer: 'whitespace', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } + field(:text, type: 'text', analyzer: 'verbatim', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } field(:searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) }) field(:language, type: 'keyword') field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) From 9d9de8d219b7545826e9fb70f84aa65a0409d138 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 12:19:42 +0200 Subject: [PATCH 22/81] Update dependency @testing-library/jest-dom to v6 (#26479) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Renaud Chaput --- app/javascript/mastodon/test_setup.js | 2 +- package.json | 2 +- yarn.lock | 25 +++++++++---------------- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/app/javascript/mastodon/test_setup.js b/app/javascript/mastodon/test_setup.js index 666127af3..7b0828bfa 100644 --- a/app/javascript/mastodon/test_setup.js +++ b/app/javascript/mastodon/test_setup.js @@ -1 +1 @@ -import '@testing-library/jest-dom/extend-expect'; +import '@testing-library/jest-dom'; diff --git a/package.json b/package.json index 16c5d529a..403883105 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,7 @@ }, "devDependencies": { "@formatjs/cli": "^6.1.1", - "@testing-library/jest-dom": "^5.16.5", + "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^14.0.0", "@types/babel__core": "^7.20.1", "@types/emoji-mart": "^3.0.9", diff --git a/yarn.lock b/yarn.lock index 4a6d642ce..3e9c9abf7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2022,14 +2022,13 @@ lz-string "^1.5.0" pretty-format "^27.0.2" -"@testing-library/jest-dom@^5.16.5": - version "5.17.0" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz#5e97c8f9a15ccf4656da00fecab505728de81e0c" - integrity sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg== +"@testing-library/jest-dom@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.0.0.tgz#d2ba5a3fd13724d5966b3f8cd24d2cedcab4fa76" + integrity sha512-Ye2R3+/oM27jir8CzYPmuWdavTaKwNZcu0d22L9pO/vnOYE0wmrtpw79TQJa8H6gV8/i7yd+pLaqeLlA0rTMfg== dependencies: "@adobe/css-tools" "^4.0.1" "@babel/runtime" "^7.9.2" - "@types/testing-library__jest-dom" "^5.9.1" aria-query "^5.0.0" chalk "^3.0.0" css.escape "^1.5.1" @@ -2225,10 +2224,10 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@*", "@types/jest@^29.5.2": - version "29.5.4" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.4.tgz#9d0a16edaa009a71e6a71a999acd582514dab566" - integrity sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A== +"@types/jest@^29.5.2": + version "29.5.3" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.3.tgz#7a35dc0044ffb8b56325c6802a4781a626b05777" + integrity sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -2535,13 +2534,6 @@ resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== -"@types/testing-library__jest-dom@^5.9.1": - version "5.14.9" - resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz#0fb1e6a0278d87b6737db55af5967570b67cb466" - integrity sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw== - dependencies: - "@types/jest" "*" - "@types/tough-cookie@*": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" @@ -11353,6 +11345,7 @@ stringz@^2.1.0: char-regex "^1.0.2" "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + name strip-ansi-cjs version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== From 67166de865b00a972e7d1c4e507c9f20a454efbd Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Aug 2023 12:43:00 +0200 Subject: [PATCH 23/81] Add `from:me` syntax to search (#26660) --- app/lib/search_query_transformer.rb | 13 ++++++++----- app/services/statuses_search_service.rb | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index 915f9c331..f1fc687e4 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -85,8 +85,9 @@ class SearchQueryTransformer < Parslet::Transform class PrefixClause attr_reader :type, :filter, :operator, :term - def initialize(prefix, operator, term) + def initialize(prefix, operator, term, options = {}) @negated = operator == '-' + @options = options @operator = :filter case prefix @@ -105,15 +106,15 @@ class SearchQueryTransformer < Parslet::Transform when 'before' @filter = :created_at @type = :range - @term = { lt: term } + @term = { lt: term, time_zone: @options[:current_account]&.user_time_zone || 'UTC' } when 'after' @filter = :created_at @type = :range - @term = { gt: term } + @term = { gt: term, time_zone: @options[:current_account]&.user_time_zone || 'UTC' } when 'during' @filter = :created_at @type = :range - @term = { gte: term, lte: term } + @term = { gte: term, lte: term, time_zone: @options[:current_account]&.user_time_zone || 'UTC' } else raise Mastodon::SyntaxError end @@ -126,6 +127,8 @@ class SearchQueryTransformer < Parslet::Transform private def account_id_from_term(term) + return @options[:current_account]&.id || -1 if term == 'me' + username, domain = term.gsub(/\A@/, '').split('@') domain = nil if TagManager.instance.local_domain?(domain) account = Account.find_remote(username, domain) @@ -141,7 +144,7 @@ class SearchQueryTransformer < Parslet::Transform operator = clause[:operator]&.to_s if clause[:prefix] - PrefixClause.new(prefix, operator, clause[:term].to_s) + PrefixClause.new(prefix, operator, clause[:term].to_s, current_account: current_account) elsif clause[:term] TermClause.new(prefix, operator, clause[:term].to_s) elsif clause[:shortcode] diff --git a/app/services/statuses_search_service.rb b/app/services/statuses_search_service.rb index 0d0de2a9d..2317a2a1a 100644 --- a/app/services/statuses_search_service.rb +++ b/app/services/statuses_search_service.rb @@ -59,6 +59,6 @@ class StatusesSearchService < BaseService end def parsed_query - SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query)) + SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query), current_account: @account) end end From bceb8931591b4b9df6e8dcfb47864c230884262c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Aug 2023 13:18:39 +0200 Subject: [PATCH 24/81] Add search options to search popout in web UI (#26662) --- .../features/compose/components/search.jsx | 47 +++++++++++++++++-- app/javascript/mastodon/locales/en.json | 4 ++ .../styles/mastodon/components.scss | 6 +++ 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/app/javascript/mastodon/features/compose/components/search.jsx b/app/javascript/mastodon/features/compose/components/search.jsx index 682f8d3c8..1c629bcbb 100644 --- a/app/javascript/mastodon/features/compose/components/search.jsx +++ b/app/javascript/mastodon/features/compose/components/search.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; -import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import { defineMessages, injectIntl, FormattedMessage, FormattedList } from 'react-intl'; import classNames from 'classnames'; @@ -45,6 +45,16 @@ class Search extends PureComponent { options: [], }; + defaultOptions = [ + { label: <>has: , action: e => { e.preventDefault(); this._insertText('has:') } }, + { label: <>is: , action: e => { e.preventDefault(); this._insertText('is:') } }, + { label: <>language: , action: e => { e.preventDefault(); this._insertText('language:') } }, + { label: <>from: , action: e => { e.preventDefault(); this._insertText('from:') } }, + { label: <>before: , action: e => { e.preventDefault(); this._insertText('before:') } }, + { label: <>during: , action: e => { e.preventDefault(); this._insertText('during:') } }, + { label: <>after: , action: e => { e.preventDefault(); this._insertText('after:') } }, + ]; + setRef = c => { this.searchForm = c; }; @@ -70,7 +80,7 @@ class Search extends PureComponent { handleKeyDown = (e) => { const { selectedOption } = this.state; - const options = this._getOptions(); + const options = this._getOptions().concat(this.defaultOptions); switch(e.key) { case 'Escape': @@ -100,11 +110,9 @@ class Search extends PureComponent { if (selectedOption === -1) { this._submit(); } else if (options.length > 0) { - options[selectedOption].action(); + options[selectedOption].action(e); } - this._unfocus(); - break; case 'Delete': if (selectedOption > -1 && options.length > 0) { @@ -147,6 +155,7 @@ class Search extends PureComponent { router.history.push(`/tags/${query}`); onClickSearchResult(query, 'hashtag'); + this._unfocus(); }; handleAccountClick = () => { @@ -157,6 +166,7 @@ class Search extends PureComponent { router.history.push(`/@${query}`); onClickSearchResult(query, 'account'); + this._unfocus(); }; handleURLClick = () => { @@ -164,6 +174,7 @@ class Search extends PureComponent { const { value, onOpenURL } = this.props; onOpenURL(value, router.history); + this._unfocus(); }; handleStatusSearch = () => { @@ -182,6 +193,8 @@ class Search extends PureComponent { } else if (search.get('type') === 'hashtag') { router.history.push(`/tags/${search.get('q')}`); } + + this._unfocus(); }; handleForgetRecentSearchClick = search => { @@ -194,6 +207,18 @@ class Search extends PureComponent { document.querySelector('.ui').parentElement.focus(); } + _insertText (text) { + const { value, onChange } = this.props; + + if (value === '') { + onChange(text); + } else if (value[value.length - 1] === ' ') { + onChange(`${value}${text}`); + } else { + onChange(`${value} ${text}`); + } + } + _submit (type) { const { onSubmit, openInRoute } = this.props; const { router } = this.context; @@ -203,6 +228,8 @@ class Search extends PureComponent { if (openInRoute) { router.history.push('/search'); } + + this._unfocus(); } _getOptions () { @@ -325,6 +352,16 @@ class Search extends PureComponent {
)} + +

+ +
+ {this.defaultOptions.map(({ key, label, action }, i) => ( + + ))} +
); diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 5eeaf8044..5871b08de 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -586,8 +586,12 @@ "search.quick_action.open_url": "Open URL in Mastodon", "search.quick_action.status_search": "Posts matching {x}", "search.search_or_paste": "Search or paste URL", + "search_popout.language_code": "ISO language code", + "search_popout.options": "Search options", "search_popout.quick_actions": "Quick actions", "search_popout.recent": "Recent searches", + "search_popout.specific_date": "specific date", + "search_popout.user": "user", "search_results.accounts": "Profiles", "search_results.all": "All", "search_results.hashtags": "Hashtags", diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 7e7bf4488..6fa5c545e 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -4991,6 +4991,12 @@ a.status-card { } &__menu { + margin-bottom: 20px; + + &:last-child { + margin-bottom: 0; + } + &__message { color: $dark-text-color; padding: 0 10px; From 0cce7fb6172680fb5819c4d61d7adfba4147dfc1 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 28 Aug 2023 15:04:57 +0200 Subject: [PATCH 25/81] Fix incorrect call to `PublicStatusesIndex.import` (#26697) --- app/models/concerns/account_statuses_search.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/concerns/account_statuses_search.rb b/app/models/concerns/account_statuses_search.rb index 626bf3890..fa9238e6e 100644 --- a/app/models/concerns/account_statuses_search.rb +++ b/app/models/concerns/account_statuses_search.rb @@ -32,7 +32,7 @@ module AccountStatusesSearch return unless Chewy.enabled? statuses.without_reblogs.where(visibility: :public).find_in_batches do |batch| - PublicStatusesIndex.import(query: batch) + PublicStatusesIndex.import(batch) end end From 2304cc6456a5a03624e94849f95d324b132ee72b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Aug 2023 17:08:37 +0200 Subject: [PATCH 26/81] Fix bad search type heuristic (#26673) --- app/services/search_service.rb | 14 ++++++-------- spec/services/search_service_spec.rb | 9 --------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 4e1e7ea26..40d82fc52 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -17,7 +17,7 @@ class SearchService < BaseService results.merge!(url_resource_results) unless url_resource.nil? || @offset.positive? || (@options[:type].present? && url_resource_symbol != @options[:type].to_sym) elsif @query.present? results[:accounts] = perform_accounts_search! if account_searchable? - results[:statuses] = perform_statuses_search! if full_text_searchable? + results[:statuses] = perform_statuses_search! if status_searchable? results[:hashtags] = perform_hashtags_search! if hashtag_searchable? end end @@ -79,18 +79,16 @@ class SearchService < BaseService url_resource.class.name.downcase.pluralize.to_sym end - def full_text_searchable? - return false unless Chewy.enabled? - - statuses_search? && !@account.nil? && !(@query.include?('@') && !@query.include?(' ')) + def status_searchable? + Chewy.enabled? && status_search? && @account.present? end def account_searchable? - account_search? && !(@query.include?('@') && @query.include?(' ')) + account_search? end def hashtag_searchable? - hashtag_search? && !@query.include?('@') + hashtag_search? end def account_search? @@ -101,7 +99,7 @@ class SearchService < BaseService @options[:type].blank? || @options[:type] == 'hashtags' end - def statuses_search? + def status_search? @options[:type].blank? || @options[:type] == 'statuses' end end diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb index 123bd4f56..cb69af5f5 100644 --- a/spec/services/search_service_spec.rb +++ b/spec/services/search_service_spec.rb @@ -83,15 +83,6 @@ describe SearchService, type: :service do expect(Tag).to have_received(:search_for).with('tag', 10, 0, exclude_unreviewed: nil) expect(results).to eq empty_results.merge(hashtags: [tag]) end - - it 'does not include tag when starts with @ character' do - query = '@username' - allow(Tag).to receive(:search_for) - - results = subject.call(query, nil, 10) - expect(Tag).to_not have_received(:search_for) - expect(results).to eq empty_results - end end end end From dd72a8d28b4479afdc19ac73cac83609d85b5f9e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 17:10:58 +0200 Subject: [PATCH 27/81] Update dependency stoplight to v3.0.2 (#26698) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7a68249c2..49ada55d2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -731,7 +731,7 @@ GEM net-ssh (>= 2.8.0) stackprof (0.2.25) statsd-ruby (1.5.0) - stoplight (3.0.1) + stoplight (3.0.2) redlock (~> 1.0) strong_migrations (0.8.0) activerecord (>= 5.2) From 01b87a1632a9024eb8be807cfc41a592b6a3c8ce Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Aug 2023 19:40:08 +0200 Subject: [PATCH 28/81] Change video compression parameters (#26631) --- app/models/media_attachment.rb | 16 ++++++---------- lib/paperclip/transcoder.rb | 10 ++++++++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index 7474b5653..88abd9339 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -44,6 +44,7 @@ class MediaAttachment < ApplicationRecord MAX_VIDEO_MATRIX_LIMIT = 8_294_400 # 3840x2160px MAX_VIDEO_FRAME_RATE = 120 + MAX_VIDEO_FRAMES = 36_000 # Approx. 5 minutes at 120 fps IMAGE_FILE_EXTENSIONS = %w(.jpg .jpeg .png .gif .webp .heic .heif .avif).freeze VIDEO_FILE_EXTENSIONS = %w(.webm .mp4 .m4v .mov).freeze @@ -98,17 +99,12 @@ class MediaAttachment < ApplicationRecord convert_options: { output: { 'loglevel' => 'fatal', - 'movflags' => 'faststart', - 'pix_fmt' => 'yuv420p', - 'vf' => 'scale=\'trunc(iw/2)*2:trunc(ih/2)*2\'', - 'vsync' => 'cfr', + 'preset' => 'veryfast', 'c:v' => 'h264', - 'maxrate' => '1300K', - 'bufsize' => '1300K', - 'b:v' => '1300K', - 'frames:v' => 60 * 60 * 3, - 'crf' => 18, + 'c:a' => 'aac', + 'b:a' => '192k', 'map_metadata' => '-1', + 'frames:v' => MAX_VIDEO_FRAMES, }.freeze, }.freeze, }.freeze @@ -135,7 +131,7 @@ class MediaAttachment < ApplicationRecord convert_options: { output: { 'loglevel' => 'fatal', - :vf => 'scale=\'min(400\, iw):min(400\, ih)\':force_original_aspect_ratio=decrease', + :vf => 'scale=\'min(640\, iw):min(640\, ih)\':force_original_aspect_ratio=decrease', }.freeze, }.freeze, format: 'png', diff --git a/lib/paperclip/transcoder.rb b/lib/paperclip/transcoder.rb index 0f2e30f7d..b88cf662c 100644 --- a/lib/paperclip/transcoder.rb +++ b/lib/paperclip/transcoder.rb @@ -4,6 +4,9 @@ module Paperclip # This transcoder is only to be used for the MediaAttachment model # to check when uploaded videos are actually gifv's class Transcoder < Paperclip::Processor + # This is the H.264 "High" value taken from https://www.dr-lex.be/info-stuff/videocalc.html + BITS_PER_PIXEL = 0.11 + def initialize(file, options = {}, attachment = nil) super @@ -38,8 +41,11 @@ module Paperclip @output_options['vframes'] = 1 when 'mp4' unless eligible_to_passthrough?(metadata) - @output_options['acodec'] = 'aac' - @output_options['strict'] = 'experimental' + bitrate = (metadata.width * metadata.height * 30 * BITS_PER_PIXEL) / 1_000 + + @output_options['b:v'] = "#{bitrate}k" + @output_options['maxrate'] = "#{bitrate + 192}k" + @output_options['bufsize'] = "#{bitrate * 5}k" if high_vfr?(metadata) @output_options['vsync'] = 'vfr' From 10b06436d13702aedaa92a8ece84fbfde2829d57 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Aug 2023 19:49:38 +0200 Subject: [PATCH 29/81] Fix colors and typography on hashtag bar in web UI (#26666) --- .../mastodon/components/hashtag_bar.tsx | 6 +++--- .../styles/mastodon/components.scss | 19 ++++++++++++------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/app/javascript/mastodon/components/hashtag_bar.tsx b/app/javascript/mastodon/components/hashtag_bar.tsx index 674c481b8..d45a6e20e 100644 --- a/app/javascript/mastodon/components/hashtag_bar.tsx +++ b/app/javascript/mastodon/components/hashtag_bar.tsx @@ -10,8 +10,8 @@ import { groupBy, minBy } from 'lodash'; import { getStatusContent } from './status_content'; -// About two lines on desktop -const VISIBLE_HASHTAGS = 7; +// Fit on a single line on desktop +const VISIBLE_HASHTAGS = 3; // Those types are not correct, they need to be replaced once this part of the state is typed export type TagLike = Record<{ name: string }>; @@ -210,7 +210,7 @@ const HashtagBar: React.FC<{ const revealedHashtags = expanded ? hashtags - : hashtags.slice(0, VISIBLE_HASHTAGS - 1); + : hashtags.slice(0, VISIBLE_HASHTAGS); return (
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 6fa5c545e..c07f95f56 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -9308,19 +9308,24 @@ noscript { display: flex; flex-wrap: wrap; font-size: 14px; + line-height: 18px; gap: 4px; + color: $darker-text-color; a { display: inline-flex; - color: $dark-text-color; + color: inherit; text-decoration: none; - &:hover { - text-decoration: none; - - span { - text-decoration: underline; - } + &:hover span { + text-decoration: underline; } } + + .link-button { + color: inherit; + font-size: inherit; + line-height: inherit; + padding: 0; + } } From a7d96e6affd4a2da65f09f3f4ffa1b9350bb9a42 Mon Sep 17 00:00:00 2001 From: Lukas Martini Date: Tue, 29 Aug 2023 09:14:44 +0200 Subject: [PATCH 30/81] Improve error messages when DeepL quota is exceeded (#26704) --- .../api/v1/statuses/translations_controller.rb | 10 +++++++++- config/locales/en.yml | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/v1/statuses/translations_controller.rb b/app/controllers/api/v1/statuses/translations_controller.rb index 540b17d00..ec5ea5b85 100644 --- a/app/controllers/api/v1/statuses/translations_controller.rb +++ b/app/controllers/api/v1/statuses/translations_controller.rb @@ -8,7 +8,15 @@ class Api::V1::Statuses::TranslationsController < Api::BaseController before_action :set_translation rescue_from TranslationService::NotConfiguredError, with: :not_found - rescue_from TranslationService::UnexpectedResponseError, TranslationService::QuotaExceededError, TranslationService::TooManyRequestsError, with: :service_unavailable + rescue_from TranslationService::UnexpectedResponseError, with: :service_unavailable + + rescue_from TranslationService::QuotaExceededError do + render json: { error: I18n.t('translation.errors.quota_exceeded') }, status: 503 + end + + rescue_from TranslationService::TooManyRequestsError do + render json: { error: I18n.t('translation.errors.too_many_requests') }, status: 503 + end def create render json: @translation, serializer: REST::TranslationSerializer diff --git a/config/locales/en.yml b/config/locales/en.yml index 71121bb2e..8bdfd1ec9 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1709,6 +1709,10 @@ en: default: "%b %d, %Y, %H:%M" month: "%b %Y" time: "%H:%M" + translation: + errors: + quota_exceeded: The server-wide usage quota for the translation service has been exceeded. + too_many_requests: There have been too many requests to the translation service recently. two_factor_authentication: add: Add disable: Disable 2FA From 0719216368bf3a90fdb7ab27201a0607b63ea203 Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Tue, 29 Aug 2023 10:16:18 +0200 Subject: [PATCH 31/81] Remove dead code from public.jsx (#26547) --- app/javascript/packs/public.jsx | 81 ++++----------------------------- 1 file changed, 9 insertions(+), 72 deletions(-) diff --git a/app/javascript/packs/public.jsx b/app/javascript/packs/public.jsx index 1d917d60e..ae4a7410e 100644 --- a/app/javascript/packs/public.jsx +++ b/app/javascript/packs/public.jsx @@ -7,8 +7,6 @@ import { defineMessages } from 'react-intl'; import { delegate } from '@rails/ujs'; import axios from 'axios'; -import escapeTextContentForBrowser from 'escape-html'; -import { createBrowserHistory } from 'history'; import { throttle } from 'lodash'; import { start } from '../mastodon/common'; @@ -48,23 +46,6 @@ window.addEventListener('message', e => { function loaded() { const { messages: localeData } = getLocale(); - const scrollToDetailedStatus = () => { - const history = createBrowserHistory(); - const detailedStatuses = document.querySelectorAll('.public-layout .detailed-status'); - const location = history.location; - - if (detailedStatuses.length === 1 && (!location.state || !location.state.scrolledToDetailedStatus)) { - detailedStatuses[0].scrollIntoView(); - history.replace(location.pathname, { ...location.state, scrolledToDetailedStatus: true }); - } - }; - - const getEmojiAnimationHandler = (swapTo) => { - return ({ target }) => { - target.src = target.getAttribute(swapTo); - }; - }; - const locale = document.documentElement.lang; const dateTimeFormat = new Intl.DateTimeFormat(locale, { @@ -158,27 +139,21 @@ function loaded() { const root = createRoot(content); root.render(); document.body.appendChild(content); - scrollToDetailedStatus(); }) .catch(error => { console.error(error); - scrollToDetailedStatus(); }); - } else { - scrollToDetailedStatus(); } - delegate(document, '#user_account_attributes_username', 'input', throttle(() => { - const username = document.getElementById('user_account_attributes_username'); - - if (username.value && username.value.length > 0) { - axios.get('/api/v1/accounts/lookup', { params: { acct: username.value } }).then(() => { - username.setCustomValidity(formatMessage(messages.usernameTaken)); + delegate(document, '#user_account_attributes_username', 'input', throttle(({ target }) => { + if (target.value && target.value.length > 0) { + axios.get('/api/v1/accounts/lookup', { params: { acct: target.value } }).then(() => { + target.setCustomValidity(formatMessage(messages.usernameTaken)); }).catch(() => { - username.setCustomValidity(''); + target.setCustomValidity(''); }); } else { - username.setCustomValidity(''); + target.setCustomValidity(''); } }, 500, { leading: false, trailing: true })); @@ -196,9 +171,6 @@ function loaded() { } }); - delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original')); - delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static')); - delegate(document, '.status__content__spoiler-link', 'click', function() { const statusEl = this.parentNode.parentNode; @@ -220,17 +192,6 @@ function loaded() { }); } -delegate(document, '#account_display_name', 'input', ({ target }) => { - const name = document.querySelector('.card .display-name strong'); - if (name) { - if (target.value) { - name.innerHTML = emojify(escapeTextContentForBrowser(target.value)); - } else { - name.textContent = target.dataset.default; - } - } -}); - delegate(document, '#edit_profile input[type=file]', 'change', ({ target }) => { const avatar = document.getElementById(target.id + '-preview'); const [file] = target.files || []; @@ -239,33 +200,6 @@ delegate(document, '#edit_profile input[type=file]', 'change', ({ target }) => { avatar.src = url; }); -const getProfileAvatarAnimationHandler = (swapTo) => { - //animate avatar gifs on the profile page when moused over - return ({ target }) => { - const swapSrc = target.getAttribute(swapTo); - //only change the img source if autoplay is off and the image src is actually different - if(target.getAttribute('data-autoplay') !== 'true' && target.src !== swapSrc) { - target.src = swapSrc; - } - }; -}; - -delegate(document, 'img#profile_page_avatar', 'mouseover', getProfileAvatarAnimationHandler('data-original')); - -delegate(document, 'img#profile_page_avatar', 'mouseout', getProfileAvatarAnimationHandler('data-static')); - -delegate(document, '#account_locked', 'change', ({ target }) => { - const lock = document.querySelector('.card .display-name i'); - - if (lock) { - if (target.checked) { - delete lock.dataset.hidden; - } else { - lock.dataset.hidden = 'true'; - } - } -}); - delegate(document, '.input-copy input', 'click', ({ target }) => { target.focus(); target.select(); @@ -325,6 +259,9 @@ delegate(document, '.sidebar__toggle__icon', 'keydown', e => { } }); +delegate(document, '.custom-emoji', 'mouseover', ({ target }) => target.src = target.getAttribute('data-original')); +delegate(document, '.custom-emoji', 'mouseout', ({ target }) => target.src = target.getAttribute('data-static')); + // Empty the honeypot fields in JS in case something like an extension // automatically filled them. delegate(document, '#registration_new_user,#new_user', 'submit', () => { From 286a21afdc427a24a32d506dcb5355df434e22ce Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Tue, 29 Aug 2023 10:17:57 +0200 Subject: [PATCH 32/81] Support webpacker live-reloading on Docker (#26419) --- .devcontainer/docker-compose.yml | 1 + Procfile.dev | 2 +- config/initializers/content_security_policy.rb | 3 ++- config/webpacker.yml | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index a2658ea8b..20aecd71d 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -25,6 +25,7 @@ services: command: sleep infinity ports: - '127.0.0.1:3000:3000' + - '127.0.0.1:3035:3035' - '127.0.0.1:4000:4000' networks: - external_network diff --git a/Procfile.dev b/Procfile.dev index ba04fb661..fbb2c2de2 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,4 +1,4 @@ web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq stream: env PORT=4000 yarn run start -webpack: ./bin/webpack-dev-server --listen-host 0.0.0.0 +webpack: bin/webpack-dev-server diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index 98c4f541f..59ac3bdea 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -34,7 +34,8 @@ Rails.application.config.content_security_policy do |p| p.worker_src :self, :blob, assets_host if Rails.env.development? - webpacker_urls = %w(ws http).map { |protocol| "#{protocol}#{Webpacker.dev_server.https? ? 's' : ''}://#{Webpacker.dev_server.host_with_port}" } + webpacker_public_host = ENV.fetch('WEBPACKER_DEV_SERVER_PUBLIC', Webpacker.config.dev_server[:public]) + webpacker_urls = %w(ws http).map { |protocol| "#{protocol}#{Webpacker.dev_server.https? ? 's' : ''}://#{webpacker_public_host}" } p.connect_src :self, :data, :blob, assets_host, media_host, Rails.configuration.x.streaming_api_base_url, *webpacker_urls p.script_src :self, :unsafe_inline, :unsafe_eval, assets_host diff --git a/config/webpacker.yml b/config/webpacker.yml index 6fd0fa1a0..f8462e53a 100644 --- a/config/webpacker.yml +++ b/config/webpacker.yml @@ -58,7 +58,7 @@ development: # Reference: https://webpack.js.org/configuration/dev-server/ dev_server: https: false - host: localhost + host: 0.0.0.0 port: 3035 public: localhost:3035 hmr: false From 075cc8e8a64bd43b83865c7beddb877787ed674f Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Tue, 29 Aug 2023 10:20:36 +0200 Subject: [PATCH 33/81] Improve Codespaces port forwarding (#26400) --- .devcontainer/Dockerfile | 4 -- .devcontainer/codespaces/devcontainer.json | 49 ++++++++++++++++++++++ .devcontainer/devcontainer.json | 22 +++++++--- README.md | 34 +++++++++++---- config/environments/development.rb | 2 + 5 files changed, 93 insertions(+), 18 deletions(-) create mode 100644 .devcontainer/codespaces/devcontainer.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index f991036ad..b3b1d97a2 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -4,10 +4,6 @@ FROM mcr.microsoft.com/devcontainers/ruby:1-3.2-bullseye # Install Rails # RUN gem install rails webdrivers -# Default value to allow debug server to serve content over GitHub Codespace's port forwarding service -# The value is a comma-separated list of allowed domains -ENV RAILS_DEVELOPMENT_HOSTS=".githubpreview.dev,.preview.app.github.dev,.app.github.dev" - ARG NODE_VERSION="16" RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1" diff --git a/.devcontainer/codespaces/devcontainer.json b/.devcontainer/codespaces/devcontainer.json new file mode 100644 index 000000000..ca9156fda --- /dev/null +++ b/.devcontainer/codespaces/devcontainer.json @@ -0,0 +1,49 @@ +{ + "name": "Mastodon on GitHub Codespaces", + "dockerComposeFile": "../docker-compose.yml", + "service": "app", + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + + "features": { + "ghcr.io/devcontainers/features/sshd:1": {} + }, + + "runServices": ["app", "db", "redis"], + + "forwardPorts": [3000, 4000], + + "portsAttributes": { + "3000": { + "label": "web", + "onAutoForward": "notify" + }, + "4000": { + "label": "stream", + "onAutoForward": "silent" + } + }, + + "otherPortsAttributes": { + "onAutoForward": "silent" + }, + + "remoteEnv": { + "LOCAL_DOMAIN": "${localEnv:CODESPACE_NAME}-3000.app.github.dev", + "LOCAL_HTTPS": "true", + "STREAMING_API_BASE_URL": "https://${localEnv:CODESPACE_NAME}-4000.app.github.dev", + "DISABLE_FORGERY_REQUEST_PROTECTION": "true", + "ES_ENABLED": "", + "LIBRE_TRANSLATE_ENDPOINT": "" + }, + + "onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}", + "postCreateCommand": ".devcontainer/post-create.sh", + "waitFor": "postCreateCommand", + + "customizations": { + "vscode": { + "settings": {}, + "extensions": ["EditorConfig.EditorConfig", "webben.browserslist"] + } + } +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ce14169aa..fa8d6542c 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,5 +1,5 @@ { - "name": "Mastodon", + "name": "Mastodon on local machine", "dockerComposeFile": "docker-compose.yml", "service": "app", "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", @@ -8,13 +8,23 @@ "ghcr.io/devcontainers/features/sshd:1": {} }, - "runServices": ["app", "db", "redis"], - "forwardPorts": [3000, 4000], - "containerEnv": { - "ES_ENABLED": "", - "LIBRE_TRANSLATE_ENDPOINT": "" + "portsAttributes": { + "3000": { + "label": "web", + "onAutoForward": "notify", + "requireLocalPort": true + }, + "4000": { + "label": "stream", + "onAutoForward": "silent", + "requireLocalPort": true + } + }, + + "otherPortsAttributes": { + "onAutoForward": "silent" }, "onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}", diff --git a/README.md b/README.md index 37cd3dfb4..e925bec51 100644 --- a/README.md +++ b/README.md @@ -59,13 +59,13 @@ Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Stre ## Deployment -### Tech stack: +### Tech stack - **Ruby on Rails** powers the REST API and other web pages - **React.js** and Redux are used for the dynamic parts of the interface - **Node.js** powers the streaming API -### Requirements: +### Requirements - **PostgreSQL** 9.5+ - **Redis** 4+ @@ -74,6 +74,10 @@ Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Stre The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation. +## Development + +### Vagrant + A **Vagrant** configuration is included for development purposes. To use it, complete the following steps: - Install Vagrant and Virtualbox @@ -82,9 +86,11 @@ A **Vagrant** configuration is included for development purposes. To use it, com - Run `vagrant ssh -c "cd /vagrant && foreman start"` - Open `http://mastodon.local` in your browser +### MacOS + To set up **MacOS** for native development, complete the following steps: -- Install the latest stable Ruby version (use a ruby version manager for easy installation and management of ruby versions) +- Install the latest stable Ruby version (use a Ruby version manager for easy installation and management of Ruby versions) - Run `brew install postgresql@14` - Run `brew install redis` - Run `brew install imagemagick` @@ -94,15 +100,27 @@ To set up **MacOS** for native development, complete the following steps: - Run `bundle exec rails db:setup` (optionally prepend `RAILS_ENV=development` to target the dev environment) - Finally, run `overmind start -f Procfile.dev` -### Getting Started with GitHub Codespaces +### Docker -To get started, create a codespace for this repository by clicking this 👇 +For development with **Docker**, complete the following steps: -[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=52281283) +- Install Docker Desktop +- Run `docker compose -f .devcontainer/docker-compose.yml up -d` +- Run `docker compose -f .devcontainer/docker-compose.yml exec app .devcontainer/post-create.sh` +- Finally, run `docker compose -f .devcontainer/docker-compose.yml exec app foreman start -f Procfile.dev` -A codespace will open in a web-based version of Visual Studio Code. The [dev container](.devcontainer/devcontainer.json) is fully configured with the software needed for this project. +If you are using an IDE with [support for the Development Container specification](https://containers.dev/supporting), it will run the above `docker compose` commands automatically. For **Visual Studio Code** this requires the [Dev Container extension](https://containers.dev/supporting#dev-containers). -**Note**: Dev containers are an open spec that is supported by [GitHub Codespaces](https://github.com/codespaces) and [other tools](https://containers.dev/supporting). +### GitHub Codespaces + +To get you coding in just a few minutes, GitHub Codespaces provides a web-based version of Visual Studio Code and a cloud-hosted development environment fully configured with the software needed for this project.. + +- Click this button to create a new codespace:
+ [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=52281283&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json) +- Wait for the environment to build. This will take a few minutes. +- When the editor is ready, run `foreman start -f Procfile.dev` in the terminal. +- After a few seconds, a popup will appear with a button labeled _Open in Browser_. This will open Mastodon. +- On the _Ports_ tab, right click on the “stream” row and select _Port visibility_ → _Public_. ## Contributing diff --git a/config/environments/development.rb b/config/environments/development.rb index 31a396245..9a6637bdb 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -35,6 +35,8 @@ Rails.application.configure do config.cache_store = :null_store end + config.action_controller.forgery_protection_origin_check = ENV['DISABLE_FORGERY_REQUEST_PROTECTION'].nil? + ActiveSupport::Logger.new(STDOUT).tap do |logger| logger.formatter = config.log_formatter config.logger = ActiveSupport::TaggedLogging.new(logger) From 25bf6406290f49f87c6ed00474702d6729fc98f2 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 29 Aug 2023 10:29:07 +0200 Subject: [PATCH 34/81] Add debug logging on signature verification failure (#26637) --- app/controllers/concerns/signature_verification.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/concerns/signature_verification.rb b/app/controllers/concerns/signature_verification.rb index 1d27c92c8..b0c4fff8b 100644 --- a/app/controllers/concerns/signature_verification.rb +++ b/app/controllers/concerns/signature_verification.rb @@ -119,6 +119,8 @@ module SignatureVerification private def fail_with!(message, **options) + Rails.logger.warn { "Signature verification failed: #{message}" } + @signature_verification_failure_reason = { error: message }.merge(options) @signed_request_actor = nil end From a67cf439eebe0763ae920d242dfe7b8cd0730f23 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Aug 2023 10:50:27 +0200 Subject: [PATCH 35/81] Update dependency axios to v1.5.0 (#26680) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3e9c9abf7..0f34ad355 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3344,9 +3344,9 @@ axe-core@^4.6.2: integrity sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g== axios@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" - integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== + version "1.5.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.5.0.tgz#f02e4af823e2e46a9768cfc74691fdd0517ea267" + integrity sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" From 4ad1c5aa7174e5cef4ba9608a44429916a119bee Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Aug 2023 10:53:01 +0200 Subject: [PATCH 36/81] Update dependency aws-sdk-s3 to v1.133.0 (#26616) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 49ada55d2..f26856bf9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -124,8 +124,8 @@ GEM attr_required (1.0.1) awrence (1.2.1) aws-eventstream (1.2.0) - aws-partitions (1.793.0) - aws-sdk-core (3.180.3) + aws-partitions (1.809.0) + aws-sdk-core (3.181.0) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.5) @@ -133,8 +133,8 @@ GEM aws-sdk-kms (1.71.0) aws-sdk-core (~> 3, >= 3.177.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.132.1) - aws-sdk-core (~> 3, >= 3.179.0) + aws-sdk-s3 (1.133.0) + aws-sdk-core (~> 3, >= 3.181.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.6) aws-sigv4 (1.6.0) From ae6cf33321a9f240ef73666a552e552b65390012 Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Tue, 29 Aug 2023 03:56:19 -0500 Subject: [PATCH 37/81] Fix bug with favourited view on Toots only showing latest favouriting accounts (#26577) --- .../mastodon/actions/interactions.js | 58 ++++++++++++++++++- .../mastodon/features/favourites/index.jsx | 25 +++++--- .../mastodon/reducers/user_lists.js | 15 ++++- 3 files changed, 85 insertions(+), 13 deletions(-) diff --git a/app/javascript/mastodon/actions/interactions.js b/app/javascript/mastodon/actions/interactions.js index 092a67ea7..1ffd23db5 100644 --- a/app/javascript/mastodon/actions/interactions.js +++ b/app/javascript/mastodon/actions/interactions.js @@ -1,5 +1,6 @@ -import api from '../api'; +import api, { getLinks } from '../api'; +import { fetchRelationships } from './accounts'; import { importFetchedAccounts, importFetchedStatus } from './importer'; export const REBLOG_REQUEST = 'REBLOG_REQUEST'; @@ -26,6 +27,10 @@ export const FAVOURITES_FETCH_REQUEST = 'FAVOURITES_FETCH_REQUEST'; export const FAVOURITES_FETCH_SUCCESS = 'FAVOURITES_FETCH_SUCCESS'; export const FAVOURITES_FETCH_FAIL = 'FAVOURITES_FETCH_FAIL'; +export const FAVOURITES_EXPAND_REQUEST = 'FAVOURITES_EXPAND_REQUEST'; +export const FAVOURITES_EXPAND_SUCCESS = 'FAVOURITES_EXPAND_SUCCESS'; +export const FAVOURITES_EXPAND_FAIL = 'FAVOURITES_EXPAND_FAIL'; + export const PIN_REQUEST = 'PIN_REQUEST'; export const PIN_SUCCESS = 'PIN_SUCCESS'; export const PIN_FAIL = 'PIN_FAIL'; @@ -308,8 +313,10 @@ export function fetchFavourites(id) { dispatch(fetchFavouritesRequest(id)); api(getState).get(`/api/v1/statuses/${id}/favourited_by`).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); dispatch(importFetchedAccounts(response.data)); - dispatch(fetchFavouritesSuccess(id, response.data)); + dispatch(fetchFavouritesSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); }).catch(error => { dispatch(fetchFavouritesFail(id, error)); }); @@ -323,17 +330,62 @@ export function fetchFavouritesRequest(id) { }; } -export function fetchFavouritesSuccess(id, accounts) { +export function fetchFavouritesSuccess(id, accounts, next) { return { type: FAVOURITES_FETCH_SUCCESS, id, accounts, + next, }; } export function fetchFavouritesFail(id, error) { return { type: FAVOURITES_FETCH_FAIL, + id, + error, + }; +} + +export function expandFavourites(id) { + return (dispatch, getState) => { + const url = getState().getIn(['user_lists', 'favourited_by', id, 'next']); + if (url === null) { + return; + } + + dispatch(expandFavouritesRequest(id)); + + api(getState).get(url).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + + dispatch(importFetchedAccounts(response.data)); + dispatch(expandFavouritesSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); + }).catch(error => dispatch(expandFavouritesFail(id, error))); + }; +} + +export function expandFavouritesRequest(id) { + return { + type: FAVOURITES_EXPAND_REQUEST, + id, + }; +} + +export function expandFavouritesSuccess(id, accounts, next) { + return { + type: FAVOURITES_EXPAND_SUCCESS, + id, + accounts, + next, + }; +} + +export function expandFavouritesFail(id, error) { + return { + type: FAVOURITES_EXPAND_FAIL, + id, error, }; } diff --git a/app/javascript/mastodon/features/favourites/index.jsx b/app/javascript/mastodon/features/favourites/index.jsx index bfde78708..b8ba94872 100644 --- a/app/javascript/mastodon/features/favourites/index.jsx +++ b/app/javascript/mastodon/features/favourites/index.jsx @@ -8,7 +8,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { connect } from 'react-redux'; -import { fetchFavourites } from 'mastodon/actions/interactions'; +import { debounce } from 'lodash'; + +import { fetchFavourites, expandFavourites } from 'mastodon/actions/interactions'; import ColumnHeader from 'mastodon/components/column_header'; import { Icon } from 'mastodon/components/icon'; import { LoadingIndicator } from 'mastodon/components/loading_indicator'; @@ -21,7 +23,9 @@ const messages = defineMessages({ }); const mapStateToProps = (state, props) => ({ - accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]), + accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'items']), + hasMore: !!state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'next']), + isLoading: state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'isLoading'], true), }); class Favourites extends ImmutablePureComponent { @@ -30,6 +34,8 @@ class Favourites extends ImmutablePureComponent { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, accountIds: ImmutablePropTypes.list, + hasMore: PropTypes.bool, + isLoading: PropTypes.bool, multiColumn: PropTypes.bool, intl: PropTypes.object.isRequired, }; @@ -40,18 +46,16 @@ class Favourites extends ImmutablePureComponent { } } - UNSAFE_componentWillReceiveProps (nextProps) { - if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) { - this.props.dispatch(fetchFavourites(nextProps.params.statusId)); - } - } - handleRefresh = () => { this.props.dispatch(fetchFavourites(this.props.params.statusId)); }; + handleLoadMore = debounce(() => { + this.props.dispatch(expandFavourites(this.props.params.statusId)); + }, 300, { leading: true }); + render () { - const { intl, accountIds, multiColumn } = this.props; + const { intl, accountIds, hasMore, isLoading, multiColumn } = this.props; if (!accountIds) { return ( @@ -75,6 +79,9 @@ class Favourites extends ImmutablePureComponent { diff --git a/app/javascript/mastodon/reducers/user_lists.js b/app/javascript/mastodon/reducers/user_lists.js index e33d365c9..cc9a8b19a 100644 --- a/app/javascript/mastodon/reducers/user_lists.js +++ b/app/javascript/mastodon/reducers/user_lists.js @@ -46,7 +46,12 @@ import { } from '../actions/blocks'; import { REBLOGS_FETCH_SUCCESS, + FAVOURITES_FETCH_REQUEST, FAVOURITES_FETCH_SUCCESS, + FAVOURITES_FETCH_FAIL, + FAVOURITES_EXPAND_REQUEST, + FAVOURITES_EXPAND_SUCCESS, + FAVOURITES_EXPAND_FAIL, } from '../actions/interactions'; import { MUTES_FETCH_REQUEST, @@ -136,7 +141,15 @@ export default function userLists(state = initialState, action) { case REBLOGS_FETCH_SUCCESS: return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id))); case FAVOURITES_FETCH_SUCCESS: - return state.setIn(['favourited_by', action.id], ImmutableList(action.accounts.map(item => item.id))); + return normalizeList(state, ['favourited_by', action.id], action.accounts, action.next); + case FAVOURITES_EXPAND_SUCCESS: + return appendToList(state, ['favourited_by', action.id], action.accounts, action.next); + case FAVOURITES_FETCH_REQUEST: + case FAVOURITES_EXPAND_REQUEST: + return state.setIn(['favourited_by', action.id, 'isLoading'], true); + case FAVOURITES_FETCH_FAIL: + case FAVOURITES_EXPAND_FAIL: + return state.setIn(['favourited_by', action.id, 'isLoading'], false); case NOTIFICATIONS_UPDATE: return action.notification.type === 'follow_request' ? normalizeFollowRequest(state, action.notification) : state; case FOLLOW_REQUESTS_FETCH_SUCCESS: From c0605747adf028c7f5c7cc8aeca01f8285aa6802 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 29 Aug 2023 14:06:22 +0200 Subject: [PATCH 38/81] Fix N+1 in `tootctl search deploy` (#26710) --- .../public_statuses_index_importer.rb | 23 ++++++------------- app/lib/importer/statuses_index_importer.rb | 2 +- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/app/lib/importer/public_statuses_index_importer.rb b/app/lib/importer/public_statuses_index_importer.rb index 8e36e36f9..72d02318b 100644 --- a/app/lib/importer/public_statuses_index_importer.rb +++ b/app/lib/importer/public_statuses_index_importer.rb @@ -2,23 +2,14 @@ class Importer::PublicStatusesIndexImporter < Importer::BaseImporter def import! - indexable_statuses_scope.find_in_batches(batch_size: @batch_size) do |batch| - in_work_unit(batch.map(&:status_id)) do |status_ids| + scope.select(:id).find_in_batches(batch_size: @batch_size) do |batch| + in_work_unit(batch.pluck(:id)) do |status_ids| bulk = ActiveRecord::Base.connection_pool.with_connection do - Chewy::Index::Import::BulkBuilder.new(index, to_index: Status.includes(:media_attachments, :preloadable_poll).where(id: status_ids)).bulk_body + Chewy::Index::Import::BulkBuilder.new(index, to_index: Status.includes(:media_attachments, :preloadable_poll, :preview_cards).where(id: status_ids)).bulk_body end - indexed = 0 - deleted = 0 - - bulk.map! do |entry| - if entry[:index] - indexed += 1 - else - deleted += 1 - end - entry - end + indexed = bulk.count { |entry| entry[:index] } + deleted = bulk.count { |entry| entry[:delete] } Chewy::Index::Import::BulkRequest.new(index).perform(bulk) @@ -35,7 +26,7 @@ class Importer::PublicStatusesIndexImporter < Importer::BaseImporter PublicStatusesIndex end - def indexable_statuses_scope - Status.indexable.select('"statuses"."id", COALESCE("statuses"."reblog_of_id", "statuses"."id") AS status_id') + def scope + Status.indexable end end diff --git a/app/lib/importer/statuses_index_importer.rb b/app/lib/importer/statuses_index_importer.rb index b0721c2e0..0277cd0ef 100644 --- a/app/lib/importer/statuses_index_importer.rb +++ b/app/lib/importer/statuses_index_importer.rb @@ -14,7 +14,7 @@ class Importer::StatusesIndexImporter < Importer::BaseImporter scope.find_in_batches(batch_size: @batch_size) do |tmp| in_work_unit(tmp.map(&:status_id)) do |status_ids| bulk = ActiveRecord::Base.connection_pool.with_connection do - Chewy::Index::Import::BulkBuilder.new(index, to_index: Status.includes(:media_attachments, :preloadable_poll).where(id: status_ids)).bulk_body + Chewy::Index::Import::BulkBuilder.new(index, to_index: Status.includes(:media_attachments, :preloadable_poll, :preview_cards).where(id: status_ids)).bulk_body end indexed = 0 From 74eb7dbf2d79b74f7d6f09ca3d39b3ba67f5f7bf Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Tue, 29 Aug 2023 07:42:20 -0500 Subject: [PATCH 39/81] Fix bug with reblogged view on Toots only showing latest reblogging accounts (#26574) --- .../mastodon/actions/interactions.js | 55 ++++++++++++++++++- .../mastodon/features/reblogs/index.jsx | 27 +++++---- .../mastodon/reducers/user_lists.js | 15 ++++- 3 files changed, 84 insertions(+), 13 deletions(-) diff --git a/app/javascript/mastodon/actions/interactions.js b/app/javascript/mastodon/actions/interactions.js index 1ffd23db5..7d0144438 100644 --- a/app/javascript/mastodon/actions/interactions.js +++ b/app/javascript/mastodon/actions/interactions.js @@ -7,6 +7,10 @@ export const REBLOG_REQUEST = 'REBLOG_REQUEST'; export const REBLOG_SUCCESS = 'REBLOG_SUCCESS'; export const REBLOG_FAIL = 'REBLOG_FAIL'; +export const REBLOGS_EXPAND_REQUEST = 'REBLOGS_EXPAND_REQUEST'; +export const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS'; +export const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL'; + export const FAVOURITE_REQUEST = 'FAVOURITE_REQUEST'; export const FAVOURITE_SUCCESS = 'FAVOURITE_SUCCESS'; export const FAVOURITE_FAIL = 'FAVOURITE_FAIL'; @@ -278,8 +282,10 @@ export function fetchReblogs(id) { dispatch(fetchReblogsRequest(id)); api(getState).get(`/api/v1/statuses/${id}/reblogged_by`).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); dispatch(importFetchedAccounts(response.data)); - dispatch(fetchReblogsSuccess(id, response.data)); + dispatch(fetchReblogsSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); }).catch(error => { dispatch(fetchReblogsFail(id, error)); }); @@ -293,17 +299,62 @@ export function fetchReblogsRequest(id) { }; } -export function fetchReblogsSuccess(id, accounts) { +export function fetchReblogsSuccess(id, accounts, next) { return { type: REBLOGS_FETCH_SUCCESS, id, accounts, + next, }; } export function fetchReblogsFail(id, error) { return { type: REBLOGS_FETCH_FAIL, + id, + error, + }; +} + +export function expandReblogs(id) { + return (dispatch, getState) => { + const url = getState().getIn(['user_lists', 'reblogged_by', id, 'next']); + if (url === null) { + return; + } + + dispatch(expandReblogsRequest(id)); + + api(getState).get(url).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + + dispatch(importFetchedAccounts(response.data)); + dispatch(expandReblogsSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); + }).catch(error => dispatch(expandReblogsFail(id, error))); + }; +} + +export function expandReblogsRequest(id) { + return { + type: REBLOGS_EXPAND_REQUEST, + id, + }; +} + +export function expandReblogsSuccess(id, accounts, next) { + return { + type: REBLOGS_EXPAND_SUCCESS, + id, + accounts, + next, + }; +} + +export function expandReblogsFail(id, error) { + return { + type: REBLOGS_EXPAND_FAIL, + id, error, }; } diff --git a/app/javascript/mastodon/features/reblogs/index.jsx b/app/javascript/mastodon/features/reblogs/index.jsx index 8bcef863f..0c4e6dbb9 100644 --- a/app/javascript/mastodon/features/reblogs/index.jsx +++ b/app/javascript/mastodon/features/reblogs/index.jsx @@ -8,9 +8,11 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { connect } from 'react-redux'; +import { debounce } from 'lodash'; + import { Icon } from 'mastodon/components/icon'; -import { fetchReblogs } from '../../actions/interactions'; +import { fetchReblogs, expandReblogs } from '../../actions/interactions'; import ColumnHeader from '../../components/column_header'; import { LoadingIndicator } from '../../components/loading_indicator'; import ScrollableList from '../../components/scrollable_list'; @@ -22,7 +24,9 @@ const messages = defineMessages({ }); const mapStateToProps = (state, props) => ({ - accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId]), + accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId, 'items']), + hasMore: !!state.getIn(['user_lists', 'reblogged_by', props.params.statusId, 'next']), + isLoading: state.getIn(['user_lists', 'reblogged_by', props.params.statusId, 'isLoading'], true), }); class Reblogs extends ImmutablePureComponent { @@ -31,6 +35,8 @@ class Reblogs extends ImmutablePureComponent { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, accountIds: ImmutablePropTypes.list, + hasMore: PropTypes.bool, + isLoading: PropTypes.bool, multiColumn: PropTypes.bool, intl: PropTypes.object.isRequired, }; @@ -39,20 +45,18 @@ class Reblogs extends ImmutablePureComponent { if (!this.props.accountIds) { this.props.dispatch(fetchReblogs(this.props.params.statusId)); } - } - - UNSAFE_componentWillReceiveProps(nextProps) { - if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) { - this.props.dispatch(fetchReblogs(nextProps.params.statusId)); - } - } + }; handleRefresh = () => { this.props.dispatch(fetchReblogs(this.props.params.statusId)); }; + handleLoadMore = debounce(() => { + this.props.dispatch(expandReblogs(this.props.params.statusId)); + }, 300, { leading: true }); + render () { - const { intl, accountIds, multiColumn } = this.props; + const { intl, accountIds, hasMore, isLoading, multiColumn } = this.props; if (!accountIds) { return ( @@ -76,6 +80,9 @@ class Reblogs extends ImmutablePureComponent { diff --git a/app/javascript/mastodon/reducers/user_lists.js b/app/javascript/mastodon/reducers/user_lists.js index cc9a8b19a..089899398 100644 --- a/app/javascript/mastodon/reducers/user_lists.js +++ b/app/javascript/mastodon/reducers/user_lists.js @@ -45,7 +45,12 @@ import { BLOCKS_EXPAND_FAIL, } from '../actions/blocks'; import { + REBLOGS_FETCH_REQUEST, REBLOGS_FETCH_SUCCESS, + REBLOGS_FETCH_FAIL, + REBLOGS_EXPAND_REQUEST, + REBLOGS_EXPAND_SUCCESS, + REBLOGS_EXPAND_FAIL, FAVOURITES_FETCH_REQUEST, FAVOURITES_FETCH_SUCCESS, FAVOURITES_FETCH_FAIL, @@ -139,7 +144,15 @@ export default function userLists(state = initialState, action) { case FOLLOWING_EXPAND_FAIL: return state.setIn(['following', action.id, 'isLoading'], false); case REBLOGS_FETCH_SUCCESS: - return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id))); + return normalizeList(state, ['reblogged_by', action.id], action.accounts, action.next); + case REBLOGS_EXPAND_SUCCESS: + return appendToList(state, ['reblogged_by', action.id], action.accounts, action.next); + case REBLOGS_FETCH_REQUEST: + case REBLOGS_EXPAND_REQUEST: + return state.setIn(['reblogged_by', action.id, 'isLoading'], true); + case REBLOGS_FETCH_FAIL: + case REBLOGS_EXPAND_FAIL: + return state.setIn(['reblogged_by', action.id, 'isLoading'], false); case FAVOURITES_FETCH_SUCCESS: return normalizeList(state, ['favourited_by', action.id], action.accounts, action.next); case FAVOURITES_EXPAND_SUCCESS: From 9e77ab7db245a9a4725600cf69a617c0be1f1018 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 29 Aug 2023 17:51:13 +0200 Subject: [PATCH 40/81] Change private statuses index to index without crutches (#26713) --- app/chewy/statuses_index.rb | 31 ++------------------ app/lib/importer/statuses_index_importer.rb | 2 +- app/models/concerns/status_search_concern.rb | 28 +++++++----------- app/models/poll.rb | 1 + app/models/status.rb | 6 ++++ 5 files changed, 21 insertions(+), 47 deletions(-) diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index 6d3352105..2be7e4525 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -40,40 +40,13 @@ class StatusesIndex < Chewy::Index }, } - # We do not use delete_if option here because it would call a method that we - # expect to be called with crutches without crutches, causing n+1 queries - index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preloadable_poll, :preview_cards) - - crutch :mentions do |collection| - data = ::Mention.where(status_id: collection.map(&:id)).where(account: Account.local, silent: false).pluck(:status_id, :account_id) - data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } - end - - crutch :favourites do |collection| - data = ::Favourite.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id) - data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } - end - - crutch :reblogs do |collection| - data = ::Status.where(reblog_of_id: collection.map(&:id)).where(account: Account.local).pluck(:reblog_of_id, :account_id) - data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } - end - - crutch :bookmarks do |collection| - data = ::Bookmark.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id) - data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } - end - - crutch :votes do |collection| - data = ::PollVote.joins(:poll).where(poll: { status_id: collection.map(&:id) }).where(account: Account.local).pluck(:status_id, :account_id) - data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } - end + index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preview_cards, :local_mentioned, :local_favorited, :local_reblogged, :local_bookmarked, preloadable_poll: :local_voters), delete_if: ->(status) { status.searchable_by.empty? } root date_detection: false do field(:id, type: 'long') field(:account_id, type: 'long') field(:text, type: 'text', analyzer: 'verbatim', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } - field(:searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) }) + field(:searchable_by, type: 'long', value: ->(status) { status.searchable_by }) field(:language, type: 'keyword') field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) field(:created_at, type: 'date') diff --git a/app/lib/importer/statuses_index_importer.rb b/app/lib/importer/statuses_index_importer.rb index 0277cd0ef..285ddc871 100644 --- a/app/lib/importer/statuses_index_importer.rb +++ b/app/lib/importer/statuses_index_importer.rb @@ -14,7 +14,7 @@ class Importer::StatusesIndexImporter < Importer::BaseImporter scope.find_in_batches(batch_size: @batch_size) do |tmp| in_work_unit(tmp.map(&:status_id)) do |status_ids| bulk = ActiveRecord::Base.connection_pool.with_connection do - Chewy::Index::Import::BulkBuilder.new(index, to_index: Status.includes(:media_attachments, :preloadable_poll, :preview_cards).where(id: status_ids)).bulk_body + Chewy::Index::Import::BulkBuilder.new(index, to_index: index.adapter.default_scope.where(id: status_ids)).bulk_body end indexed = 0 diff --git a/app/models/concerns/status_search_concern.rb b/app/models/concerns/status_search_concern.rb index 21048b568..3ef45754a 100644 --- a/app/models/concerns/status_search_concern.rb +++ b/app/models/concerns/status_search_concern.rb @@ -7,26 +7,20 @@ module StatusSearchConcern scope :indexable, -> { without_reblogs.where(visibility: :public).joins(:account).where(account: { indexable: true }) } end - def searchable_by(preloaded = nil) - ids = [] + def searchable_by + @searchable_by ||= begin + ids = [] - ids << account_id if local? + ids << account_id if local? - if preloaded.nil? - ids += mentions.joins(:account).merge(Account.local).active.pluck(:account_id) - ids += favourites.joins(:account).merge(Account.local).pluck(:account_id) - ids += reblogs.joins(:account).merge(Account.local).pluck(:account_id) - ids += bookmarks.joins(:account).merge(Account.local).pluck(:account_id) - ids += poll.votes.joins(:account).merge(Account.local).pluck(:account_id) if poll.present? - else - ids += preloaded.mentions[id] || [] - ids += preloaded.favourites[id] || [] - ids += preloaded.reblogs[id] || [] - ids += preloaded.bookmarks[id] || [] - ids += preloaded.votes[id] || [] + ids += local_mentioned.pluck(:id) + ids += local_favorited.pluck(:id) + ids += local_reblogged.pluck(:id) + ids += local_bookmarked.pluck(:id) + ids += preloadable_poll.local_voters.pluck(:id) if preloadable_poll.present? + + ids.uniq end - - ids.uniq end def searchable_text diff --git a/app/models/poll.rb b/app/models/poll.rb index 74a77978b..efa625eb5 100644 --- a/app/models/poll.rb +++ b/app/models/poll.rb @@ -28,6 +28,7 @@ class Poll < ApplicationRecord has_many :votes, class_name: 'PollVote', inverse_of: :poll, dependent: :delete_all has_many :voters, -> { group('accounts.id') }, through: :votes, class_name: 'Account', source: :account + has_many :local_voters, -> { group('accounts.id').merge(Account.local) }, through: :votes, class_name: 'Account', source: :account has_many :notifications, as: :activity, dependent: :destroy diff --git a/app/models/status.rb b/app/models/status.rb index 760b8ec33..1c41ef1d5 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -72,6 +72,12 @@ class Status < ApplicationRecord has_many :active_mentions, -> { active }, class_name: 'Mention', inverse_of: :status has_many :media_attachments, dependent: :nullify + # Those associations are used for the private search index + has_many :local_mentioned, -> { merge(Account.local) }, through: :active_mentions, source: :account + has_many :local_favorited, -> { merge(Account.local) }, through: :favourites, source: :account + has_many :local_reblogged, -> { merge(Account.local) }, through: :reblogs, source: :account + has_many :local_bookmarked, -> { merge(Account.local) }, through: :bookmarks, source: :account + has_and_belongs_to_many :tags has_and_belongs_to_many :preview_cards From 168688ef1c538c295fe08516825c845f08e2b7d5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 09:34:07 +0200 Subject: [PATCH 41/81] Update dependency webmock to v3.19.1 (#26722) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f26856bf9..7e5dac031 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -109,7 +109,7 @@ GEM i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - addressable (2.8.4) + addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) aes_key_wrap (1.1.0) airbrussh (1.4.1) @@ -795,7 +795,7 @@ GEM webfinger (1.2.0) activesupport httpclient (>= 2.4) - webmock (3.18.1) + webmock (3.19.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) From 24deaf2e4a6bd7a3d5fb50fa8ed5102286fa8988 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 09:44:14 +0200 Subject: [PATCH 42/81] Update dependency postcss to v8.4.29 (#26720) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0f34ad355..0a7c840dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9625,9 +9625,9 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss@^8.2.15, postcss@^8.4.24, postcss@^8.4.25: - version "8.4.28" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.28.tgz#c6cc681ed00109072816e1557f889ef51cf950a5" - integrity sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw== + version "8.4.29" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.29.tgz#33bc121cf3b3688d4ddef50be869b2a54185a1dd" + integrity sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw== dependencies: nanoid "^3.3.6" picocolors "^1.0.0" From 0e1bff178ecaa4434191335d81d2a8809f31e550 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 09:45:13 +0200 Subject: [PATCH 43/81] Update dependency chewy to v7.3.4 (#26717) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7e5dac031..f1a61c5e0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -203,7 +203,7 @@ GEM activesupport cbor (0.5.9.6) charlock_holmes (0.7.7) - chewy (7.3.3) + chewy (7.3.4) activesupport (>= 5.2) elasticsearch (>= 7.12.0, < 7.14.0) elasticsearch-dsl From bba76e726714e49dae8ea6dd4b2439ae1d7699a8 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 30 Aug 2023 16:07:26 +0200 Subject: [PATCH 44/81] Change `language:` to attempt to match to a known value in search (#26663) --- app/lib/search_query_transformer.rb | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index f1fc687e4..86e3f5000 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -98,7 +98,7 @@ class SearchQueryTransformer < Parslet::Transform when 'language' @filter = :language @type = :term - @term = term + @term = language_code_from_term(term) when 'from' @filter = :account_id @type = :term @@ -137,6 +137,22 @@ class SearchQueryTransformer < Parslet::Transform # an ID that does not exist account&.id || -1 end + + def language_code_from_term(term) + language_code = term + + return language_code if LanguagesHelper::SUPPORTED_LOCALES.key?(language_code.to_sym) + + language_code = term.downcase + + return language_code if LanguagesHelper::SUPPORTED_LOCALES.key?(language_code.to_sym) + + language_code = term.split(/[_-]/).first.downcase + + return language_code if LanguagesHelper::SUPPORTED_LOCALES.key?(language_code.to_sym) + + term + end end rule(clause: subtree(:clause)) do From 21ec596dabbe28a5cfc5b29362f46f47b48eb140 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 30 Aug 2023 16:29:52 +0200 Subject: [PATCH 45/81] Fix sign up steps progress layout in right-to-left locales (#26728) --- app/javascript/styles/mastodon/forms.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index beb45ab6e..a7079c145 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -1185,14 +1185,14 @@ code { } li:first-child .label { - left: auto; inset-inline-start: 0; + inset-inline-end: auto; text-align: start; transform: none; } li:last-child .label { - left: auto; + inset-inline-start: auto; inset-inline-end: 0; text-align: end; transform: none; From 5c38c3a9a1e6bb07f33f5fee598b51cb1fd695ba Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 30 Aug 2023 17:36:16 +0200 Subject: [PATCH 46/81] Change text extraction in `PlainTextFormatter` to be faster (#26727) --- app/lib/plain_text_formatter.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/lib/plain_text_formatter.rb b/app/lib/plain_text_formatter.rb index 8eac730be..d1ff6808b 100644 --- a/app/lib/plain_text_formatter.rb +++ b/app/lib/plain_text_formatter.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true class PlainTextFormatter - include ActionView::Helpers::TextHelper - NEWLINE_TAGS_RE = %r{(
|
|

)+} attr_reader :text, :local @@ -18,7 +16,10 @@ class PlainTextFormatter if local? text else - html_entities.decode(strip_tags(insert_newlines)).chomp + node = Nokogiri::HTML.fragment(insert_newlines) + # Elements that are entirely removed with our Sanitize config + node.xpath('.//iframe|.//math|.//noembed|.//noframes|.//noscript|.//plaintext|.//script|.//style|.//svg|.//xmp').remove + node.text.chomp end end @@ -27,8 +28,4 @@ class PlainTextFormatter def insert_newlines text.gsub(NEWLINE_TAGS_RE) { |match| "#{match}\n" } end - - def html_entities - HTMLEntities.new - end end From 15949e42c292344c63f828bed24d6af045a686e6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 31 Aug 2023 09:49:34 +0200 Subject: [PATCH 47/81] Update dependency glob to v10.3.4 (#26734) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0a7c840dd..ec5566be3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6231,9 +6231,9 @@ glob-parent@^6.0.2: is-glob "^4.0.3" glob@^10.2.5, glob@^10.2.6: - version "10.3.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.3.tgz#8360a4ffdd6ed90df84aa8d52f21f452e86a123b" - integrity sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw== + version "10.3.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.4.tgz#c85c9c7ab98669102b6defda76d35c5b1ef9766f" + integrity sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ== dependencies: foreground-child "^3.1.0" jackspeak "^2.0.3" @@ -7336,9 +7336,9 @@ iterator.prototype@^1.1.0: reflect.getprototypeof "^1.0.3" jackspeak@^2.0.3: - version "2.2.1" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.2.1.tgz#655e8cf025d872c9c03d3eb63e8f0c024fef16a6" - integrity sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw== + version "2.3.1" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.1.tgz#ce2effa4c458e053640e61938865a5b5fae98456" + integrity sha512-4iSY3Bh1Htv+kLhiiZunUhQ+OYXIn0ze3ulq8JeWrFKmhPAJSySV2+kdtRh2pGcCeF0s6oR8Oc+pYZynJj4t8A== dependencies: "@isaacs/cliui" "^8.0.2" optionalDependencies: @@ -8192,9 +8192,9 @@ lru-cache@^6.0.0: yallist "^4.0.0" "lru-cache@^9.1.1 || ^10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.0.tgz#b9e2a6a72a129d81ab317202d93c7691df727e61" - integrity sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw== + version "10.0.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" + integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== lz-string@^1.5.0: version "1.5.0" @@ -8526,9 +8526,9 @@ minipass@^5.0.0: integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== "minipass@^5.0.0 || ^6.0.2 || ^7.0.0": - version "7.0.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.2.tgz#58a82b7d81c7010da5bd4b2c0c85ac4b4ec5131e" - integrity sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA== + version "7.0.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.3.tgz#05ea638da44e475037ed94d1c7efcc76a25e1974" + integrity sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg== minizlib@^2.1.1: version "2.1.2" @@ -11345,7 +11345,6 @@ stringz@^2.1.0: char-regex "^1.0.2" "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: - name strip-ansi-cjs version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== From ef9a85a2d82908478cf7cd7fbc5ea6311200264e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 31 Aug 2023 09:52:23 +0200 Subject: [PATCH 48/81] Update dependency webpack-bundle-analyzer to v4.9.1 (#26733) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 74 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/yarn.lock b/yarn.lock index ec5566be3..89b11e8e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2913,16 +2913,16 @@ acorn@^6.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== -acorn@^8.0.4, acorn@^8.8.2: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== - -acorn@^8.1.0, acorn@^8.8.1, acorn@^8.9.0: +acorn@^8.0.4, acorn@^8.1.0, acorn@^8.8.1, acorn@^8.9.0: version "8.10.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== +acorn@^8.8.2: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -8100,6 +8100,16 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== +lodash.escape@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" + integrity sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw== + +lodash.flatten@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g== + lodash.get@^4.0: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -8110,6 +8120,11 @@ lodash.has@^4.0: resolved "https://registry.yarnpkg.com/lodash.has/-/lodash.has-4.5.2.tgz#d19f4dc1095058cccbe2b0cdf4ee0fe4aa37c862" integrity sha512-rnYUdIo6xRCJnQmbVFEwcxF144erlD+M3YcJUVesflU9paQaE8p+fJDcIQrlMYbxoANFL+AB9hZrzSBBk5PL+g== +lodash.invokemap@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.invokemap/-/lodash.invokemap-4.6.0.tgz#1748cda5d8b0ef8369c4eb3ec54c21feba1f2d62" + integrity sha512-CfkycNtMqgUlfjfdh2BhKO/ZXrP8ePOX5lEU/g0R3ItJcnuxWDwokMGKx1hWcfOikmyOVx6X9IwWnDGlgKl61w== + lodash.isboolean@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" @@ -8135,6 +8150,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.pullall@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.pullall/-/lodash.pullall-4.2.0.tgz#9d98b8518b7c965b0fae4099bd9fb7df8bbf38ba" + integrity sha512-VhqxBKH0ZxPpLhiu68YD1KnHmbhQJQctcipvmFnqIBDYzcIHzf3Zpu0tpeOKtR4x76p9yohc506eGdOjTmyIBg== + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -8150,6 +8170,11 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== +lodash.uniqby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302" + integrity sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww== + lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -10905,14 +10930,14 @@ signal-exit@^4.0.1: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -sirv@^1.0.7: - version "1.0.19" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" - integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== +sirv@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.3.tgz#ca5868b87205a74bef62a469ed0296abceccd446" + integrity sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA== dependencies: "@polka/url" "^1.0.0-next.20" mrmime "^1.0.0" - totalist "^1.0.0" + totalist "^3.0.0" sisteransi@^1.0.5: version "1.0.5" @@ -11831,10 +11856,10 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -totalist@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" - integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== tough-cookie@^4.1.2: version "4.1.3" @@ -12387,19 +12412,26 @@ webpack-assets-manifest@^4.0.6: webpack-sources "^1.0" webpack-bundle-analyzer@^4.8.0: - version "4.9.0" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.0.tgz#fc093c4ab174fd3dcbd1c30b763f56d10141209d" - integrity sha512-+bXGmO1LyiNx0i9enBu3H8mv42sj/BJWhZNFwjz92tVnBa9J3JMGo2an2IXlEleoDOPn/Hofl5hr/xCpObUDtw== + version "4.9.1" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.1.tgz#d00bbf3f17500c10985084f22f1a2bf45cb2f09d" + integrity sha512-jnd6EoYrf9yMxCyYDPj8eutJvtjQNp8PHmni/e/ulydHBWhT5J3menXt3HEkScsu9YqMAcG4CfFjs3rj5pVU1w== dependencies: "@discoveryjs/json-ext" "0.5.7" acorn "^8.0.4" acorn-walk "^8.0.0" - chalk "^4.1.0" commander "^7.2.0" + escape-string-regexp "^4.0.0" gzip-size "^6.0.0" - lodash "^4.17.20" + is-plain-object "^5.0.0" + lodash.debounce "^4.0.8" + lodash.escape "^4.0.1" + lodash.flatten "^4.4.0" + lodash.invokemap "^4.6.0" + lodash.pullall "^4.2.0" + lodash.uniqby "^4.7.0" opener "^1.5.2" - sirv "^1.0.7" + picocolors "^1.0.0" + sirv "^2.0.3" ws "^7.3.1" webpack-cli@^3.3.12: From 430eac3eb1021d30f1ca73ec21444a3a5cb0bab7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 31 Aug 2023 10:23:24 +0200 Subject: [PATCH 49/81] Update dependency @babel/preset-env to v7.22.14 (#26732) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 224 +++++++++++++++++++++++++++--------------------------- 1 file changed, 111 insertions(+), 113 deletions(-) diff --git a/yarn.lock b/yarn.lock index 89b11e8e2..880ded977 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,7 +29,7 @@ jsonpointer "^5.0.0" leven "^3.1.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.10", "@babel/code-frame@^7.22.5": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.10": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.10.tgz#1c20e612b768fefa75f6e90d6ecb86329247f0a3" integrity sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA== @@ -44,7 +44,15 @@ dependencies: "@babel/highlight" "^7.22.5" -"@babel/compat-data@^7.22.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": +"@babel/code-frame@^7.22.5": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== @@ -113,7 +121,7 @@ lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.22.11": +"@babel/helper-create-class-features-plugin@^7.22.11", "@babel/helper-create-class-features-plugin@^7.22.5": version "7.22.11" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.11.tgz#4078686740459eeb4af3494a273ac09148dfb213" integrity sha512-y1grdYL4WzmUDBRGK0pDbIoFd7UZKoDurDzWEoNMYoj1EL+foGRQNyPWDcC+YyegN5y1DUsFFmzjGijB3nSVAQ== @@ -128,21 +136,6 @@ "@babel/helper-split-export-declaration" "^7.22.6" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz#dd2612d59eac45588021ac3d6fa976d08f4e95a3" - integrity sha512-5IBb77txKYQPpOEdUdIhBx8VrZyDCQ+H82H0+5dX1TmuscP5vJKEE3cKurjtIw/vFwzbVH48VweE78kVDBrqjA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" - "@babel/helper-member-expression-to-functions" "^7.22.5" - "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - semver "^6.3.1" - "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.5": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz#9d8e61a8d9366fe66198f57c40565663de0825f6" @@ -292,7 +285,16 @@ "@babel/traverse" "^7.22.11" "@babel/types" "^7.22.11" -"@babel/highlight@^7.22.10", "@babel/highlight@^7.22.5": +"@babel/highlight@^7.22.10", "@babel/highlight@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.13.tgz#9cda839e5d3be9ca9e8c26b6dd69e7548f0cbf16" + integrity sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@babel/highlight@^7.22.5": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.10.tgz#02a3f6d8c1cb4521b2fd0ab0da8f4739936137d7" integrity sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ== @@ -306,11 +308,16 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== -"@babel/parser@^7.22.11", "@babel/parser@^7.22.5": +"@babel/parser@^7.22.11": version "7.22.11" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.11.tgz#becf8ee33aad2a35ed5607f521fe6e72a615f905" integrity sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g== +"@babel/parser@^7.22.5": + version "7.22.14" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.14.tgz#c7de58e8de106e88efca42ce17f0033209dfd245" + integrity sha512-1KucTHgOvaw/LzCVrEOAyXkr9rQlp0A1HiHRYnSUE9dmb8PvPW7o5sscg+5169r54n3vGlbx6GevTE/Iw/P3AQ== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" @@ -487,10 +494,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-async-generator-functions@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.10.tgz#45946cd17f915b10e65c29b8ed18a0a50fc648c8" - integrity sha512-eueE8lvKVzq5wIObKK/7dvoeKJ+xc6TvRn6aysIjS6pSCeLy7S/eVi7pEQknZqyqvzaNKdDtem8nUNTBgDVR2g== +"@babel/plugin-transform-async-generator-functions@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.11.tgz#dbe3b1ff5a52e2e5edc4b19a60d325a675ed2649" + integrity sha512-0pAlmeRJn6wU84zzZsEOx1JV1Jf8fqO9ok7wofIJwUnplYo247dcd24P+cMJht7ts9xkzdtB0EPHmOb7F+KzXw== dependencies: "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" @@ -528,12 +535,12 @@ "@babel/helper-create-class-features-plugin" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-class-static-block@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz#3e40c46f048403472d6f4183116d5e46b1bff5ba" - integrity sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA== +"@babel/plugin-transform-class-static-block@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz#dc8cc6e498f55692ac6b4b89e56d87cec766c974" + integrity sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g== dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.11" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-class-static-block" "^7.14.5" @@ -582,10 +589,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-dynamic-import@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz#d6908a8916a810468c4edff73b5b75bda6ad393e" - integrity sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ== +"@babel/plugin-transform-dynamic-import@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz#2c7722d2a5c01839eaf31518c6ff96d408e447aa" + integrity sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" @@ -598,10 +605,10 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-export-namespace-from@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz#57c41cb1d0613d22f548fddd8b288eedb9973a5b" - integrity sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg== +"@babel/plugin-transform-export-namespace-from@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz#b3c84c8f19880b6c7440108f8929caf6056db26c" + integrity sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" @@ -622,10 +629,10 @@ "@babel/helper-function-name" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-json-strings@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz#14b64352fdf7e1f737eed68de1a1468bd2a77ec0" - integrity sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A== +"@babel/plugin-transform-json-strings@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz#689a34e1eed1928a40954e37f74509f48af67835" + integrity sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-json-strings" "^7.8.3" @@ -637,10 +644,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-logical-assignment-operators@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz#66ae5f068fd5a9a5dc570df16f56c2a8462a9d6c" - integrity sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA== +"@babel/plugin-transform-logical-assignment-operators@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz#24c522a61688bde045b7d9bc3c2597a4d948fc9c" + integrity sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" @@ -669,22 +676,13 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-simple-access" "^7.22.5" -"@babel/plugin-transform-modules-commonjs@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" - integrity sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA== - dependencies: - "@babel/helper-module-transforms" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-simple-access" "^7.22.5" - -"@babel/plugin-transform-modules-systemjs@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz#18c31410b5e579a0092638f95c896c2a98a5d496" - integrity sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ== +"@babel/plugin-transform-modules-systemjs@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.11.tgz#3386be5875d316493b517207e8f1931d93154bb1" + integrity sha512-rIqHmHoMEOhI3VkVf5jQ15l539KrwhzqcBO6wdCNWPWc/JWt9ILNYNUssbRpeq0qWns8svuw8LnMNCvWBIJ8wA== dependencies: "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-module-transforms" "^7.22.9" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-validator-identifier" "^7.22.5" @@ -711,7 +709,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-nullish-coalescing-operator@^7.22.3", "@babel/plugin-transform-nullish-coalescing-operator@^7.22.5": +"@babel/plugin-transform-nullish-coalescing-operator@^7.22.11", "@babel/plugin-transform-nullish-coalescing-operator@^7.22.3": version "7.22.11" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz#debef6c8ba795f5ac67cd861a81b744c5d38d9fc" integrity sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg== @@ -719,21 +717,21 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-transform-numeric-separator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz#57226a2ed9e512b9b446517ab6fa2d17abb83f58" - integrity sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g== +"@babel/plugin-transform-numeric-separator@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz#498d77dc45a6c6db74bb829c02a01c1d719cbfbd" + integrity sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-transform-object-rest-spread@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz#9686dc3447df4753b0b2a2fae7e8bc33cdc1f2e1" - integrity sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ== +"@babel/plugin-transform-object-rest-spread@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.11.tgz#dbbb06ce783cd994a8f430d8cefa553e9b42ca62" + integrity sha512-nX8cPFa6+UmbepISvlf5jhQyaC7ASs/7UxHmMkuJ/k5xSHvDPPaibMo+v3TXwU/Pjqhep/nFNpd3zn4YR59pnw== dependencies: - "@babel/compat-data" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.5" + "@babel/compat-data" "^7.22.9" + "@babel/helper-compilation-targets" "^7.22.10" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-transform-parameters" "^7.22.5" @@ -746,18 +744,18 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-replace-supers" "^7.22.5" -"@babel/plugin-transform-optional-catch-binding@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz#842080be3076703be0eaf32ead6ac8174edee333" - integrity sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg== +"@babel/plugin-transform-optional-catch-binding@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz#461cc4f578a127bb055527b3e77404cad38c08e0" + integrity sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-transform-optional-chaining@^7.22.10", "@babel/plugin-transform-optional-chaining@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.10.tgz#076d28a7e074392e840d4ae587d83445bac0372a" - integrity sha512-MMkQqZAZ+MGj+jGTG3OTuhKeBpNcO+0oCEbrGNEaOmiEn+1MzRyQlYsruGiU8RTK3zV6XwrVJTmwiDOyYK6J9g== +"@babel/plugin-transform-optional-chaining@^7.22.12", "@babel/plugin-transform-optional-chaining@^7.22.5": + version "7.22.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.12.tgz#d7ebf6a88cd2f4d307b0e000ab630acd8124b333" + integrity sha512-7XXCVqZtyFWqjDsYDY4T45w4mlx1rf7aOgkc/Ww76xkgBiOlmjPkx36PBLHa1k1rwWvVgYMPsbuVnIamx2ZQJw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" @@ -778,13 +776,13 @@ "@babel/helper-create-class-features-plugin" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-private-property-in-object@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz#07a77f28cbb251546a43d175a1dda4cf3ef83e32" - integrity sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ== +"@babel/plugin-transform-private-property-in-object@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz#ad45c4fc440e9cb84c718ed0906d96cf40f9a4e1" + integrity sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.11" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" @@ -948,9 +946,9 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.22.4": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.10.tgz#3263b9fe2c8823d191d28e61eac60a79f9ce8a0f" - integrity sha512-riHpLb1drNkpLlocmSyEg4oYJIQFeXAK/d7rI6mbD0XsvoTOOweXDmQPG/ErxsEhWk3rl3Q/3F6RFQlVFS8m0A== + version "7.22.14" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.14.tgz#1cbb468d899f64fa71c53446f13b7ff8c0005cc1" + integrity sha512-daodMIoVo+ol/g+//c/AH+szBkFj4STQUikvBijRGL72Ph+w+AMTSh55DUETe8KJlPlDT1k/mp7NBfOuiWmoig== dependencies: "@babel/compat-data" "^7.22.9" "@babel/helper-compilation-targets" "^7.22.10" @@ -978,41 +976,41 @@ "@babel/plugin-syntax-top-level-await" "^7.14.5" "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" "@babel/plugin-transform-arrow-functions" "^7.22.5" - "@babel/plugin-transform-async-generator-functions" "^7.22.10" + "@babel/plugin-transform-async-generator-functions" "^7.22.11" "@babel/plugin-transform-async-to-generator" "^7.22.5" "@babel/plugin-transform-block-scoped-functions" "^7.22.5" "@babel/plugin-transform-block-scoping" "^7.22.10" "@babel/plugin-transform-class-properties" "^7.22.5" - "@babel/plugin-transform-class-static-block" "^7.22.5" + "@babel/plugin-transform-class-static-block" "^7.22.11" "@babel/plugin-transform-classes" "^7.22.6" "@babel/plugin-transform-computed-properties" "^7.22.5" "@babel/plugin-transform-destructuring" "^7.22.10" "@babel/plugin-transform-dotall-regex" "^7.22.5" "@babel/plugin-transform-duplicate-keys" "^7.22.5" - "@babel/plugin-transform-dynamic-import" "^7.22.5" + "@babel/plugin-transform-dynamic-import" "^7.22.11" "@babel/plugin-transform-exponentiation-operator" "^7.22.5" - "@babel/plugin-transform-export-namespace-from" "^7.22.5" + "@babel/plugin-transform-export-namespace-from" "^7.22.11" "@babel/plugin-transform-for-of" "^7.22.5" "@babel/plugin-transform-function-name" "^7.22.5" - "@babel/plugin-transform-json-strings" "^7.22.5" + "@babel/plugin-transform-json-strings" "^7.22.11" "@babel/plugin-transform-literals" "^7.22.5" - "@babel/plugin-transform-logical-assignment-operators" "^7.22.5" + "@babel/plugin-transform-logical-assignment-operators" "^7.22.11" "@babel/plugin-transform-member-expression-literals" "^7.22.5" "@babel/plugin-transform-modules-amd" "^7.22.5" - "@babel/plugin-transform-modules-commonjs" "^7.22.5" - "@babel/plugin-transform-modules-systemjs" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.11" + "@babel/plugin-transform-modules-systemjs" "^7.22.11" "@babel/plugin-transform-modules-umd" "^7.22.5" "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" "@babel/plugin-transform-new-target" "^7.22.5" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.5" - "@babel/plugin-transform-numeric-separator" "^7.22.5" - "@babel/plugin-transform-object-rest-spread" "^7.22.5" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.11" + "@babel/plugin-transform-numeric-separator" "^7.22.11" + "@babel/plugin-transform-object-rest-spread" "^7.22.11" "@babel/plugin-transform-object-super" "^7.22.5" - "@babel/plugin-transform-optional-catch-binding" "^7.22.5" - "@babel/plugin-transform-optional-chaining" "^7.22.10" + "@babel/plugin-transform-optional-catch-binding" "^7.22.11" + "@babel/plugin-transform-optional-chaining" "^7.22.12" "@babel/plugin-transform-parameters" "^7.22.5" "@babel/plugin-transform-private-methods" "^7.22.5" - "@babel/plugin-transform-private-property-in-object" "^7.22.5" + "@babel/plugin-transform-private-property-in-object" "^7.22.11" "@babel/plugin-transform-property-literals" "^7.22.5" "@babel/plugin-transform-regenerator" "^7.22.10" "@babel/plugin-transform-reserved-words" "^7.22.5" @@ -1026,7 +1024,7 @@ "@babel/plugin-transform-unicode-regex" "^7.22.5" "@babel/plugin-transform-unicode-sets-regex" "^7.22.5" "@babel/preset-modules" "0.1.6-no-external-plugins" - "@babel/types" "^7.22.10" + "@babel/types" "^7.22.11" babel-plugin-polyfill-corejs2 "^0.4.5" babel-plugin-polyfill-corejs3 "^0.8.3" babel-plugin-polyfill-regenerator "^0.5.2" @@ -1125,7 +1123,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.3.3", "@babel/types@^7.4.4": +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.3.3": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03" integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg== @@ -1143,7 +1141,7 @@ "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" -"@babel/types@^7.22.10", "@babel/types@^7.22.11", "@babel/types@^7.22.5": +"@babel/types@^7.22.10", "@babel/types@^7.22.11", "@babel/types@^7.22.5", "@babel/types@^7.4.4": version "7.22.11" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.11.tgz#0e65a6a1d4d9cbaa892b2213f6159485fe632ea2" integrity sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg== @@ -3932,9 +3930,9 @@ caniuse-lite@^1.0.30001502: integrity sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA== caniuse-lite@^1.0.30001517: - version "1.0.30001522" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz#44b87a406c901269adcdb834713e23582dd71856" - integrity sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg== + version "1.0.30001524" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001524.tgz#1e14bce4f43c41a7deaeb5ebfe86664fe8dadb80" + integrity sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA== caniuse-lite@^1.0.30001520: version "1.0.30001520" @@ -4335,11 +4333,11 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-compat@^3.31.0: - version "3.32.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.32.0.tgz#f41574b6893ab15ddb0ac1693681bd56c8550a90" - integrity sha512-7a9a3D1k4UCVKnLhrgALyFcP7YCsLOQIxPd0dKjf/6GuPcgyiGP70ewWdCGrSK7evyhymi0qO4EqCmSJofDeYw== + version "3.32.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.32.1.tgz#55f9a7d297c0761a8eb1d31b593e0f5b6ffae964" + integrity sha512-GSvKDv4wE0bPnQtjklV101juQ85g6H3rm5PDP20mqlS5j0kXF3pP97YvAu5hl+uFHqMictp3b2VxOHljWMAtuA== dependencies: - browserslist "^4.21.9" + browserslist "^4.21.10" core-js@^2.5.0: version "2.6.12" @@ -5085,9 +5083,9 @@ electron-to-chromium@^1.4.428: integrity sha512-/g3UyNDmDd6ebeWapmAoiyy+Sy2HyJ+/X8KyvNeHfKRFfHaA2W8oF5fxD5F3tjBDcjpwo0iek6YNgxNXDBoEtA== electron-to-chromium@^1.4.477: - version "1.4.500" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.500.tgz#7dd05fdfbe02ed34b9f6099cfe01407b473d5af7" - integrity sha512-P38NO8eOuWOKY1sQk5yE0crNtrjgjJj6r3NrbIKtG18KzCHmHE2Bt+aQA7/y0w3uYsHWxDa6icOohzjLJ4vJ4A== + version "1.4.505" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.505.tgz#00571ade5975b58413f0f56a665b065bfc29cdfc" + integrity sha512-0A50eL5BCCKdxig2SsCXhpuztnB9PfUgRMojj5tMvt8O54lbwz3t6wNgnpiTRosw5QjlJB7ixhVyeg8daLQwSQ== elliptic@^6.5.3: version "6.5.4" From cb9f96036cf78835e22aafb30b80717b5a8b7394 Mon Sep 17 00:00:00 2001 From: gunchleoc Date: Thu, 31 Aug 2023 12:17:10 +0200 Subject: [PATCH 50/81] Add suggestion for secure cyphers to nginx.conf (#26349) --- dist/nginx.conf | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dist/nginx.conf b/dist/nginx.conf index 39fa58e50..5bb990386 100644 --- a/dist/nginx.conf +++ b/dist/nginx.conf @@ -36,7 +36,11 @@ server { server_name example.com; ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA; + + # You can use https://ssl-config.mozilla.org/ to generate your cipher set. + # We recommend their "Intermediate" level. + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305; + ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_tickets off; From 40b69cc1cd62fa57047905a6999985ae6c183f6c Mon Sep 17 00:00:00 2001 From: Stanislas Signoud Date: Thu, 31 Aug 2023 12:18:46 +0200 Subject: [PATCH 51/81] Add an explanation banner on switching to single column mode (#26019) --- .../features/ui/components/navigation_panel.jsx | 16 +++++++++++----- app/javascript/mastodon/locales/en.json | 1 + app/javascript/mastodon/locales/fr.json | 1 + app/javascript/styles/mastodon/components.scss | 16 ++++++++++++++++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx index d36abf8f1..8006ca89a 100644 --- a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx +++ b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx @@ -31,6 +31,7 @@ const messages = defineMessages({ about: { id: 'navigation_bar.about', defaultMessage: 'About' }, search: { id: 'navigation_bar.search', defaultMessage: 'Search' }, advancedInterface: { id: 'navigation_bar.advanced_interface', defaultMessage: 'Open in advanced web interface' }, + openedInClassicInterface: { id: 'navigation_bar.opened_in_classic_interface', defaultMessage: 'Posts, accounts, and other specific pages are opened by default in the classic web interface.' }, }); class NavigationPanel extends Component { @@ -57,12 +58,17 @@ class NavigationPanel extends Component {
- {transientSingleColumn && ( - - {intl.formatMessage(messages.advancedInterface)} - + {transientSingleColumn ? ( +
+ {intl.formatMessage(messages.openedInClassicInterface)} + {" "} + + {intl.formatMessage(messages.advancedInterface)} + +
+ ) : ( +
)} -
{signedIn && ( diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 5871b08de..90bb9616f 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -411,6 +411,7 @@ "navigation_bar.lists": "Lists", "navigation_bar.logout": "Logout", "navigation_bar.mutes": "Muted users", + "navigation_bar.opened_in_classic_interface": "Posts, accounts, and other specific pages are opened by default in the classic web interface.", "navigation_bar.personal": "Personal", "navigation_bar.pins": "Pinned posts", "navigation_bar.preferences": "Preferences", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 2bef3bb4b..116ed66d0 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -409,6 +409,7 @@ "navigation_bar.lists": "Listes", "navigation_bar.logout": "Déconnexion", "navigation_bar.mutes": "Comptes masqués", + "navigation_bar.opened_in_classic_interface": "Les messages, les comptes et les pages spécifiques sont ouvertes dans l’interface classique.", "navigation_bar.personal": "Personnel", "navigation_bar.pins": "Messages épinglés", "navigation_bar.preferences": "Préférences", diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index c07f95f56..95e6667b5 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -3270,6 +3270,22 @@ $ui-header-height: 55px; border-color: $ui-highlight-color; } +.switch-to-advanced { + color: $classic-primary-color; + background-color: $classic-base-color; + padding: 15px; + border-radius: 4px; + margin-top: 4px; + margin-bottom: 12px; + font-size: 13px; + line-height: 18px; + + .switch-to-advanced__toggle { + color: $ui-button-tertiary-color; + font-weight: bold; + } +} + .column-link { background: lighten($ui-base-color, 8%); color: $primary-text-color; From 336ec503c28356a969e2987ef426063f0943b4e3 Mon Sep 17 00:00:00 2001 From: Tyler Deitz Date: Thu, 31 Aug 2023 04:46:27 -0700 Subject: [PATCH 52/81] Add avatar image to webfinger responses (#26558) --- app/serializers/webfinger_serializer.rb | 37 +++++++---- .../well_known/webfinger_controller_spec.rb | 64 +++++++++++++++++++ 2 files changed, 89 insertions(+), 12 deletions(-) diff --git a/app/serializers/webfinger_serializer.rb b/app/serializers/webfinger_serializer.rb index 3ca344116..b67cd2771 100644 --- a/app/serializers/webfinger_serializer.rb +++ b/app/serializers/webfinger_serializer.rb @@ -18,18 +18,31 @@ class WebfingerSerializer < ActiveModel::Serializer end def links - if object.instance_actor? - [ - { rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: about_more_url(instance_actor: true) }, - { rel: 'self', type: 'application/activity+json', href: instance_actor_url }, - { rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" }, - ] - else - [ - { rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: short_account_url(object) }, - { rel: 'self', type: 'application/activity+json', href: account_url(object) }, - { rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" }, - ] + [ + { rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: profile_page_href }, + { rel: 'self', type: 'application/activity+json', href: self_href }, + { rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" }, + ].tap do |x| + x << { rel: 'http://webfinger.net/rel/avatar', type: object.avatar.content_type, href: full_asset_url(object.avatar_original_url) } if show_avatar? end end + + private + + def show_avatar? + media_present = object.avatar.present? && object.avatar.content_type.present? + + # Show avatar only if an instance shows profiles to logged out users + allowed_by_config = ENV['DISALLOW_UNAUTHENTICATED_API_ACCESS'] != 'true' && !Rails.configuration.x.limited_federation_mode + + media_present && allowed_by_config + end + + def profile_page_href + object.instance_actor? ? about_more_url(instance_actor: true) : short_account_url(object) + end + + def self_href + object.instance_actor? ? instance_actor_url : account_url(object) + end end diff --git a/spec/controllers/well_known/webfinger_controller_spec.rb b/spec/controllers/well_known/webfinger_controller_spec.rb index 8dc0f329b..20770a721 100644 --- a/spec/controllers/well_known/webfinger_controller_spec.rb +++ b/spec/controllers/well_known/webfinger_controller_spec.rb @@ -3,6 +3,8 @@ require 'rails_helper' describe WellKnown::WebfingerController do + include RoutingHelper + render_views describe 'GET #show' do @@ -167,5 +169,67 @@ describe WellKnown::WebfingerController do expect(response).to have_http_status(400) end end + + context 'when an account has an avatar' do + let(:alice) { Fabricate(:account, username: 'alice', avatar: attachment_fixture('attachment.jpg')) } + let(:resource) { alice.to_webfinger_s } + + it 'returns avatar in response' do + perform_show! + + avatar_link = get_avatar_link(body_as_json) + expect(avatar_link).to_not be_nil + expect(avatar_link[:type]).to eq alice.avatar.content_type + expect(avatar_link[:href]).to eq full_asset_url(alice.avatar) + end + + context 'with limited federation mode' do + before do + allow(Rails.configuration.x).to receive(:limited_federation_mode).and_return(true) + end + + it 'does not return avatar in response' do + perform_show! + + avatar_link = get_avatar_link(body_as_json) + expect(avatar_link).to be_nil + end + end + + context 'when enabling DISALLOW_UNAUTHENTICATED_API_ACCESS' do + around do |example| + ClimateControl.modify DISALLOW_UNAUTHENTICATED_API_ACCESS: 'true' do + example.run + end + end + + it 'does not return avatar in response' do + perform_show! + + avatar_link = get_avatar_link(body_as_json) + expect(avatar_link).to be_nil + end + end + end + + context 'when an account does not have an avatar' do + let(:alice) { Fabricate(:account, username: 'alice', avatar: nil) } + let(:resource) { alice.to_webfinger_s } + + before do + perform_show! + end + + it 'does not return avatar in response' do + avatar_link = get_avatar_link(body_as_json) + expect(avatar_link).to be_nil + end + end + end + + private + + def get_avatar_link(json) + json[:links].find { |link| link[:rel] == 'http://webfinger.net/rel/avatar' } end end From ccca542db1e73610a86c1d34d8b42f3f4a818af9 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Thu, 31 Aug 2023 08:53:24 -0300 Subject: [PATCH 53/81] Fix `/api/v1/timelines/tag/:hashtag` allowing for unauthenticated access when public preview is disabled (#26237) --- .../api/v1/timelines/tag_controller.rb | 5 ++ .../api/v1/timelines/tag_controller_spec.rb | 66 ++++++++++++++----- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/app/controllers/api/v1/timelines/tag_controller.rb b/app/controllers/api/v1/timelines/tag_controller.rb index 9cd7b9904..a79d65c12 100644 --- a/app/controllers/api/v1/timelines/tag_controller.rb +++ b/app/controllers/api/v1/timelines/tag_controller.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Api::V1::Timelines::TagController < Api::BaseController + before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :show, if: :require_auth? before_action :load_tag after_action :insert_pagination_headers, unless: -> { @statuses.empty? } @@ -12,6 +13,10 @@ class Api::V1::Timelines::TagController < Api::BaseController private + def require_auth? + !Setting.timeline_preview + end + def load_tag @tag = Tag.find_normalized(params[:id]) end diff --git a/spec/controllers/api/v1/timelines/tag_controller_spec.rb b/spec/controllers/api/v1/timelines/tag_controller_spec.rb index 718911083..1c60798fc 100644 --- a/spec/controllers/api/v1/timelines/tag_controller_spec.rb +++ b/spec/controllers/api/v1/timelines/tag_controller_spec.rb @@ -5,36 +5,66 @@ require 'rails_helper' describe Api::V1::Timelines::TagController do render_views - let(:user) { Fabricate(:user) } + let(:user) { Fabricate(:user) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses') } before do allow(controller).to receive(:doorkeeper_token) { token } end - context 'with a user context' do - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id) } + describe 'GET #show' do + subject do + get :show, params: { id: 'test' } + end - describe 'GET #show' do - before do - PostStatusService.new.call(user.account, text: 'It is a #test') + before do + PostStatusService.new.call(user.account, text: 'It is a #test') + end + + context 'when the instance allows public preview' do + context 'when the user is not authenticated' do + let(:token) { nil } + + it 'returns http success', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(response.headers['Link'].links.size).to eq(2) + end end - it 'returns http success' do - get :show, params: { id: 'test' } - expect(response).to have_http_status(200) - expect(response.headers['Link'].links.size).to eq(2) + context 'when the user is authenticated' do + it 'returns http success', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(response.headers['Link'].links.size).to eq(2) + end end end - end - context 'without a user context' do - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil) } + context 'when the instance does not allow public preview' do + before do + Form::AdminSettings.new(timeline_preview: false).save + end - describe 'GET #show' do - it 'returns http success' do - get :show, params: { id: 'test' } - expect(response).to have_http_status(200) - expect(response.headers['Link']).to be_nil + context 'when the user is not authenticated' do + let(:token) { nil } + + it 'returns http unauthorized' do + subject + + expect(response).to have_http_status(401) + end + end + + context 'when the user is authenticated' do + it 'returns http success', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(response.headers['Link'].links.size).to eq(2) + end end end end From f1d250135ccf9be5c4d982a2c48417da89d38eb5 Mon Sep 17 00:00:00 2001 From: Santiago Kozak Date: Thu, 31 Aug 2023 09:15:58 -0300 Subject: [PATCH 54/81] Allow filter form in profiles directory to wrap (#26682) --- app/javascript/styles/mastodon/components.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 95e6667b5..f61cd059f 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2381,6 +2381,7 @@ $ui-header-height: 55px; .filter-form { display: flex; + flex-wrap: wrap; } .autosuggest-textarea__textarea { From cffc5d2b0180e2ead23f32391246cdac357bf71b Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Thu, 31 Aug 2023 14:59:50 +0200 Subject: [PATCH 55/81] Fix comment in build image workflow (#26740) --- .github/workflows/build-container-image.yml | 2 -- .github/workflows/build-releases.yml | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-container-image.yml b/.github/workflows/build-container-image.yml index 897bb9caa..a1aeddf20 100644 --- a/.github/workflows/build-container-image.yml +++ b/.github/workflows/build-container-image.yml @@ -76,8 +76,6 @@ jobs: if: ${{ inputs.push_to_images != '' }} with: images: ${{ inputs.push_to_images }} - # Only tag with latest when ran against the latest stable branch - # This needs to be updated after each minor version release flavor: ${{ inputs.flavor }} tags: ${{ inputs.tags }} labels: ${{ inputs.labels }} diff --git a/.github/workflows/build-releases.yml b/.github/workflows/build-releases.yml index b40817468..f739a69d9 100644 --- a/.github/workflows/build-releases.yml +++ b/.github/workflows/build-releases.yml @@ -17,6 +17,8 @@ jobs: push_to_images: | tootsuite/mastodon ghcr.io/mastodon/mastodon + # Only tag with latest when ran against the latest stable branch + # This needs to be updated after each minor version release flavor: | latest=${{ startsWith(github.ref, 'refs/tags/v4.1.') }} tags: | From 6b58cfd8dd2f635dbb1b9d10b6b243d20ba23535 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 31 Aug 2023 15:35:58 +0200 Subject: [PATCH 56/81] Fix searching by username by reverting account verbatim tokenizer to `standard` (#26739) --- app/chewy/accounts_index.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb index 8881b08f6..00db257ac 100644 --- a/app/chewy/accounts_index.rb +++ b/app/chewy/accounts_index.rb @@ -34,7 +34,7 @@ class AccountsIndex < Chewy::Index }, verbatim: { - tokenizer: 'uax_url_email', + tokenizer: 'standard', filter: %w(lowercase asciifolding cjk_width), }, From 1471be82255d948460b0b653dd48cdfbc67d90cf Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 31 Aug 2023 15:47:30 +0200 Subject: [PATCH 57/81] Fix `AddUniqueIndexOnPreviewCardsStatuses` migration requiring PostgreSQL 12+ in some cases (#26737) --- ...1_add_unique_index_on_preview_cards_statuses.rb | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/db/post_migrate/20230803082451_add_unique_index_on_preview_cards_statuses.rb b/db/post_migrate/20230803082451_add_unique_index_on_preview_cards_statuses.rb index c35ad8002..3e9ab134b 100644 --- a/db/post_migrate/20230803082451_add_unique_index_on_preview_cards_statuses.rb +++ b/db/post_migrate/20230803082451_add_unique_index_on_preview_cards_statuses.rb @@ -15,10 +15,22 @@ class AddUniqueIndexOnPreviewCardsStatuses < ActiveRecord::Migration[6.1] private + def supports_concurrent_reindex? + @supports_concurrent_reindex ||= begin + version = select_one("SELECT current_setting('server_version_num') AS v")['v'].to_i + version >= 12_000 + end + end + def deduplicate_and_reindex! deduplicate_preview_cards! - safety_assured { execute 'REINDEX INDEX CONCURRENTLY preview_cards_statuses_pkey' } + if supports_concurrent_reindex? + safety_assured { execute 'REINDEX INDEX CONCURRENTLY preview_cards_statuses_pkey' } + else + remove_index :preview_cards_statuses, name: :preview_cards_statuses_pkey + add_index :preview_cards_statuses, [:status_id, :preview_card_id], name: :preview_cards_statuses_pkey, algorithm: :concurrently, unique: true + end rescue ActiveRecord::RecordNotUnique retry end From ecd76fa413e31d4eb26e09fa4b65f8b13bbbb0b7 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 31 Aug 2023 17:21:06 +0200 Subject: [PATCH 58/81] Fix videos not playing in some browsers due to unsupported color space (#26745) --- app/models/media_attachment.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index 88abd9339..984f4252a 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -100,6 +100,8 @@ class MediaAttachment < ApplicationRecord output: { 'loglevel' => 'fatal', 'preset' => 'veryfast', + 'movflags' => 'faststart', # Move metadata to start of file so playback can begin before download finishes + 'pix_fmt' => 'yuv420p', # Ensure color space for cross-browser compatibility 'c:v' => 'h264', 'c:a' => 'aac', 'b:a' => '192k', From 9bb2fb6b1484c90c5b2c6cc52ce148019e82a3e2 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 31 Aug 2023 19:04:27 +0200 Subject: [PATCH 59/81] Change importers to avoid a few inefficiencies (#26721) --- app/lib/importer/accounts_index_importer.rb | 6 ++-- app/lib/importer/base_importer.rb | 8 +++++ app/lib/importer/instances_index_importer.rb | 6 ++-- .../public_statuses_index_importer.rb | 6 ++-- app/lib/importer/statuses_index_importer.rb | 35 ++++++++----------- app/lib/importer/tags_index_importer.rb | 6 ++-- 6 files changed, 34 insertions(+), 33 deletions(-) diff --git a/app/lib/importer/accounts_index_importer.rb b/app/lib/importer/accounts_index_importer.rb index fd869c396..d8b919027 100644 --- a/app/lib/importer/accounts_index_importer.rb +++ b/app/lib/importer/accounts_index_importer.rb @@ -4,10 +4,10 @@ class Importer::AccountsIndexImporter < Importer::BaseImporter def import! scope.includes(:account_stat).find_in_batches(batch_size: @batch_size) do |tmp| in_work_unit(tmp) do |accounts| - bulk = Chewy::Index::Import::BulkBuilder.new(index, to_index: accounts).bulk_body + bulk = build_bulk_body(accounts) - indexed = bulk.count { |entry| entry[:index] } - deleted = bulk.count { |entry| entry[:delete] } + indexed = bulk.size + deleted = 0 Chewy::Index::Import::BulkRequest.new(index).perform(bulk) diff --git a/app/lib/importer/base_importer.rb b/app/lib/importer/base_importer.rb index cc1b7b44d..a21557d30 100644 --- a/app/lib/importer/base_importer.rb +++ b/app/lib/importer/base_importer.rb @@ -68,6 +68,14 @@ class Importer::BaseImporter protected + def build_bulk_body(to_import) + # Specialize `Chewy::Index::Import::BulkBuilder#bulk_body` to avoid a few + # inefficiencies, as none of our fields or join fields and we do not need + # `BulkBuilder`'s versatility. + crutches = Chewy::Index::Crutch::Crutches.new index, to_import + to_import.map { |object| { index: { _id: object.id, data: index.compose(object, crutches, fields: []) } } } + end + def in_work_unit(...) work_unit = Concurrent::Promises.future_on(@executor, ...) diff --git a/app/lib/importer/instances_index_importer.rb b/app/lib/importer/instances_index_importer.rb index 7318b51b5..ebdceb72e 100644 --- a/app/lib/importer/instances_index_importer.rb +++ b/app/lib/importer/instances_index_importer.rb @@ -4,10 +4,10 @@ class Importer::InstancesIndexImporter < Importer::BaseImporter def import! index.adapter.default_scope.find_in_batches(batch_size: @batch_size) do |tmp| in_work_unit(tmp) do |instances| - bulk = Chewy::Index::Import::BulkBuilder.new(index, to_index: instances).bulk_body + bulk = build_bulk_body(instances) - indexed = bulk.count { |entry| entry[:index] } - deleted = bulk.count { |entry| entry[:delete] } + indexed = bulk.size + deleted = 0 Chewy::Index::Import::BulkRequest.new(index).perform(bulk) diff --git a/app/lib/importer/public_statuses_index_importer.rb b/app/lib/importer/public_statuses_index_importer.rb index 72d02318b..ebaac3794 100644 --- a/app/lib/importer/public_statuses_index_importer.rb +++ b/app/lib/importer/public_statuses_index_importer.rb @@ -5,11 +5,11 @@ class Importer::PublicStatusesIndexImporter < Importer::BaseImporter scope.select(:id).find_in_batches(batch_size: @batch_size) do |batch| in_work_unit(batch.pluck(:id)) do |status_ids| bulk = ActiveRecord::Base.connection_pool.with_connection do - Chewy::Index::Import::BulkBuilder.new(index, to_index: Status.includes(:media_attachments, :preloadable_poll, :preview_cards).where(id: status_ids)).bulk_body + build_bulk_body(index.adapter.default_scope.where(id: status_ids)) end - indexed = bulk.count { |entry| entry[:index] } - deleted = bulk.count { |entry| entry[:delete] } + indexed = bulk.size + deleted = 0 Chewy::Index::Import::BulkRequest.new(index).perform(bulk) diff --git a/app/lib/importer/statuses_index_importer.rb b/app/lib/importer/statuses_index_importer.rb index 285ddc871..08ad3e379 100644 --- a/app/lib/importer/statuses_index_importer.rb +++ b/app/lib/importer/statuses_index_importer.rb @@ -13,32 +13,25 @@ class Importer::StatusesIndexImporter < Importer::BaseImporter scope.find_in_batches(batch_size: @batch_size) do |tmp| in_work_unit(tmp.map(&:status_id)) do |status_ids| - bulk = ActiveRecord::Base.connection_pool.with_connection do - Chewy::Index::Import::BulkBuilder.new(index, to_index: index.adapter.default_scope.where(id: status_ids)).bulk_body - end - - indexed = 0 deleted = 0 - # We can't use the delete_if proc to do the filtering because delete_if - # is called before rendering the data and we need to filter based - # on the results of the filter, so this filtering happens here instead - bulk.map! do |entry| - new_entry = if entry[:index] && entry.dig(:index, :data, 'searchable_by').blank? - { delete: entry[:index].except(:data) } - else - entry - end - - if new_entry[:index] - indexed += 1 - else - deleted += 1 + bulk = ActiveRecord::Base.connection_pool.with_connection do + to_index = index.adapter.default_scope.where(id: status_ids) + crutches = Chewy::Index::Crutch::Crutches.new index, to_index + to_index.map do |object| + # This is unlikely to happen, but the post may have been + # un-interacted with since it was queued for indexing + if object.searchable_by.empty? + deleted += 1 + { delete: { _id: object.id } } + else + { index: { _id: object.id, data: index.compose(object, crutches, fields: []) } } + end end - - new_entry end + indexed = bulk.size - deleted + Chewy::Index::Import::BulkRequest.new(index).perform(bulk) [indexed, deleted] diff --git a/app/lib/importer/tags_index_importer.rb b/app/lib/importer/tags_index_importer.rb index 77710ed7d..067fd8cd2 100644 --- a/app/lib/importer/tags_index_importer.rb +++ b/app/lib/importer/tags_index_importer.rb @@ -4,10 +4,10 @@ class Importer::TagsIndexImporter < Importer::BaseImporter def import! index.adapter.default_scope.find_in_batches(batch_size: @batch_size) do |tmp| in_work_unit(tmp) do |tags| - bulk = Chewy::Index::Import::BulkBuilder.new(index, to_index: tags).bulk_body + bulk = build_bulk_body(tags) - indexed = bulk.count { |entry| entry[:index] } - deleted = bulk.count { |entry| entry[:delete] } + indexed = bulk.size + deleted = 0 Chewy::Index::Import::BulkRequest.new(index).perform(bulk) From 00084581289b4b7afd120845363b16247c5fa93b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 31 Aug 2023 19:04:44 +0200 Subject: [PATCH 60/81] Fix search queries with slash causing or-condition (#26699) --- app/lib/search_query_transformer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index 86e3f5000..f10ccfb28 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -25,7 +25,7 @@ class SearchQueryTransformer < Parslet::Transform def clause_to_query(clause) case clause when TermClause - { multi_match: { type: 'most_fields', query: clause.term, fields: ['text', 'text.stemmed'] } } + { multi_match: { type: 'most_fields', query: clause.term, fields: ['text', 'text.stemmed'], operator: 'and' } } when PhraseClause { match_phrase: { text: { query: clause.phrase } } } else From 872145d1c2aec709770de10579d2a4f83c601f37 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 1 Sep 2023 09:27:03 +0200 Subject: [PATCH 61/81] Fix not being able to invoke phrase search using unicode quotation marks (#26687) --- app/services/search_service.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 40d82fc52..9a40d7bdd 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -1,8 +1,10 @@ # frozen_string_literal: true class SearchService < BaseService + QUOTE_EQUIVALENT_CHARACTERS = /[“”„«»「」『』《》]/ + def call(query, account, limit, options = {}) - @query = query&.strip + @query = query&.strip&.gsub(QUOTE_EQUIVALENT_CHARACTERS, '"') @account = account @options = options @limit = limit.to_i From e754083e8a33778b5dd8d43efc5604ed50efb0e4 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 1 Sep 2023 09:43:12 +0200 Subject: [PATCH 62/81] Fix unmatched quotes and prefixes causing search to fail (#26701) --- app/lib/search_query_parser.rb | 4 +- app/lib/search_query_transformer.rb | 101 +++++++++++----------- spec/lib/search_query_parser_spec.rb | 98 +++++++++++++++++++++ spec/lib/search_query_transformer_spec.rb | 57 ++++++++++-- 4 files changed, 200 insertions(+), 60 deletions(-) create mode 100644 spec/lib/search_query_parser_spec.rb diff --git a/app/lib/search_query_parser.rb b/app/lib/search_query_parser.rb index 5d6ffbf29..1c57b9b02 100644 --- a/app/lib/search_query_parser.rb +++ b/app/lib/search_query_parser.rb @@ -6,10 +6,10 @@ class SearchQueryParser < Parslet::Parser rule(:colon) { str(':') } rule(:space) { match('\s').repeat(1) } rule(:operator) { (str('+') | str('-')).as(:operator) } - rule(:prefix) { (term >> colon).as(:prefix) } + rule(:prefix) { term >> colon } rule(:shortcode) { (colon >> term >> colon.maybe).as(:shortcode) } rule(:phrase) { (quote >> (term >> space.maybe).repeat >> quote).as(:phrase) } - rule(:clause) { (operator.maybe >> prefix.maybe >> (phrase | term | shortcode)).as(:clause) } + rule(:clause) { (operator.maybe >> prefix.maybe.as(:prefix) >> (phrase | term | shortcode)).as(:clause) | prefix.as(:clause) | quote.as(:junk) } rule(:query) { (clause >> space.maybe).repeat.as(:query) } root(:query) end diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index f10ccfb28..e81c0c081 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -1,50 +1,32 @@ # frozen_string_literal: true class SearchQueryTransformer < Parslet::Transform + SUPPORTED_PREFIXES = %w( + has + is + language + from + before + after + during + ).freeze + class Query - attr_reader :should_clauses, :must_not_clauses, :must_clauses, :filter_clauses + attr_reader :must_not_clauses, :must_clauses, :filter_clauses def initialize(clauses) - grouped = clauses.chunk(&:operator).to_h - @should_clauses = grouped.fetch(:should, []) + grouped = clauses.compact.chunk(&:operator).to_h @must_not_clauses = grouped.fetch(:must_not, []) @must_clauses = grouped.fetch(:must, []) @filter_clauses = grouped.fetch(:filter, []) end def apply(search) - should_clauses.each { |clause| search = search.query.should(clause_to_query(clause)) } - must_clauses.each { |clause| search = search.query.must(clause_to_query(clause)) } - must_not_clauses.each { |clause| search = search.query.must_not(clause_to_query(clause)) } - filter_clauses.each { |clause| search = search.filter(**clause_to_filter(clause)) } + must_clauses.each { |clause| search = search.query.must(clause.to_query) } + must_not_clauses.each { |clause| search = search.query.must_not(clause.to_query) } + filter_clauses.each { |clause| search = search.filter(**clause.to_query) } search.query.minimum_should_match(1) end - - private - - def clause_to_query(clause) - case clause - when TermClause - { multi_match: { type: 'most_fields', query: clause.term, fields: ['text', 'text.stemmed'], operator: 'and' } } - when PhraseClause - { match_phrase: { text: { query: clause.phrase } } } - else - raise "Unexpected clause type: #{clause}" - end - end - - def clause_to_filter(clause) - case clause - when PrefixClause - if clause.negated? - { bool: { must_not: { clause.type => { clause.filter => clause.term } } } } - else - { clause.type => { clause.filter => clause.term } } - end - else - raise "Unexpected clause type: #{clause}" - end - end end class Operator @@ -63,31 +45,38 @@ class SearchQueryTransformer < Parslet::Transform end class TermClause - attr_reader :prefix, :operator, :term + attr_reader :operator, :term - def initialize(prefix, operator, term) - @prefix = prefix + def initialize(operator, term) @operator = Operator.symbol(operator) @term = term end + + def to_query + { multi_match: { type: 'most_fields', query: @term, fields: ['text', 'text.stemmed'], operator: 'and' } } + end end class PhraseClause - attr_reader :prefix, :operator, :phrase + attr_reader :operator, :phrase - def initialize(prefix, operator, phrase) - @prefix = prefix + def initialize(operator, phrase) @operator = Operator.symbol(operator) @phrase = phrase end + + def to_query + { match_phrase: { text: { query: @phrase } } } + end end class PrefixClause - attr_reader :type, :filter, :operator, :term + attr_reader :operator, :prefix, :term def initialize(prefix, operator, term, options = {}) - @negated = operator == '-' - @options = options + @prefix = prefix + @negated = operator == '-' + @options = options @operator = :filter case prefix @@ -116,12 +105,16 @@ class SearchQueryTransformer < Parslet::Transform @type = :range @term = { gte: term, lte: term, time_zone: @options[:current_account]&.user_time_zone || 'UTC' } else - raise Mastodon::SyntaxError + raise "Unknown prefix: #{prefix}" end end - def negated? - @negated + def to_query + if @negated + { bool: { must_not: { @type => { @filter => @term } } } } + else + { @type => { @filter => @term } } + end end private @@ -159,18 +152,26 @@ class SearchQueryTransformer < Parslet::Transform prefix = clause[:prefix][:term].to_s if clause[:prefix] operator = clause[:operator]&.to_s - if clause[:prefix] + if clause[:prefix] && SUPPORTED_PREFIXES.include?(prefix) PrefixClause.new(prefix, operator, clause[:term].to_s, current_account: current_account) + elsif clause[:prefix] + TermClause.new(operator, "#{prefix} #{clause[:term]}") elsif clause[:term] - TermClause.new(prefix, operator, clause[:term].to_s) + TermClause.new(operator, clause[:term].to_s) elsif clause[:shortcode] - TermClause.new(prefix, operator, ":#{clause[:term]}:") + TermClause.new(operator, ":#{clause[:term]}:") elsif clause[:phrase] - PhraseClause.new(prefix, operator, clause[:phrase].is_a?(Array) ? clause[:phrase].map { |p| p[:term].to_s }.join(' ') : clause[:phrase].to_s) + PhraseClause.new(operator, clause[:phrase].is_a?(Array) ? clause[:phrase].map { |p| p[:term].to_s }.join(' ') : clause[:phrase].to_s) else raise "Unexpected clause type: #{clause}" end end - rule(query: sequence(:clauses)) { Query.new(clauses) } + rule(junk: subtree(:junk)) do + nil + end + + rule(query: sequence(:clauses)) do + Query.new(clauses) + end end diff --git a/spec/lib/search_query_parser_spec.rb b/spec/lib/search_query_parser_spec.rb new file mode 100644 index 000000000..66b0e8f9e --- /dev/null +++ b/spec/lib/search_query_parser_spec.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'parslet/rig/rspec' + +describe SearchQueryParser do + let(:parser) { described_class.new } + + context 'with term' do + it 'consumes "hello"' do + expect(parser.term).to parse('hello') + end + end + + context 'with prefix' do + it 'consumes "foo:"' do + expect(parser.prefix).to parse('foo:') + end + end + + context 'with operator' do + it 'consumes "+"' do + expect(parser.operator).to parse('+') + end + + it 'consumes "-"' do + expect(parser.operator).to parse('-') + end + end + + context 'with shortcode' do + it 'consumes ":foo:"' do + expect(parser.shortcode).to parse(':foo:') + end + end + + context 'with phrase' do + it 'consumes "hello world"' do + expect(parser.phrase).to parse('"hello world"') + end + end + + context 'with clause' do + it 'consumes "foo"' do + expect(parser.clause).to parse('foo') + end + + it 'consumes "-foo"' do + expect(parser.clause).to parse('-foo') + end + + it 'consumes "foo:bar"' do + expect(parser.clause).to parse('foo:bar') + end + + it 'consumes "-foo:bar"' do + expect(parser.clause).to parse('-foo:bar') + end + + it 'consumes \'foo:"hello world"\'' do + expect(parser.clause).to parse('foo:"hello world"') + end + + it 'consumes \'-foo:"hello world"\'' do + expect(parser.clause).to parse('-foo:"hello world"') + end + + it 'consumes "foo:"' do + expect(parser.clause).to parse('foo:') + end + + it 'consumes \'"\'' do + expect(parser.clause).to parse('"') + end + end + + context 'with query' do + it 'consumes "hello -world"' do + expect(parser.query).to parse('hello -world') + end + + it 'consumes \'foo "hello world"\'' do + expect(parser.query).to parse('foo "hello world"') + end + + it 'consumes "foo:bar hello"' do + expect(parser.query).to parse('foo:bar hello') + end + + it 'consumes \'"hello" world "\'' do + expect(parser.query).to parse('"hello" world "') + end + + it 'consumes "foo:bar bar: hello"' do + expect(parser.query).to parse('foo:bar bar: hello') + end + end +end diff --git a/spec/lib/search_query_transformer_spec.rb b/spec/lib/search_query_transformer_spec.rb index 953f9acb2..17f06d283 100644 --- a/spec/lib/search_query_transformer_spec.rb +++ b/spec/lib/search_query_transformer_spec.rb @@ -3,16 +3,57 @@ require 'rails_helper' describe SearchQueryTransformer do - describe 'initialization' do - let(:parser) { SearchQueryParser.new.parse('query') } + subject { described_class.new.apply(parser, current_account: nil) } - it 'sets attributes' do - transformer = described_class.new.apply(parser) + let(:parser) { SearchQueryParser.new.parse(query) } - expect(transformer.should_clauses.first).to be_nil - expect(transformer.must_clauses.first).to be_a(SearchQueryTransformer::TermClause) - expect(transformer.must_not_clauses.first).to be_nil - expect(transformer.filter_clauses.first).to be_nil + context 'with "hello world"' do + let(:query) { 'hello world' } + + it 'transforms clauses' do + expect(subject.must_clauses.map(&:term)).to match_array %w(hello world) + expect(subject.must_not_clauses).to be_empty + expect(subject.filter_clauses).to be_empty + end + end + + context 'with "hello -world"' do + let(:query) { 'hello -world' } + + it 'transforms clauses' do + expect(subject.must_clauses.map(&:term)).to match_array %w(hello) + expect(subject.must_not_clauses.map(&:term)).to match_array %w(world) + expect(subject.filter_clauses).to be_empty + end + end + + context 'with "hello is:reply"' do + let(:query) { 'hello is:reply' } + + it 'transforms clauses' do + expect(subject.must_clauses.map(&:term)).to match_array %w(hello) + expect(subject.must_not_clauses).to be_empty + expect(subject.filter_clauses.map(&:term)).to match_array %w(reply) + end + end + + context 'with "foo: bar"' do + let(:query) { 'foo: bar' } + + it 'transforms clauses' do + expect(subject.must_clauses.map(&:term)).to match_array %w(foo bar) + expect(subject.must_not_clauses).to be_empty + expect(subject.filter_clauses).to be_empty + end + end + + context 'with "foo:bar"' do + let(:query) { 'foo:bar' } + + it 'transforms clauses' do + expect(subject.must_clauses.map(&:term)).to contain_exactly('foo bar') + expect(subject.must_not_clauses).to be_empty + expect(subject.filter_clauses).to be_empty end end end From 630e558677d961e74ce82ab5e2a62a0760bbe815 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 10:17:08 +0200 Subject: [PATCH 63/81] Update dependency oj to v3.16.1 (#26749) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index f1a61c5e0..6dd8c567f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -482,7 +482,7 @@ GEM nokogiri (1.15.4) mini_portile2 (~> 2.8.2) racc (~> 1.4) - oj (3.16.0) + oj (3.16.1) omniauth (2.1.1) hashie (>= 3.4.6) rack (>= 2.2.3) From bb0edb178f56a488017071e00a994c9033267ef9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 10:48:49 +0200 Subject: [PATCH 64/81] Update dependency pg to v1.5.4 (#26750) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6dd8c567f..4e30c4222 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -519,7 +519,7 @@ GEM parslet (2.0.0) pastel (0.8.0) tty-color (~> 0.5) - pg (1.5.3) + pg (1.5.4) pghero (3.3.3) activerecord (>= 6) posix-spawn (0.3.15) From 5c0a9aac3b2add81ff2a9c7e724dff4eb9c6451b Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 1 Sep 2023 15:09:44 +0200 Subject: [PATCH 65/81] Revert to using primary database in IndexingScheduler (#26754) --- app/workers/scheduler/indexing_scheduler.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/workers/scheduler/indexing_scheduler.rb b/app/workers/scheduler/indexing_scheduler.rb index 1b09730c7..ff1b74444 100644 --- a/app/workers/scheduler/indexing_scheduler.rb +++ b/app/workers/scheduler/indexing_scheduler.rb @@ -16,9 +16,7 @@ class Scheduler::IndexingScheduler indexes.each do |type| with_redis do |redis| redis.sscan_each("chewy:queue:#{type.name}", count: SCAN_BATCH_SIZE).each_slice(IMPORT_BATCH_SIZE) do |ids| - with_read_replica do - type.import!(ids) - end + type.import!(ids) redis.srem("chewy:queue:#{type.name}", ids) end From 6c4c72497a5722870e4432ef41dd4c9ec36a8928 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 1 Sep 2023 15:13:27 +0200 Subject: [PATCH 66/81] Fix search popout including full-text search instructions when full-text search is disabled (#26755) --- .../features/compose/components/search.jsx | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/app/javascript/mastodon/features/compose/components/search.jsx b/app/javascript/mastodon/features/compose/components/search.jsx index 1c629bcbb..848b81263 100644 --- a/app/javascript/mastodon/features/compose/components/search.jsx +++ b/app/javascript/mastodon/features/compose/components/search.jsx @@ -80,7 +80,7 @@ class Search extends PureComponent { handleKeyDown = (e) => { const { selectedOption } = this.state; - const options = this._getOptions().concat(this.defaultOptions); + const options = searchEnabled ? this._getOptions().concat(this.defaultOptions) : this._getOptions(); switch(e.key) { case 'Escape': @@ -353,15 +353,19 @@ class Search extends PureComponent { )} -

+ {searchEnabled && ( + <> +

-
- {this.defaultOptions.map(({ key, label, action }, i) => ( - - ))} -
+
+ {this.defaultOptions.map(({ key, label, action }, i) => ( + + ))} +
+ + )}
); From 9e26cd55038084638fdf71b75f526494777d2849 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 1 Sep 2023 15:41:10 +0200 Subject: [PATCH 67/81] Add `authorized_fetch` server setting in addition to env var (#25798) --- app/controllers/application_controller.rb | 5 +---- app/helpers/authorized_fetch_helper.rb | 11 +++++++++++ app/javascript/styles/mastodon/accounts.scss | 2 ++ app/javascript/styles/mastodon/forms.scss | 1 + app/models/form/admin_settings.rb | 10 ++++++++++ app/services/concerns/payloadable.rb | 4 +++- app/views/admin/settings/discovery/show.html.haml | 5 +++++ config/i18n-tasks.yml | 2 +- config/initializers/simple_form.rb | 5 +++-- config/locales/en.yml | 5 +++++ config/locales/simple_form.en.yml | 1 + 11 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 app/helpers/authorized_fetch_helper.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 975315e24..6ec93f824 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -11,6 +11,7 @@ class ApplicationController < ActionController::Base include CacheConcern include DomainControlHelper include DatabaseHelper + include AuthorizedFetchHelper helper_method :current_account helper_method :current_session @@ -51,10 +52,6 @@ class ApplicationController < ActionController::Base private - def authorized_fetch_mode? - ENV['AUTHORIZED_FETCH'] == 'true' || Rails.configuration.x.limited_federation_mode - end - def public_fetch_mode? !authorized_fetch_mode? end diff --git a/app/helpers/authorized_fetch_helper.rb b/app/helpers/authorized_fetch_helper.rb new file mode 100644 index 000000000..ce87526e6 --- /dev/null +++ b/app/helpers/authorized_fetch_helper.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module AuthorizedFetchHelper + def authorized_fetch_mode? + ENV.fetch('AUTHORIZED_FETCH') { Setting.authorized_fetch } == 'true' || Rails.configuration.x.limited_federation_mode + end + + def authorized_fetch_overridden? + ENV.key?('AUTHORIZED_FETCH') || Rails.configuration.x.limited_federation_mode + end +end diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss index 2a5285ee0..80d6c13ce 100644 --- a/app/javascript/styles/mastodon/accounts.scss +++ b/app/javascript/styles/mastodon/accounts.scss @@ -188,6 +188,7 @@ } .information-badge, +.simple_form .overridden, .simple_form .recommended, .simple_form .not_recommended { display: inline-block; @@ -204,6 +205,7 @@ } .information-badge, +.simple_form .overridden, .simple_form .recommended, .simple_form .not_recommended { background-color: rgba($ui-secondary-color, 0.1); diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index a7079c145..0f8eecee0 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -103,6 +103,7 @@ code { } } + .overridden, .recommended, .not_recommended { position: absolute; diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index a6be55fd7..7be026d85 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -3,6 +3,8 @@ class Form::AdminSettings include ActiveModel::Model + include AuthorizedFetchHelper + KEYS = %i( site_contact_username site_contact_email @@ -34,6 +36,7 @@ class Form::AdminSettings backups_retention_period status_page_url captcha_enabled + authorized_fetch ).freeze INTEGER_KEYS = %i( @@ -54,6 +57,7 @@ class Form::AdminSettings noindex require_invite_text captcha_enabled + authorized_fetch ).freeze UPLOAD_KEYS = %i( @@ -61,6 +65,10 @@ class Form::AdminSettings mascot ).freeze + OVERRIDEN_SETTINGS = { + authorized_fetch: :authorized_fetch_mode?, + }.freeze + attr_accessor(*KEYS) validates :registrations_mode, inclusion: { in: %w(open approved none) }, if: -> { defined?(@registrations_mode) } @@ -80,6 +88,8 @@ class Form::AdminSettings stored_value = if UPLOAD_KEYS.include?(key) SiteUpload.where(var: key).first_or_initialize(var: key) + elsif OVERRIDEN_SETTINGS.include?(key) + public_send(OVERRIDEN_SETTINGS[key]) else Setting.public_send(key) end diff --git a/app/services/concerns/payloadable.rb b/app/services/concerns/payloadable.rb index 1389a42ed..bd9d9d74b 100644 --- a/app/services/concerns/payloadable.rb +++ b/app/services/concerns/payloadable.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true module Payloadable + include AuthorizedFetchHelper + # @param [ActiveModelSerializers::Model] record # @param [ActiveModelSerializers::Serializer] serializer # @param [Hash] options @@ -23,6 +25,6 @@ module Payloadable end def signing_enabled? - ENV['AUTHORIZED_FETCH'] != 'true' && !Rails.configuration.x.limited_federation_mode + !authorized_fetch_mode? end end diff --git a/app/views/admin/settings/discovery/show.html.haml b/app/views/admin/settings/discovery/show.html.haml index c48e0fdc6..62011d5c5 100644 --- a/app/views/admin/settings/discovery/show.html.haml +++ b/app/views/admin/settings/discovery/show.html.haml @@ -39,6 +39,11 @@ .fields-group = f.input :peers_api_enabled, as: :boolean, wrapper: :with_label, recommended: :recommended + %h4= t('admin.settings.security.federation_authentication') + + .fields-group + = f.input :authorized_fetch, as: :boolean, wrapper: :with_label, label: t('admin.settings.security.authorized_fetch'), warning_hint: authorized_fetch_overridden? ? t('admin.settings.security.authorized_fetch_overridden_hint') : nil, hint: t('admin.settings.security.authorized_fetch_hint'), disabled: authorized_fetch_overridden?, recommended: authorized_fetch_overridden? ? :overridden : nil + %h4= t('admin.settings.discovery.follow_recommendations') .fields-group diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index d0677b80f..2d4487ce5 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -50,7 +50,7 @@ ignore_unused: - 'activerecord.errors.*' - '{devise,pagination,doorkeeper}.*' - '{date,datetime,time,number}.*' - - 'simple_form.{yes,no,recommended,not_recommended}' + - 'simple_form.{yes,no,recommended,not_recommended,overridden}' - 'simple_form.{placeholders,hints,labels}.*' - 'simple_form.{error_notification,required}.:' - 'errors.messages.*' diff --git a/config/initializers/simple_form.rb b/config/initializers/simple_form.rb index 9d90cc6ca..a7a2d251e 100644 --- a/config/initializers/simple_form.rb +++ b/config/initializers/simple_form.rb @@ -97,7 +97,8 @@ SimpleForm.setup do |config| end end - b.use :hint, wrap_with: { tag: :span, class: :hint } + b.use :warning_hint, wrap_with: { tag: :span, class: [:hint, 'warning-hint'] } + b.use :hint, wrap_with: { tag: :span, class: :hint } b.use :error, wrap_with: { tag: :span, class: :error } end @@ -111,8 +112,8 @@ SimpleForm.setup do |config| config.wrappers :with_block_label, class: [:input, :with_block_label], hint_class: :field_with_hint, error_class: :field_with_errors do |b| b.use :html5 b.use :label - b.use :hint, wrap_with: { tag: :span, class: :hint } b.use :warning_hint, wrap_with: { tag: :span, class: [:hint, 'warning-hint'] } + b.use :hint, wrap_with: { tag: :span, class: :hint } b.use :input, wrap_with: { tag: :div, class: :label_input } b.use :error, wrap_with: { tag: :span, class: :error } end diff --git a/config/locales/en.yml b/config/locales/en.yml index 8bdfd1ec9..693155d6e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -770,6 +770,11 @@ en: approved: Approval required for sign up none: Nobody can sign up open: Anyone can sign up + security: + authorized_fetch: Require authentication from federated servers + authorized_fetch_hint: Requiring authentication from federated servers enables stricter enforcement of both user-level and server-level blocks. However, this comes at the cost of a performance penalty, reduces the reach of your replies, and may introduce compatibility issues with some federated services. In addition, this will not prevent dedicated actors from fetching your public posts and accounts. + authorized_fetch_overridden_hint: You are currently unable to change this setting because it is overridden by an environment variable. + federation_authentication: Federation authentication enforcement title: Server settings site_uploads: delete: Delete uploaded file diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index efda7b778..b1297606b 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -317,6 +317,7 @@ en: url: Endpoint URL 'no': 'No' not_recommended: Not recommended + overridden: Overridden recommended: Recommended required: mark: "*" From be991f1d18006a4820c1e9ca6625bf2bd2bfedac Mon Sep 17 00:00:00 2001 From: Gabriel Simmer Date: Fri, 1 Sep 2023 16:44:28 +0100 Subject: [PATCH 68/81] Move to ioredis for streaming (#26581) Co-authored-by: Emelia Smith --- package.json | 2 +- streaming/index.js | 82 +++++++++++++++++------------------- yarn.lock | 101 +++++++++++++++++++++------------------------ 3 files changed, 85 insertions(+), 100 deletions(-) diff --git a/package.json b/package.json index 403883105..e92115610 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "immutable": "^4.3.0", "imports-loader": "^1.2.0", "intl-messageformat": "^10.3.5", + "ioredis": "^5.3.2", "js-yaml": "^4.1.0", "jsdom": "^22.1.0", "lodash": "^4.17.21", @@ -118,7 +119,6 @@ "react-swipeable-views": "^0.14.0", "react-textarea-autosize": "^8.4.1", "react-toggle": "^4.1.3", - "redis": "^4.6.5", "redux": "^4.2.1", "redux-immutable": "^4.0.0", "redux-thunk": "^2.4.2", diff --git a/streaming/index.js b/streaming/index.js index a241fa328..c9fac063d 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -6,12 +6,12 @@ const url = require('url'); const dotenv = require('dotenv'); const express = require('express'); +const Redis = require('ioredis'); const { JSDOM } = require('jsdom'); const log = require('npmlog'); const pg = require('pg'); const dbUrlToConfig = require('pg-connection-string').parse; const metrics = require('prom-client'); -const redis = require('redis'); const uuid = require('uuid'); const WebSocket = require('ws'); @@ -24,30 +24,12 @@ dotenv.config({ log.level = process.env.LOG_LEVEL || 'verbose'; /** - * @param {Object.} defaultConfig - * @param {string} redisUrl + * @param {Object.} config */ -const redisUrlToClient = async (defaultConfig, redisUrl) => { - const config = defaultConfig; - - let client; - - if (!redisUrl) { - client = redis.createClient(config); - } else if (redisUrl.startsWith('unix://')) { - client = redis.createClient(Object.assign(config, { - socket: { - path: redisUrl.slice(7), - }, - })); - } else { - client = redis.createClient(Object.assign(config, { - url: redisUrl, - })); - } - +const createRedisClient = async (config) => { + const { redisParams, redisUrl } = config; + const client = new Redis(redisUrl, redisParams); client.on('error', (err) => log.error('Redis Client Error!', err)); - await client.connect(); return client; }; @@ -147,23 +129,22 @@ const pgConfigFromEnv = (env) => { * @returns {Object.} configuration for the Redis connection */ const redisConfigFromEnv = (env) => { - const redisNamespace = env.REDIS_NAMESPACE || null; + // ioredis *can* transparently add prefixes for us, but it doesn't *in some cases*, + // which means we can't use it. But this is something that should be looked into. + const redisPrefix = env.REDIS_NAMESPACE ? `${env.REDIS_NAMESPACE}:` : ''; const redisParams = { - socket: { - host: env.REDIS_HOST || '127.0.0.1', - port: env.REDIS_PORT || 6379, - }, - database: env.REDIS_DB || 0, + host: env.REDIS_HOST || '127.0.0.1', + port: env.REDIS_PORT || 6379, + db: env.REDIS_DB || 0, password: env.REDIS_PASSWORD || undefined, }; - if (redisNamespace) { - redisParams.namespace = redisNamespace; + // redisParams.path takes precedence over host and port. + if (env.REDIS_URL && env.REDIS_URL.startsWith('unix://')) { + redisParams.path = env.REDIS_URL.slice(7); } - const redisPrefix = redisNamespace ? `${redisNamespace}:` : ''; - return { redisParams, redisPrefix, @@ -179,15 +160,15 @@ const startServer = async () => { const pgPool = new pg.Pool(pgConfigFromEnv(process.env)); const server = http.createServer(app); - const { redisParams, redisUrl, redisPrefix } = redisConfigFromEnv(process.env); - /** * @type {Object.): void>>} */ const subs = {}; - const redisSubscribeClient = await redisUrlToClient(redisParams, redisUrl); - const redisClient = await redisUrlToClient(redisParams, redisUrl); + const redisConfig = redisConfigFromEnv(process.env); + const redisSubscribeClient = await createRedisClient(redisConfig); + const redisClient = await createRedisClient(redisConfig); + const { redisPrefix } = redisConfig; // Collect metrics from Node.js metrics.collectDefaultMetrics(); @@ -277,13 +258,13 @@ const startServer = async () => { }; /** - * @param {string} message * @param {string} channel + * @param {string} message */ - const onRedisMessage = (message, channel) => { + const onRedisMessage = (channel, message) => { const callbacks = subs[channel]; - log.silly(`New message on channel ${channel}`); + log.silly(`New message on channel ${redisPrefix}${channel}`); if (!callbacks) { return; @@ -294,6 +275,7 @@ const startServer = async () => { callbacks.forEach(callback => callback(json)); }; + redisSubscribeClient.on("message", onRedisMessage); /** * @callback SubscriptionListener @@ -312,8 +294,14 @@ const startServer = async () => { if (subs[channel].length === 0) { log.verbose(`Subscribe ${channel}`); - redisSubscribeClient.subscribe(channel, onRedisMessage); - redisSubscriptions.inc(); + redisSubscribeClient.subscribe(channel, (err, count) => { + if (err) { + log.error(`Error subscribing to ${channel}`); + } + else { + redisSubscriptions.set(count); + } + }); } subs[channel].push(callback); @@ -334,8 +322,14 @@ const startServer = async () => { if (subs[channel].length === 0) { log.verbose(`Unsubscribe ${channel}`); - redisSubscribeClient.unsubscribe(channel); - redisSubscriptions.dec(); + redisSubscribeClient.unsubscribe(channel, (err, count) => { + if (err) { + log.error(`Error unsubscribing to ${channel}`); + } + else { + redisSubscriptions.set(count); + } + }); delete subs[channel]; } }; diff --git a/yarn.lock b/yarn.lock index 880ded977..74ed5214e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1452,6 +1452,11 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@ioredis/commands@^1.1.1": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11" + integrity sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg== + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -1786,40 +1791,6 @@ resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-7.0.7.tgz#54af8d66160a8a7bf7d8f184703d2bf4b3fab914" integrity sha512-J2v5Ca7HgejO7diGKiDylaVDQKmbQ5FJih6Oo3hXuBKEuXlcaccJu64lj8MNVLaPVyZx0g4gaOQZQz95QEb/hg== -"@redis/bloom@1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@redis/bloom/-/bloom-1.2.0.tgz#d3fd6d3c0af3ef92f26767b56414a370c7b63b71" - integrity sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg== - -"@redis/client@1.5.9": - version "1.5.9" - resolved "https://registry.yarnpkg.com/@redis/client/-/client-1.5.9.tgz#c4ee81bbfedb4f1d9c7c5e9859661b9388fb4021" - integrity sha512-SffgN+P1zdWJWSXBvJeynvEnmnZrYmtKSRW00xl8pOPFOMJjxRR9u0frSxJpPR6Y4V+k54blJjGW7FgxbTI7bQ== - dependencies: - cluster-key-slot "1.1.2" - generic-pool "3.9.0" - yallist "4.0.0" - -"@redis/graph@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@redis/graph/-/graph-1.1.0.tgz#cc2b82e5141a29ada2cce7d267a6b74baa6dd519" - integrity sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg== - -"@redis/json@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@redis/json/-/json-1.0.4.tgz#f372b5f93324e6ffb7f16aadcbcb4e5c3d39bda1" - integrity sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw== - -"@redis/search@1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@redis/search/-/search-1.1.3.tgz#b5a6837522ce9028267fe6f50762a8bcfd2e998b" - integrity sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng== - -"@redis/time-series@1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@redis/time-series/-/time-series-1.0.5.tgz#a6d70ef7a0e71e083ea09b967df0a0ed742bc6ad" - integrity sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg== - "@reduxjs/toolkit@^1.9.5": version "1.9.5" resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.9.5.tgz#d3987849c24189ca483baa7aa59386c8e52077c4" @@ -4111,7 +4082,7 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -cluster-key-slot@1.1.2: +cluster-key-slot@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== @@ -4857,6 +4828,11 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== +denque@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" + integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== + depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -6139,11 +6115,6 @@ gauge@^5.0.0: strip-ansi "^6.0.1" wide-align "^1.1.5" -generic-pool@3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.9.0.tgz#36f4a678e963f4fdb8707eab050823abc4e8f5e4" - integrity sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g== - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -6823,6 +6794,21 @@ invariant@^2.2.2, invariant@^2.2.4: dependencies: loose-envify "^1.0.0" +ioredis@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.3.2.tgz#9139f596f62fc9c72d873353ac5395bcf05709f7" + integrity sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA== + dependencies: + "@ioredis/commands" "^1.1.1" + cluster-key-slot "^1.1.0" + debug "^4.3.4" + denque "^2.1.0" + lodash.defaults "^4.2.0" + lodash.isarguments "^3.1.0" + redis-errors "^1.2.0" + redis-parser "^3.0.0" + standard-as-callback "^2.1.0" + ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -10283,17 +10269,17 @@ redent@^4.0.0: indent-string "^5.0.0" strip-indent "^4.0.0" -redis@^4.6.5: - version "4.6.8" - resolved "https://registry.yarnpkg.com/redis/-/redis-4.6.8.tgz#54c5992e8a5ba512506fe9f53142cadc405547e7" - integrity sha512-S7qNkPUYrsofQ0ztWlTHSaK0Qqfl1y+WMIxrzeAGNG+9iUZB4HGeBgkHxE6uJJ6iXrkvLd1RVJ2nvu6H1sAzfQ== +redis-errors@^1.0.0, redis-errors@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" + integrity sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w== + +redis-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" + integrity sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A== dependencies: - "@redis/bloom" "1.2.0" - "@redis/client" "1.5.9" - "@redis/graph" "1.1.0" - "@redis/json" "1.0.4" - "@redis/search" "1.1.3" - "@redis/time-series" "1.0.5" + redis-errors "^1.0.0" redux-immutable@^4.0.0: version "4.0.0" @@ -11211,6 +11197,11 @@ stacktrace-js@^2.0.2: stack-generator "^2.0.5" stacktrace-gps "^3.0.4" +standard-as-callback@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" + integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -12966,16 +12957,16 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yallist@4.0.0, yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + yaml@^1.10.0: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" From 16681e0f20e1f8584e11439953c8d59b322571f5 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 1 Sep 2023 17:47:07 +0200 Subject: [PATCH 69/81] Add admin notifications for new Mastodon versions (#26582) --- .../admin/software_updates_controller.rb | 18 ++ .../components/critical_update_banner.tsx | 26 +++ .../mastodon/features/home_timeline/index.jsx | 14 +- app/javascript/mastodon/initial_state.js | 2 + app/javascript/mastodon/locales/en.json | 3 + app/javascript/styles/mastodon/admin.scss | 5 + .../styles/mastodon/components.scss | 18 +- app/javascript/styles/mastodon/tables.scss | 5 + app/lib/admin/system_check.rb | 1 + .../system_check/software_version_check.rb | 27 +++ app/mailers/admin_mailer.rb | 16 ++ app/models/software_update.rb | 40 +++++ app/models/user_settings.rb | 1 + app/policies/software_update_policy.rb | 7 + app/presenters/initial_state_presenter.rb | 6 +- app/serializers/initial_state_serializer.rb | 2 + app/services/software_update_check_service.rb | 82 +++++++++ .../admin/software_updates/index.html.haml | 29 ++++ .../new_critical_software_updates.text.erb | 5 + .../new_software_updates.text.erb | 5 + .../preferences/notifications/show.html.haml | 6 +- .../software_update_check_scheduler.rb | 11 ++ config/locales/en.yml | 25 +++ config/locales/simple_form.en.yml | 6 + config/navigation.rb | 3 + config/routes/admin.rb | 2 + config/sidekiq.yml | 4 + .../20230822081029_create_software_updates.rb | 16 ++ db/schema.rb | 12 +- lib/mastodon/version.rb | 4 + lib/tasks/mastodon.rake | 4 + .../fabricators/software_update_fabricator.rb | 7 + spec/features/admin/software_updates_spec.rb | 23 +++ .../software_version_check_spec.rb | 133 +++++++++++++++ spec/mailers/admin_mailer_spec.rb | 42 +++++ spec/models/software_update_spec.rb | 87 ++++++++++ spec/policies/software_update_policy_spec.rb | 25 +++ .../software_update_check_service_spec.rb | 158 ++++++++++++++++++ .../software_update_check_scheduler_spec.rb | 20 +++ 39 files changed, 892 insertions(+), 8 deletions(-) create mode 100644 app/controllers/admin/software_updates_controller.rb create mode 100644 app/javascript/mastodon/features/home_timeline/components/critical_update_banner.tsx create mode 100644 app/lib/admin/system_check/software_version_check.rb create mode 100644 app/models/software_update.rb create mode 100644 app/policies/software_update_policy.rb create mode 100644 app/services/software_update_check_service.rb create mode 100644 app/views/admin/software_updates/index.html.haml create mode 100644 app/views/admin_mailer/new_critical_software_updates.text.erb create mode 100644 app/views/admin_mailer/new_software_updates.text.erb create mode 100644 app/workers/scheduler/software_update_check_scheduler.rb create mode 100644 db/migrate/20230822081029_create_software_updates.rb create mode 100644 spec/fabricators/software_update_fabricator.rb create mode 100644 spec/features/admin/software_updates_spec.rb create mode 100644 spec/lib/admin/system_check/software_version_check_spec.rb create mode 100644 spec/models/software_update_spec.rb create mode 100644 spec/policies/software_update_policy_spec.rb create mode 100644 spec/services/software_update_check_service_spec.rb create mode 100644 spec/workers/scheduler/software_update_check_scheduler_spec.rb diff --git a/app/controllers/admin/software_updates_controller.rb b/app/controllers/admin/software_updates_controller.rb new file mode 100644 index 000000000..52d8cb41e --- /dev/null +++ b/app/controllers/admin/software_updates_controller.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Admin + class SoftwareUpdatesController < BaseController + before_action :check_enabled! + + def index + authorize :software_update, :index? + @software_updates = SoftwareUpdate.all.sort_by(&:gem_version) + end + + private + + def check_enabled! + not_found unless SoftwareUpdate.check_enabled? + end + end +end diff --git a/app/javascript/mastodon/features/home_timeline/components/critical_update_banner.tsx b/app/javascript/mastodon/features/home_timeline/components/critical_update_banner.tsx new file mode 100644 index 000000000..d0dd2b6ac --- /dev/null +++ b/app/javascript/mastodon/features/home_timeline/components/critical_update_banner.tsx @@ -0,0 +1,26 @@ +import { FormattedMessage } from 'react-intl'; + +export const CriticalUpdateBanner = () => ( +
+
+

+ +

+

+ {' '} + + + +

+
+
+); diff --git a/app/javascript/mastodon/features/home_timeline/index.jsx b/app/javascript/mastodon/features/home_timeline/index.jsx index 1cd6edd7a..8ff037794 100644 --- a/app/javascript/mastodon/features/home_timeline/index.jsx +++ b/app/javascript/mastodon/features/home_timeline/index.jsx @@ -14,7 +14,7 @@ import { fetchAnnouncements, toggleShowAnnouncements } from 'mastodon/actions/an import { IconWithBadge } from 'mastodon/components/icon_with_badge'; import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator'; import AnnouncementsContainer from 'mastodon/features/getting_started/containers/announcements_container'; -import { me } from 'mastodon/initial_state'; +import { me, criticalUpdatesPending } from 'mastodon/initial_state'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { expandHomeTimeline } from '../../actions/timelines'; @@ -23,6 +23,7 @@ import ColumnHeader from '../../components/column_header'; import StatusListContainer from '../ui/containers/status_list_container'; import { ColumnSettings } from './components/column_settings'; +import { CriticalUpdateBanner } from './components/critical_update_banner'; import { ExplorePrompt } from './components/explore_prompt'; const messages = defineMessages({ @@ -156,8 +157,9 @@ class HomeTimeline extends PureComponent { const { intl, hasUnread, columnId, multiColumn, tooSlow, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props; const pinned = !!columnId; const { signedIn } = this.context.identity; + const banners = []; - let announcementsButton, banner; + let announcementsButton; if (hasAnnouncements) { announcementsButton = ( @@ -173,8 +175,12 @@ class HomeTimeline extends PureComponent { ); } + if (criticalUpdatesPending) { + banners.push(); + } + if (tooSlow) { - banner = ; + banners.push(); } return ( @@ -196,7 +202,7 @@ class HomeTimeline extends PureComponent { {signedIn ? ( } accounts * @property {InitialStateLanguage[]} languages + * @property {boolean=} critical_updates_pending * @property {InitialStateMeta} meta */ @@ -140,6 +141,7 @@ export const useBlurhash = getMeta('use_blurhash'); export const usePendingItems = getMeta('use_pending_items'); export const version = getMeta('version'); export const languages = initialState?.languages; +export const criticalUpdatesPending = initialState?.critical_updates_pending; // @ts-expect-error export const statusPageUrl = getMeta('status_page_url'); export const sso_redirect = getMeta('sso_redirect'); diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 90bb9616f..13cddba72 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -310,6 +310,9 @@ "home.explore_prompt.body": "Your home feed will have a mix of posts from the hashtags you've chosen to follow, the people you've chosen to follow, and the posts they boost. If that feels too quiet, you may want to:", "home.explore_prompt.title": "This is your home base within Mastodon.", "home.hide_announcements": "Hide announcements", + "home.pending_critical_update.body": "Please update your Mastodon server as soon as possible!", + "home.pending_critical_update.link": "See updates", + "home.pending_critical_update.title": "Critical security update available!", "home.show_announcements": "Show announcements", "interaction_modal.description.favourite": "With an account on Mastodon, you can favorite this post to let the author know you appreciate it and save it for later.", "interaction_modal.description.follow": "With an account on Mastodon, you can follow {name} to receive their posts in your home feed.", diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index bbb6ffdff..a65f35e7b 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -143,6 +143,11 @@ $content-width: 840px; } } + .warning a { + color: $gold-star; + font-weight: 700; + } + .simple-navigation-active-leaf a { color: $primary-text-color; background-color: $ui-highlight-color; diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index f61cd059f..10083a2a3 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -8860,7 +8860,8 @@ noscript { } } -.dismissable-banner { +.dismissable-banner, +.warning-banner { position: relative; margin: 10px; margin-bottom: 5px; @@ -8938,6 +8939,21 @@ noscript { } } +.warning-banner { + border: 1px solid $warning-red; + background: rgba($warning-red, 0.15); + + &__message { + h1 { + color: $warning-red; + } + + a { + color: $primary-text-color; + } + } +} + .image { position: relative; overflow: hidden; diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss index 38cfc8727..dd5b483ec 100644 --- a/app/javascript/styles/mastodon/tables.scss +++ b/app/javascript/styles/mastodon/tables.scss @@ -12,6 +12,11 @@ border-top: 1px solid $ui-base-color; text-align: start; background: darken($ui-base-color, 4%); + + &.critical { + font-weight: 700; + color: $gold-star; + } } & > thead > tr > th { diff --git a/app/lib/admin/system_check.rb b/app/lib/admin/system_check.rb index 89dfcef9f..25c88341a 100644 --- a/app/lib/admin/system_check.rb +++ b/app/lib/admin/system_check.rb @@ -2,6 +2,7 @@ class Admin::SystemCheck ACTIVE_CHECKS = [ + Admin::SystemCheck::SoftwareVersionCheck, Admin::SystemCheck::MediaPrivacyCheck, Admin::SystemCheck::DatabaseSchemaCheck, Admin::SystemCheck::SidekiqProcessCheck, diff --git a/app/lib/admin/system_check/software_version_check.rb b/app/lib/admin/system_check/software_version_check.rb new file mode 100644 index 000000000..e142feddf --- /dev/null +++ b/app/lib/admin/system_check/software_version_check.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class Admin::SystemCheck::SoftwareVersionCheck < Admin::SystemCheck::BaseCheck + include RoutingHelper + + def skip? + !current_user.can?(:view_devops) || !SoftwareUpdate.check_enabled? + end + + def pass? + software_updates.empty? + end + + def message + if software_updates.any?(&:urgent?) + Admin::SystemCheck::Message.new(:software_version_critical_check, nil, admin_software_updates_path, true) + else + Admin::SystemCheck::Message.new(:software_version_patch_check, nil, admin_software_updates_path) + end + end + + private + + def software_updates + @software_updates ||= SoftwareUpdate.pending_to_a.filter { |update| update.urgent? || update.patch_type? } + end +end diff --git a/app/mailers/admin_mailer.rb b/app/mailers/admin_mailer.rb index 5baf9b38a..990b92c33 100644 --- a/app/mailers/admin_mailer.rb +++ b/app/mailers/admin_mailer.rb @@ -45,6 +45,22 @@ class AdminMailer < ApplicationMailer end end + def new_software_updates + locale_for_account(@me) do + mail subject: default_i18n_subject(instance: @instance) + end + end + + def new_critical_software_updates + headers['Priority'] = 'urgent' + headers['X-Priority'] = '1' + headers['Importance'] = 'high' + + locale_for_account(@me) do + mail subject: default_i18n_subject(instance: @instance) + end + end + private def process_params diff --git a/app/models/software_update.rb b/app/models/software_update.rb new file mode 100644 index 000000000..cb3a6df2a --- /dev/null +++ b/app/models/software_update.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: software_updates +# +# id :bigint(8) not null, primary key +# version :string not null +# urgent :boolean default(FALSE), not null +# type :integer default("patch"), not null +# release_notes :string default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class SoftwareUpdate < ApplicationRecord + self.inheritance_column = nil + + enum type: { patch: 0, minor: 1, major: 2 }, _suffix: :type + + def gem_version + Gem::Version.new(version) + end + + class << self + def check_enabled? + ENV['UPDATE_CHECK_URL'] != '' + end + + def pending_to_a + return [] unless check_enabled? + + all.to_a.filter { |update| update.gem_version > Mastodon::Version.gem_version } + end + + def urgent_pending? + pending_to_a.any?(&:urgent?) + end + end +end diff --git a/app/models/user_settings.rb b/app/models/user_settings.rb index 678467c75..030cbec4d 100644 --- a/app/models/user_settings.rb +++ b/app/models/user_settings.rb @@ -44,6 +44,7 @@ class UserSettings setting :pending_account, default: true setting :trends, default: true setting :appeal, default: true + setting :software_updates, default: 'critical', in: %w(none critical patch all) end namespace :interactions do diff --git a/app/policies/software_update_policy.rb b/app/policies/software_update_policy.rb new file mode 100644 index 000000000..dcb565814 --- /dev/null +++ b/app/policies/software_update_policy.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class SoftwareUpdatePolicy < ApplicationPolicy + def index? + role.can?(:view_devops) + end +end diff --git a/app/presenters/initial_state_presenter.rb b/app/presenters/initial_state_presenter.rb index b87cff51e..222cc8566 100644 --- a/app/presenters/initial_state_presenter.rb +++ b/app/presenters/initial_state_presenter.rb @@ -3,9 +3,13 @@ class InitialStatePresenter < ActiveModelSerializers::Model attributes :settings, :push_subscription, :token, :current_account, :admin, :owner, :text, :visibility, - :disabled_account, :moved_to_account + :disabled_account, :moved_to_account, :critical_updates_pending def role current_account&.user_role end + + def critical_updates_pending + role&.can?(:view_devops) && SoftwareUpdate.urgent_pending? + end end diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index 9660c941d..56d45c588 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -7,6 +7,8 @@ class InitialStateSerializer < ActiveModel::Serializer :media_attachments, :settings, :languages + attribute :critical_updates_pending, if: -> { object&.role&.can?(:view_devops) && SoftwareUpdate.check_enabled? } + has_one :push_subscription, serializer: REST::WebPushSubscriptionSerializer has_one :role, serializer: REST::RoleSerializer diff --git a/app/services/software_update_check_service.rb b/app/services/software_update_check_service.rb new file mode 100644 index 000000000..49b92f104 --- /dev/null +++ b/app/services/software_update_check_service.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +class SoftwareUpdateCheckService < BaseService + def call + clean_outdated_updates! + return unless SoftwareUpdate.check_enabled? + + process_update_notices!(fetch_update_notices) + end + + private + + def clean_outdated_updates! + SoftwareUpdate.find_each do |software_update| + software_update.delete if Mastodon::Version.gem_version >= software_update.gem_version + rescue ArgumentError + software_update.delete + end + end + + def fetch_update_notices + Request.new(:get, "#{api_url}?version=#{version}").add_headers('Accept' => 'application/json', 'User-Agent' => 'Mastodon update checker').perform do |res| + return Oj.load(res.body_with_limit, mode: :strict) if res.code == 200 + end + rescue HTTP::Error, OpenSSL::SSL::SSLError, Oj::ParseError + nil + end + + def api_url + ENV.fetch('UPDATE_CHECK_URL', 'https://api.joinmastodon.org/update-check') + end + + def version + @version ||= Mastodon::Version.to_s.split('+')[0] + end + + def process_update_notices!(update_notices) + return if update_notices.blank? || update_notices['updatesAvailable'].blank? + + # Clear notices that are not listed by the update server anymore + SoftwareUpdate.where.not(version: update_notices['updatesAvailable'].pluck('version')).delete_all + + # Check if any of the notices is new, and issue notifications + known_versions = SoftwareUpdate.where(version: update_notices['updatesAvailable'].pluck('version')).pluck(:version) + new_update_notices = update_notices['updatesAvailable'].filter { |notice| known_versions.exclude?(notice['version']) } + return if new_update_notices.blank? + + new_updates = new_update_notices.map do |notice| + SoftwareUpdate.create!(version: notice['version'], urgent: notice['urgent'], type: notice['type'], release_notes: notice['releaseNotes']) + end + + notify_devops!(new_updates) + end + + def should_notify_user?(user, urgent_version, patch_version) + case user.settings['notification_emails.software_updates'] + when 'none' + false + when 'critical' + urgent_version + when 'patch' + urgent_version || patch_version + when 'all' + true + end + end + + def notify_devops!(new_updates) + has_new_urgent_version = new_updates.any?(&:urgent?) + has_new_patch_version = new_updates.any?(&:patch_type?) + + User.those_who_can(:view_devops).includes(:account).find_each do |user| + next unless should_notify_user?(user, has_new_urgent_version, has_new_patch_version) + + if has_new_urgent_version + AdminMailer.with(recipient: user.account).new_critical_software_updates.deliver_later + else + AdminMailer.with(recipient: user.account).new_software_updates.deliver_later + end + end + end +end diff --git a/app/views/admin/software_updates/index.html.haml b/app/views/admin/software_updates/index.html.haml new file mode 100644 index 000000000..7a223ee07 --- /dev/null +++ b/app/views/admin/software_updates/index.html.haml @@ -0,0 +1,29 @@ +- content_for :page_title do + = t('admin.software_updates.title') + +.simple_form + %p.lead + = t('admin.software_updates.description') + = link_to t('admin.software_updates.documentation_link'), 'https://docs.joinmastodon.org/admin/upgrading/#automated_checks', target: '_new' + +%hr.spacer + +- unless @software_updates.empty? + .table-wrapper + %table.table + %thead + %tr + %th= t('admin.software_updates.version') + %th= t('admin.software_updates.type') + %th + %th + %tbody + - @software_updates.each do |update| + %tr + %td= update.version + %td= t("admin.software_updates.types.#{update.type}") + - if update.urgent? + %td.critical= t("admin.software_updates.critical_update") + - else + %td + %td= table_link_to 'link', t('admin.software_updates.release_notes'), update.release_notes diff --git a/app/views/admin_mailer/new_critical_software_updates.text.erb b/app/views/admin_mailer/new_critical_software_updates.text.erb new file mode 100644 index 000000000..c901bc50f --- /dev/null +++ b/app/views/admin_mailer/new_critical_software_updates.text.erb @@ -0,0 +1,5 @@ +<%= raw t('application_mailer.salutation', name: display_name(@me)) %> + +<%= raw t('admin_mailer.new_critical_software_updates.body') %> + +<%= raw t('application_mailer.view')%> <%= admin_software_updates_url %> diff --git a/app/views/admin_mailer/new_software_updates.text.erb b/app/views/admin_mailer/new_software_updates.text.erb new file mode 100644 index 000000000..2fc4d1a5f --- /dev/null +++ b/app/views/admin_mailer/new_software_updates.text.erb @@ -0,0 +1,5 @@ +<%= raw t('application_mailer.salutation', name: display_name(@me)) %> + +<%= raw t('admin_mailer.new_software_updates.body') %> + +<%= raw t('application_mailer.view')%> <%= admin_software_updates_url %> diff --git a/app/views/settings/preferences/notifications/show.html.haml b/app/views/settings/preferences/notifications/show.html.haml index 0913bda9a..5cc101069 100644 --- a/app/views/settings/preferences/notifications/show.html.haml +++ b/app/views/settings/preferences/notifications/show.html.haml @@ -22,7 +22,7 @@ .fields-group = ff.input :always_send_emails, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_always_send_emails'), hint: I18n.t('simple_form.hints.defaults.setting_always_send_emails') - - if current_user.can?(:manage_reports, :manage_appeals, :manage_users, :manage_taxonomies) + - if current_user.can?(:manage_reports, :manage_appeals, :manage_users, :manage_taxonomies) || (SoftwareUpdate.check_enabled? && current_user.can?(:view_devops)) %h4= t 'notifications.administration_emails' .fields-group @@ -31,6 +31,10 @@ = ff.input :'notification_emails.pending_account', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.pending_account') if current_user.can?(:manage_users) = ff.input :'notification_emails.trends', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.trending_tag') if current_user.can?(:manage_taxonomies) + - if SoftwareUpdate.check_enabled? && current_user.can?(:view_devops) + .fields-group + = ff.input :'notification_emails.software_updates', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.software_updates.label'), collection: %w(none critical patch all), label_method: ->(setting) { I18n.t("simple_form.labels.notification_emails.software_updates.#{setting}") }, include_blank: false, hint: false + %h4= t 'notifications.other_settings' .fields-group diff --git a/app/workers/scheduler/software_update_check_scheduler.rb b/app/workers/scheduler/software_update_check_scheduler.rb new file mode 100644 index 000000000..c732bdedc --- /dev/null +++ b/app/workers/scheduler/software_update_check_scheduler.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class Scheduler::SoftwareUpdateCheckScheduler + include Sidekiq::Worker + + sidekiq_options retry: 0, lock: :until_executed, lock_ttl: 1.hour.to_i + + def perform + SoftwareUpdateCheckService.new.call + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml index 693155d6e..71e5fb843 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -309,6 +309,7 @@ en: unpublish: Unpublish unpublished_msg: Announcement successfully unpublished! updated_msg: Announcement successfully updated! + critical_update_pending: Critical update pending custom_emojis: assign_category: Assign category by_domain: Domain @@ -779,6 +780,18 @@ en: site_uploads: delete: Delete uploaded file destroyed_msg: Site upload successfully deleted! + software_updates: + critical_update: Critical — please update quickly + description: It is recommended to keep your Mastodon installation up to date to benefit from the latest fixes and features. Moreover, it is sometimes critical to update Mastodon in a timely manner to avoid security issues. For these reasons, Mastodon checks for updates every 30 minutes, and will notify you according to your e-mail notification preferences. + documentation_link: Learn more + release_notes: Release notes + title: Available updates + type: Type + types: + major: Major release + minor: Minor release + patch: Patch release — bugfixes and easy to apply changes + version: Version statuses: account: Author application: Application @@ -843,6 +856,12 @@ en: message_html: You haven't defined any server rules. sidekiq_process_check: message_html: No Sidekiq process running for the %{value} queue(s). Please review your Sidekiq configuration + software_version_critical_check: + action: See available updates + message_html: A critical Mastodon update is available, please update as quickly as possible. + software_version_patch_check: + action: See available updates + message_html: A bugfix Mastodon update is available. upload_check_privacy_error: action: Check here for more information message_html: "Your web server is misconfigured. The privacy of your users is at risk." @@ -956,6 +975,9 @@ en: body: "%{target} is appealing a moderation decision by %{action_taken_by} from %{date}, which was %{type}. They wrote:" next_steps: You can approve the appeal to undo the moderation decision, or ignore it. subject: "%{username} is appealing a moderation decision on %{instance}" + new_critical_software_updates: + body: New critical versions of Mastodon have been released, you may want to update as soon as possible! + subject: Critical Mastodon updates are available for %{instance}! new_pending_account: body: The details of the new account are below. You can approve or reject this application. subject: New account up for review on %{instance} (%{username}) @@ -963,6 +985,9 @@ en: body: "%{reporter} has reported %{target}" body_remote: Someone from %{domain} has reported %{target} subject: New report for %{instance} (#%{id}) + new_software_updates: + body: New Mastodon versions have been released, you may want to update! + subject: New Mastodon versions are available for %{instance}! new_trends: body: 'The following items need a review before they can be displayed publicly:' new_trending_links: diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index b1297606b..0b718c5b6 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -291,6 +291,12 @@ en: pending_account: New account needs review reblog: Someone boosted your post report: New report is submitted + software_updates: + all: Notify on all updates + critical: Notify on critical updates only + label: A new Mastodon version is available + none: Never notify of updates (not recommended) + patch: Notify on bugfix updates trending_tag: New trend requires review rule: text: Rule diff --git a/config/navigation.rb b/config/navigation.rb index f608c2eea..e86c695a9 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -3,6 +3,9 @@ SimpleNavigation::Configuration.run do |navigation| navigation.items do |n| n.item :web, safe_join([fa_icon('chevron-left fw'), t('settings.back')]), root_path + + n.item :software_updates, safe_join([fa_icon('exclamation-circle fw'), t('admin.critical_update_pending')]), admin_software_updates_path, if: -> { ENV['UPDATE_CHECK_URL'] != '' && current_user.can?(:view_devops) && SoftwareUpdate.urgent_pending? }, html: { class: 'warning' } + n.item :profile, safe_join([fa_icon('user fw'), t('settings.profile')]), settings_profile_path, if: -> { current_user.functional? }, highlights_on: %r{/settings/profile|/settings/featured_tags|/settings/verification|/settings/privacy} n.item :preferences, safe_join([fa_icon('cog fw'), t('settings.preferences')]), settings_preferences_path, if: -> { current_user.functional? } do |s| diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 4573878ed..207cb0580 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -201,4 +201,6 @@ namespace :admin do end end end + + resources :software_updates, only: [:index] end diff --git a/config/sidekiq.yml b/config/sidekiq.yml index 12c45c22a..f1ba5651d 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -58,3 +58,7 @@ interval: 1 minute class: Scheduler::SuspendedUserCleanupScheduler queue: scheduler + software_update_check_scheduler: + interval: 30 minutes + class: Scheduler::SoftwareUpdateCheckScheduler + queue: scheduler diff --git a/db/migrate/20230822081029_create_software_updates.rb b/db/migrate/20230822081029_create_software_updates.rb new file mode 100644 index 000000000..146d5d303 --- /dev/null +++ b/db/migrate/20230822081029_create_software_updates.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class CreateSoftwareUpdates < ActiveRecord::Migration[7.0] + def change + create_table :software_updates do |t| + t.string :version, null: false + t.boolean :urgent, default: false, null: false + t.integer :type, default: 0, null: false + t.string :release_notes, default: '', null: false + + t.timestamps + end + + add_index :software_updates, :version, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 8b758fc7d..c86106942 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_08_18_142253) do +ActiveRecord::Schema[7.0].define(version: 2023_08_22_081029) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -903,6 +903,16 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_18_142253) do t.index ["var"], name: "index_site_uploads_on_var", unique: true end + create_table "software_updates", force: :cascade do |t| + t.string "version", null: false + t.boolean "urgent", default: false, null: false + t.integer "type", default: 0, null: false + t.string "release_notes", default: "", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["version"], name: "index_software_updates_on_version", unique: true + end + create_table "status_edits", force: :cascade do |t| t.bigint "status_id", null: false t.bigint "account_id" diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index c542d5d49..65f90f93f 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -39,6 +39,10 @@ module Mastodon components.join end + def gem_version + @gem_version ||= Gem::Version.new(to_s.split('+')[0]) + end + def repository ENV.fetch('GITHUB_REPOSITORY', 'mastodon/mastodon') end diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index 010caaf8e..f68d1cf1f 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -424,6 +424,10 @@ namespace :mastodon do end end + prompt.say "\n" + + env['UPDATE_CHECK_URL'] = '' unless prompt.yes?('Do you want Mastodon to periodically check for important updates and notify you? (Recommended)', default: true) + prompt.say "\n" prompt.say 'This configuration will be written to .env.production' diff --git a/spec/fabricators/software_update_fabricator.rb b/spec/fabricators/software_update_fabricator.rb new file mode 100644 index 000000000..622fff66e --- /dev/null +++ b/spec/fabricators/software_update_fabricator.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +Fabricator(:software_update) do + version '99.99.99' + urgent false + type 'patch' +end diff --git a/spec/features/admin/software_updates_spec.rb b/spec/features/admin/software_updates_spec.rb new file mode 100644 index 000000000..4a635d1a7 --- /dev/null +++ b/spec/features/admin/software_updates_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'finding software updates through the admin interface' do + before do + Fabricate(:software_update, version: '99.99.99', type: 'major', urgent: true, release_notes: 'https://github.com/mastodon/mastodon/releases/v99') + + sign_in Fabricate(:user, role: UserRole.find_by(name: 'Owner')), scope: :user + end + + it 'shows a link to the software updates page, which links to release notes' do + visit settings_profile_path + click_on I18n.t('admin.critical_update_pending') + + expect(page).to have_title(I18n.t('admin.software_updates.title')) + + expect(page).to have_content('99.99.99') + + click_on I18n.t('admin.software_updates.release_notes') + expect(page).to have_current_path('https://github.com/mastodon/mastodon/releases/v99', url: true) + end +end diff --git a/spec/lib/admin/system_check/software_version_check_spec.rb b/spec/lib/admin/system_check/software_version_check_spec.rb new file mode 100644 index 000000000..de4335fc5 --- /dev/null +++ b/spec/lib/admin/system_check/software_version_check_spec.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::SystemCheck::SoftwareVersionCheck do + include RoutingHelper + + subject(:check) { described_class.new(user) } + + let(:user) { Fabricate(:user) } + + describe 'skip?' do + context 'when user cannot view devops' do + before { allow(user).to receive(:can?).with(:view_devops).and_return(false) } + + it 'returns true' do + expect(check.skip?).to be true + end + end + + context 'when user can view devops' do + before { allow(user).to receive(:can?).with(:view_devops).and_return(true) } + + it 'returns false' do + expect(check.skip?).to be false + end + + context 'when checks are disabled' do + around do |example| + ClimateControl.modify UPDATE_CHECK_URL: '' do + example.run + end + end + + it 'returns true' do + expect(check.skip?).to be true + end + end + end + end + + describe 'pass?' do + context 'when there is no known update' do + it 'returns true' do + expect(check.pass?).to be true + end + end + + context 'when there is a non-urgent major release' do + before do + Fabricate(:software_update, version: '99.99.99', type: 'major', urgent: false) + end + + it 'returns true' do + expect(check.pass?).to be true + end + end + + context 'when there is an urgent major release' do + before do + Fabricate(:software_update, version: '99.99.99', type: 'major', urgent: true) + end + + it 'returns false' do + expect(check.pass?).to be false + end + end + + context 'when there is an urgent minor release' do + before do + Fabricate(:software_update, version: '99.99.99', type: 'minor', urgent: true) + end + + it 'returns false' do + expect(check.pass?).to be false + end + end + + context 'when there is an urgent patch release' do + before do + Fabricate(:software_update, version: '99.99.99', type: 'patch', urgent: true) + end + + it 'returns false' do + expect(check.pass?).to be false + end + end + + context 'when there is a non-urgent patch release' do + before do + Fabricate(:software_update, version: '99.99.99', type: 'patch', urgent: false) + end + + it 'returns false' do + expect(check.pass?).to be false + end + end + end + + describe 'message' do + context 'when there is a non-urgent patch release pending' do + before do + Fabricate(:software_update, version: '99.99.99', type: 'patch', urgent: false) + end + + it 'sends class name symbol to message instance' do + allow(Admin::SystemCheck::Message).to receive(:new) + .with(:software_version_patch_check, anything, anything) + + check.message + + expect(Admin::SystemCheck::Message).to have_received(:new) + .with(:software_version_patch_check, nil, admin_software_updates_path) + end + end + + context 'when there is an urgent patch release pending' do + before do + Fabricate(:software_update, version: '99.99.99', type: 'patch', urgent: true) + end + + it 'sends class name symbol to message instance' do + allow(Admin::SystemCheck::Message).to receive(:new) + .with(:software_version_critical_check, anything, anything, anything) + + check.message + + expect(Admin::SystemCheck::Message).to have_received(:new) + .with(:software_version_critical_check, nil, admin_software_updates_path, true) + end + end + end +end diff --git a/spec/mailers/admin_mailer_spec.rb b/spec/mailers/admin_mailer_spec.rb index 9123804a4..423dce88a 100644 --- a/spec/mailers/admin_mailer_spec.rb +++ b/spec/mailers/admin_mailer_spec.rb @@ -85,4 +85,46 @@ RSpec.describe AdminMailer do expect(mail.body.encoded).to match 'The following items need a review before they can be displayed publicly' end end + + describe '.new_software_updates' do + let(:recipient) { Fabricate(:account, username: 'Bob') } + let(:mail) { described_class.with(recipient: recipient).new_software_updates } + + before do + recipient.user.update(locale: :en) + end + + it 'renders the headers' do + expect(mail.subject).to eq('New Mastodon versions are available for cb6e6126.ngrok.io!') + expect(mail.to).to eq [recipient.user_email] + expect(mail.from).to eq ['notifications@localhost'] + end + + it 'renders the body' do + expect(mail.body.encoded).to match 'New Mastodon versions have been released, you may want to update!' + end + end + + describe '.new_critical_software_updates' do + let(:recipient) { Fabricate(:account, username: 'Bob') } + let(:mail) { described_class.with(recipient: recipient).new_critical_software_updates } + + before do + recipient.user.update(locale: :en) + end + + it 'renders the headers', :aggregate_failures do + expect(mail.subject).to eq('Critical Mastodon updates are available for cb6e6126.ngrok.io!') + expect(mail.to).to eq [recipient.user_email] + expect(mail.from).to eq ['notifications@localhost'] + + expect(mail['Importance'].value).to eq 'high' + expect(mail['Priority'].value).to eq 'urgent' + expect(mail['X-Priority'].value).to eq '1' + end + + it 'renders the body' do + expect(mail.body.encoded).to match 'New critical versions of Mastodon have been released, you may want to update as soon as possible!' + end + end end diff --git a/spec/models/software_update_spec.rb b/spec/models/software_update_spec.rb new file mode 100644 index 000000000..0a494b0c4 --- /dev/null +++ b/spec/models/software_update_spec.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe SoftwareUpdate do + describe '.pending_to_a' do + before do + allow(Mastodon::Version).to receive(:gem_version).and_return(Gem::Version.new(mastodon_version)) + + Fabricate(:software_update, version: '3.4.42', type: 'patch', urgent: true) + Fabricate(:software_update, version: '3.5.0', type: 'minor', urgent: false) + Fabricate(:software_update, version: '4.2.0', type: 'major', urgent: false) + end + + context 'when the Mastodon version is an outdated release' do + let(:mastodon_version) { '3.4.0' } + + it 'returns the expected versions' do + expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('3.4.42', '3.5.0', '4.2.0') + end + end + + context 'when the Mastodon version is more recent than anything last returned by the server' do + let(:mastodon_version) { '5.0.0' } + + it 'returns the expected versions' do + expect(described_class.pending_to_a.pluck(:version)).to eq [] + end + end + + context 'when the Mastodon version is an outdated nightly' do + let(:mastodon_version) { '4.3.0-nightly.2023-09-10' } + + before do + Fabricate(:software_update, version: '4.3.0-nightly.2023-09-12', type: 'major', urgent: true) + end + + it 'returns the expected versions' do + expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('4.3.0-nightly.2023-09-12') + end + end + + context 'when the Mastodon version is a very outdated nightly' do + let(:mastodon_version) { '4.2.0-nightly.2023-07-10' } + + it 'returns the expected versions' do + expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('4.2.0') + end + end + + context 'when the Mastodon version is an outdated dev version' do + let(:mastodon_version) { '4.3.0-0.dev.0' } + + before do + Fabricate(:software_update, version: '4.3.0-0.dev.2', type: 'major', urgent: true) + end + + it 'returns the expected versions' do + expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('4.3.0-0.dev.2') + end + end + + context 'when the Mastodon version is an outdated beta version' do + let(:mastodon_version) { '4.3.0-beta1' } + + before do + Fabricate(:software_update, version: '4.3.0-beta2', type: 'major', urgent: true) + end + + it 'returns the expected versions' do + expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('4.3.0-beta2') + end + end + + context 'when the Mastodon version is an outdated beta version and there is a rc' do + let(:mastodon_version) { '4.3.0-beta1' } + + before do + Fabricate(:software_update, version: '4.3.0-rc1', type: 'major', urgent: true) + end + + it 'returns the expected versions' do + expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('4.3.0-rc1') + end + end + end +end diff --git a/spec/policies/software_update_policy_spec.rb b/spec/policies/software_update_policy_spec.rb new file mode 100644 index 000000000..e19ba6161 --- /dev/null +++ b/spec/policies/software_update_policy_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'pundit/rspec' + +RSpec.describe SoftwareUpdatePolicy do + subject { described_class } + + let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Owner')).account } + let(:john) { Fabricate(:account) } + + permissions :index? do + context 'when owner' do + it 'permits' do + expect(subject).to permit(admin, SoftwareUpdate) + end + end + + context 'when not owner' do + it 'denies' do + expect(subject).to_not permit(john, SoftwareUpdate) + end + end + end +end diff --git a/spec/services/software_update_check_service_spec.rb b/spec/services/software_update_check_service_spec.rb new file mode 100644 index 000000000..c8821348a --- /dev/null +++ b/spec/services/software_update_check_service_spec.rb @@ -0,0 +1,158 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe SoftwareUpdateCheckService, type: :service do + subject { described_class.new } + + shared_examples 'when the feature is enabled' do + let(:full_update_check_url) { "#{update_check_url}?version=#{Mastodon::Version.to_s.split('+')[0]}" } + + let(:devops_role) { Fabricate(:user_role, name: 'DevOps', permissions: UserRole::FLAGS[:view_devops]) } + let(:owner_user) { Fabricate(:user, role: UserRole.find_by(name: 'Owner')) } + let(:old_devops_user) { Fabricate(:user) } + let(:none_user) { Fabricate(:user, role: devops_role) } + let(:patch_user) { Fabricate(:user, role: devops_role) } + let(:critical_user) { Fabricate(:user, role: devops_role) } + + around do |example| + queue_adapter = ActiveJob::Base.queue_adapter + ActiveJob::Base.queue_adapter = :test + + example.run + + ActiveJob::Base.queue_adapter = queue_adapter + end + + before do + Fabricate(:software_update, version: '3.5.0', type: 'major', urgent: false) + Fabricate(:software_update, version: '42.13.12', type: 'major', urgent: false) + + owner_user.settings.update('notification_emails.software_updates': 'all') + owner_user.save! + + old_devops_user.settings.update('notification_emails.software_updates': 'all') + old_devops_user.save! + + none_user.settings.update('notification_emails.software_updates': 'none') + none_user.save! + + patch_user.settings.update('notification_emails.software_updates': 'patch') + patch_user.save! + + critical_user.settings.update('notification_emails.software_updates': 'critical') + critical_user.save! + end + + context 'when the update server errors out' do + before do + stub_request(:get, full_update_check_url).to_return(status: 404) + end + + it 'deletes outdated update records but keeps valid update records' do + expect { subject.call }.to change { SoftwareUpdate.pluck(:version).sort }.from(['3.5.0', '42.13.12']).to(['42.13.12']) + end + end + + context 'when the server returns new versions' do + let(:server_json) do + { + updatesAvailable: [ + { + version: '4.2.1', + urgent: false, + type: 'patch', + releaseNotes: 'https://github.com/mastodon/mastodon/releases/v4.2.1', + }, + { + version: '4.3.0', + urgent: false, + type: 'minor', + releaseNotes: 'https://github.com/mastodon/mastodon/releases/v4.3.0', + }, + { + version: '5.0.0', + urgent: false, + type: 'minor', + releaseNotes: 'https://github.com/mastodon/mastodon/releases/v5.0.0', + }, + ], + } + end + + before do + stub_request(:get, full_update_check_url).to_return(body: Oj.dump(server_json)) + end + + it 'updates the list of known updates' do + expect { subject.call }.to change { SoftwareUpdate.pluck(:version).sort }.from(['3.5.0', '42.13.12']).to(['4.2.1', '4.3.0', '5.0.0']) + end + + context 'when no update is urgent' do + it 'sends e-mail notifications according to settings', :aggregate_failures do + expect { subject.call }.to have_enqueued_mail(AdminMailer, :new_software_updates) + .with(hash_including(params: { recipient: owner_user.account })).once + .and(have_enqueued_mail(AdminMailer, :new_software_updates).with(hash_including(params: { recipient: patch_user.account })).once) + .and(have_enqueued_mail.at_most(2)) + end + end + + context 'when an update is urgent' do + let(:server_json) do + { + updatesAvailable: [ + { + version: '5.0.0', + urgent: true, + type: 'minor', + releaseNotes: 'https://github.com/mastodon/mastodon/releases/v5.0.0', + }, + ], + } + end + + it 'sends e-mail notifications according to settings', :aggregate_failures do + expect { subject.call }.to have_enqueued_mail(AdminMailer, :new_critical_software_updates) + .with(hash_including(params: { recipient: owner_user.account })).once + .and(have_enqueued_mail(AdminMailer, :new_critical_software_updates).with(hash_including(params: { recipient: patch_user.account })).once) + .and(have_enqueued_mail(AdminMailer, :new_critical_software_updates).with(hash_including(params: { recipient: critical_user.account })).once) + .and(have_enqueued_mail.at_most(3)) + end + end + end + end + + context 'when update checking is disabled' do + around do |example| + ClimateControl.modify UPDATE_CHECK_URL: '' do + example.run + end + end + + before do + Fabricate(:software_update, version: '3.5.0', type: 'major', urgent: false) + end + + it 'deletes outdated update records' do + expect { subject.call }.to change(SoftwareUpdate, :count).from(1).to(0) + end + end + + context 'when using the default update checking API' do + let(:update_check_url) { 'https://api.joinmastodon.org/update-check' } + + it_behaves_like 'when the feature is enabled' + end + + context 'when using a custom update check URL' do + let(:update_check_url) { 'https://api.example.com/update_check' } + + around do |example| + ClimateControl.modify UPDATE_CHECK_URL: 'https://api.example.com/update_check' do + example.run + end + end + + it_behaves_like 'when the feature is enabled' + end +end diff --git a/spec/workers/scheduler/software_update_check_scheduler_spec.rb b/spec/workers/scheduler/software_update_check_scheduler_spec.rb new file mode 100644 index 000000000..f596c0a1e --- /dev/null +++ b/spec/workers/scheduler/software_update_check_scheduler_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Scheduler::SoftwareUpdateCheckScheduler do + subject { described_class.new } + + describe 'perform' do + let(:service_double) { instance_double(SoftwareUpdateCheckService, call: nil) } + + before do + allow(SoftwareUpdateCheckService).to receive(:new).and_return(service_double) + end + + it 'calls SoftwareUpdateCheckService' do + subject.perform + expect(service_double).to have_received(:call) + end + end +end From 05093266e6e3c54f9096da9cdcdafdc83703c578 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 2 Sep 2023 09:02:44 +0200 Subject: [PATCH 70/81] Fix some video encoding failing due to uneven dimensions (#26766) --- app/models/media_attachment.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index 984f4252a..f0b072e23 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -102,6 +102,7 @@ class MediaAttachment < ApplicationRecord 'preset' => 'veryfast', 'movflags' => 'faststart', # Move metadata to start of file so playback can begin before download finishes 'pix_fmt' => 'yuv420p', # Ensure color space for cross-browser compatibility + 'vf' => 'crop=floor(iw/2)*2:floor(ih/2)*2', # h264 requires width and height to be even. Crop instead of scale to avoid blurring 'c:v' => 'h264', 'c:a' => 'aac', 'b:a' => '192k', From 023673c0d9956065f29d9c080d5a0c27460a39f6 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 25 Aug 2023 11:31:20 +0200 Subject: [PATCH 71/81] [Glitch] Add `data-nosnippet` attribute to remote posts and local posts with `noindex` Port c3a42e1280759ea28bee11f241aef892b148bf6a to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/components/status.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/flavours/glitch/components/status.jsx b/app/javascript/flavours/glitch/components/status.jsx index d3d432ae0..36abc6993 100644 --- a/app/javascript/flavours/glitch/components/status.jsx +++ b/app/javascript/flavours/glitch/components/status.jsx @@ -792,6 +792,7 @@ class Status extends ImmutablePureComponent { tabIndex={0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText, !status.get('hidden'))} + data-nosnippet={status.getIn(['account', 'noindex'], true) || undefined} > {!muted && prepend} From cb50d95c067fc82b788ad6a762ad339de7d1c3fa Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Aug 2023 13:18:39 +0200 Subject: [PATCH 72/81] [Glitch] Add search options to search popout in web UI Port bceb8931591b4b9df6e8dcfb47864c230884262c to glitch-soc Signed-off-by: Claire --- .../features/compose/components/search.jsx | 50 ++++++++++++++++--- .../glitch/styles/components/search.scss | 6 +++ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/app/javascript/flavours/glitch/features/compose/components/search.jsx b/app/javascript/flavours/glitch/features/compose/components/search.jsx index 2f1b46e5d..218518a7b 100644 --- a/app/javascript/flavours/glitch/features/compose/components/search.jsx +++ b/app/javascript/flavours/glitch/features/compose/components/search.jsx @@ -1,11 +1,7 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; -import { - injectIntl, - FormattedMessage, - defineMessages, -} from 'react-intl'; +import { defineMessages, injectIntl, FormattedMessage, FormattedList } from 'react-intl'; import classNames from 'classnames'; @@ -52,6 +48,16 @@ class Search extends PureComponent { options: [], }; + defaultOptions = [ + { label: <>has: , action: e => { e.preventDefault(); this._insertText('has:') } }, + { label: <>is: , action: e => { e.preventDefault(); this._insertText('is:') } }, + { label: <>language: , action: e => { e.preventDefault(); this._insertText('language:') } }, + { label: <>from: , action: e => { e.preventDefault(); this._insertText('from:') } }, + { label: <>before: , action: e => { e.preventDefault(); this._insertText('before:') } }, + { label: <>during: , action: e => { e.preventDefault(); this._insertText('during:') } }, + { label: <>after: , action: e => { e.preventDefault(); this._insertText('after:') } }, + ]; + setRef = c => { this.searchForm = c; }; @@ -100,7 +106,7 @@ class Search extends PureComponent { handleKeyDown = (e) => { const { selectedOption } = this.state; - const options = this._getOptions(); + const options = this._getOptions().concat(this.defaultOptions); switch(e.key) { case 'Escape': @@ -131,10 +137,9 @@ class Search extends PureComponent { if (selectedOption === -1) { this._submit(); } else if (options.length > 0) { - options[selectedOption].action(); + options[selectedOption].action(e); } - this._unfocus(); break; case 'Delete': if (selectedOption > -1 && options.length > 0) { @@ -161,6 +166,7 @@ class Search extends PureComponent { router.history.push(`/tags/${query}`); onClickSearchResult(query, 'hashtag'); + this._unfocus(); }; handleAccountClick = () => { @@ -171,6 +177,7 @@ class Search extends PureComponent { router.history.push(`/@${query}`); onClickSearchResult(query, 'account'); + this._unfocus(); }; handleURLClick = () => { @@ -178,6 +185,7 @@ class Search extends PureComponent { const { onOpenURL } = this.props; onOpenURL(router.history); + this._unfocus(); }; handleStatusSearch = () => { @@ -196,6 +204,8 @@ class Search extends PureComponent { } else if (search.get('type') === 'hashtag') { router.history.push(`/tags/${search.get('q')}`); } + + this._unfocus(); }; handleForgetRecentSearchClick = search => { @@ -208,6 +218,18 @@ class Search extends PureComponent { document.querySelector('.ui').parentElement.focus(); } + _insertText (text) { + const { value, onChange } = this.props; + + if (value === '') { + onChange(text); + } else if (value[value.length - 1] === ' ') { + onChange(`${value}${text}`); + } else { + onChange(`${value} ${text}`); + } + } + _submit (type) { const { onSubmit, openInRoute } = this.props; const { router } = this.context; @@ -217,6 +239,8 @@ class Search extends PureComponent { if (openInRoute) { router.history.push('/search'); } + + this._unfocus(); } _getOptions () { @@ -337,6 +361,16 @@ class Search extends PureComponent { )} + +

+ +
+ {this.defaultOptions.map(({ key, label, action }, i) => ( + + ))} +
); diff --git a/app/javascript/flavours/glitch/styles/components/search.scss b/app/javascript/flavours/glitch/styles/components/search.scss index 99ed697e3..be35077dc 100644 --- a/app/javascript/flavours/glitch/styles/components/search.scss +++ b/app/javascript/flavours/glitch/styles/components/search.scss @@ -25,6 +25,12 @@ } &__menu { + margin-bottom: 20px; + + &:last-child { + margin-bottom: 0; + } + &__message { color: $dark-text-color; padding: 0 10px; From 871ab8485434d9c89dddb14a2ee1fda8ad60035e Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Tue, 29 Aug 2023 03:56:19 -0500 Subject: [PATCH 73/81] [Glitch] Fix bug with favourited view on Toots only showing latest favouriting accounts Port ae6cf33321a9f240ef73666a552e552b65390012 to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/actions/interactions.js | 58 ++++++++++++++++++- .../glitch/features/favourites/index.jsx | 25 +++++--- .../flavours/glitch/reducers/user_lists.js | 15 ++++- 3 files changed, 85 insertions(+), 13 deletions(-) diff --git a/app/javascript/flavours/glitch/actions/interactions.js b/app/javascript/flavours/glitch/actions/interactions.js index 6b8864a03..78f7741ab 100644 --- a/app/javascript/flavours/glitch/actions/interactions.js +++ b/app/javascript/flavours/glitch/actions/interactions.js @@ -1,5 +1,6 @@ -import api from '../api'; +import api, { getLinks } from '../api'; +import { fetchRelationships } from './accounts'; import { importFetchedAccounts, importFetchedStatus } from './importer'; export const REBLOG_REQUEST = 'REBLOG_REQUEST'; @@ -26,6 +27,10 @@ export const FAVOURITES_FETCH_REQUEST = 'FAVOURITES_FETCH_REQUEST'; export const FAVOURITES_FETCH_SUCCESS = 'FAVOURITES_FETCH_SUCCESS'; export const FAVOURITES_FETCH_FAIL = 'FAVOURITES_FETCH_FAIL'; +export const FAVOURITES_EXPAND_REQUEST = 'FAVOURITES_EXPAND_REQUEST'; +export const FAVOURITES_EXPAND_SUCCESS = 'FAVOURITES_EXPAND_SUCCESS'; +export const FAVOURITES_EXPAND_FAIL = 'FAVOURITES_EXPAND_FAIL'; + export const PIN_REQUEST = 'PIN_REQUEST'; export const PIN_SUCCESS = 'PIN_SUCCESS'; export const PIN_FAIL = 'PIN_FAIL'; @@ -294,8 +299,10 @@ export function fetchFavourites(id) { dispatch(fetchFavouritesRequest(id)); api(getState).get(`/api/v1/statuses/${id}/favourited_by`).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); dispatch(importFetchedAccounts(response.data)); - dispatch(fetchFavouritesSuccess(id, response.data)); + dispatch(fetchFavouritesSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); }).catch(error => { dispatch(fetchFavouritesFail(id, error)); }); @@ -309,17 +316,62 @@ export function fetchFavouritesRequest(id) { }; } -export function fetchFavouritesSuccess(id, accounts) { +export function fetchFavouritesSuccess(id, accounts, next) { return { type: FAVOURITES_FETCH_SUCCESS, id, accounts, + next, }; } export function fetchFavouritesFail(id, error) { return { type: FAVOURITES_FETCH_FAIL, + id, + error, + }; +} + +export function expandFavourites(id) { + return (dispatch, getState) => { + const url = getState().getIn(['user_lists', 'favourited_by', id, 'next']); + if (url === null) { + return; + } + + dispatch(expandFavouritesRequest(id)); + + api(getState).get(url).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + + dispatch(importFetchedAccounts(response.data)); + dispatch(expandFavouritesSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); + }).catch(error => dispatch(expandFavouritesFail(id, error))); + }; +} + +export function expandFavouritesRequest(id) { + return { + type: FAVOURITES_EXPAND_REQUEST, + id, + }; +} + +export function expandFavouritesSuccess(id, accounts, next) { + return { + type: FAVOURITES_EXPAND_SUCCESS, + id, + accounts, + next, + }; +} + +export function expandFavouritesFail(id, error) { + return { + type: FAVOURITES_EXPAND_FAIL, + id, error, }; } diff --git a/app/javascript/flavours/glitch/features/favourites/index.jsx b/app/javascript/flavours/glitch/features/favourites/index.jsx index 2b36945ee..49fd62b96 100644 --- a/app/javascript/flavours/glitch/features/favourites/index.jsx +++ b/app/javascript/flavours/glitch/features/favourites/index.jsx @@ -8,7 +8,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { connect } from 'react-redux'; -import { fetchFavourites } from 'flavours/glitch/actions/interactions'; +import { debounce } from 'lodash'; + +import { fetchFavourites, expandFavourites } from 'flavours/glitch/actions/interactions'; import ColumnHeader from 'flavours/glitch/components/column_header'; import { Icon } from 'flavours/glitch/components/icon'; import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator'; @@ -23,7 +25,9 @@ const messages = defineMessages({ }); const mapStateToProps = (state, props) => ({ - accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]), + accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'items']), + hasMore: !!state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'next']), + isLoading: state.getIn(['user_lists', 'favourited_by', props.params.statusId, 'isLoading'], true), }); class Favourites extends ImmutablePureComponent { @@ -32,6 +36,8 @@ class Favourites extends ImmutablePureComponent { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, accountIds: ImmutablePropTypes.list, + hasMore: PropTypes.bool, + isLoading: PropTypes.bool, multiColumn: PropTypes.bool, intl: PropTypes.object.isRequired, }; @@ -42,12 +48,6 @@ class Favourites extends ImmutablePureComponent { } } - UNSAFE_componentWillReceiveProps (nextProps) { - if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) { - this.props.dispatch(fetchFavourites(nextProps.params.statusId)); - } - } - handleHeaderClick = () => { this.column.scrollTop(); }; @@ -60,8 +60,12 @@ class Favourites extends ImmutablePureComponent { this.props.dispatch(fetchFavourites(this.props.params.statusId)); }; + handleLoadMore = debounce(() => { + this.props.dispatch(expandFavourites(this.props.params.statusId)); + }, 300, { leading: true }); + render () { - const { intl, accountIds, multiColumn } = this.props; + const { intl, accountIds, hasMore, isLoading, multiColumn } = this.props; if (!accountIds) { return ( @@ -87,6 +91,9 @@ class Favourites extends ImmutablePureComponent { /> diff --git a/app/javascript/flavours/glitch/reducers/user_lists.js b/app/javascript/flavours/glitch/reducers/user_lists.js index dd240e99d..a10fed433 100644 --- a/app/javascript/flavours/glitch/reducers/user_lists.js +++ b/app/javascript/flavours/glitch/reducers/user_lists.js @@ -45,7 +45,12 @@ import { } from 'flavours/glitch/actions/featured_tags'; import { REBLOGS_FETCH_SUCCESS, + FAVOURITES_FETCH_REQUEST, FAVOURITES_FETCH_SUCCESS, + FAVOURITES_FETCH_FAIL, + FAVOURITES_EXPAND_REQUEST, + FAVOURITES_EXPAND_SUCCESS, + FAVOURITES_EXPAND_FAIL, } from 'flavours/glitch/actions/interactions'; import { MUTES_FETCH_REQUEST, @@ -135,7 +140,15 @@ export default function userLists(state = initialState, action) { case REBLOGS_FETCH_SUCCESS: return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id))); case FAVOURITES_FETCH_SUCCESS: - return state.setIn(['favourited_by', action.id], ImmutableList(action.accounts.map(item => item.id))); + return normalizeList(state, ['favourited_by', action.id], action.accounts, action.next); + case FAVOURITES_EXPAND_SUCCESS: + return appendToList(state, ['favourited_by', action.id], action.accounts, action.next); + case FAVOURITES_FETCH_REQUEST: + case FAVOURITES_EXPAND_REQUEST: + return state.setIn(['favourited_by', action.id, 'isLoading'], true); + case FAVOURITES_FETCH_FAIL: + case FAVOURITES_EXPAND_FAIL: + return state.setIn(['favourited_by', action.id, 'isLoading'], false); case NOTIFICATIONS_UPDATE: return action.notification.type === 'follow_request' ? normalizeFollowRequest(state, action.notification) : state; case FOLLOW_REQUESTS_FETCH_SUCCESS: From beb5fcd0dc158218fd3c82e7d545a53f7dc904b7 Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Tue, 29 Aug 2023 07:42:20 -0500 Subject: [PATCH 74/81] [Glitch] Fix bug with reblogged view on Toots only showing latest reblogging accounts Port 74eb7dbf2d79b74f7d6f09ca3d39b3ba67f5f7bf to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/actions/interactions.js | 55 ++++++++++++++++++- .../glitch/features/reblogs/index.jsx | 29 +++++----- .../flavours/glitch/reducers/user_lists.js | 15 ++++- 3 files changed, 83 insertions(+), 16 deletions(-) diff --git a/app/javascript/flavours/glitch/actions/interactions.js b/app/javascript/flavours/glitch/actions/interactions.js index 78f7741ab..095fb3155 100644 --- a/app/javascript/flavours/glitch/actions/interactions.js +++ b/app/javascript/flavours/glitch/actions/interactions.js @@ -7,6 +7,10 @@ export const REBLOG_REQUEST = 'REBLOG_REQUEST'; export const REBLOG_SUCCESS = 'REBLOG_SUCCESS'; export const REBLOG_FAIL = 'REBLOG_FAIL'; +export const REBLOGS_EXPAND_REQUEST = 'REBLOGS_EXPAND_REQUEST'; +export const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS'; +export const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL'; + export const FAVOURITE_REQUEST = 'FAVOURITE_REQUEST'; export const FAVOURITE_SUCCESS = 'FAVOURITE_SUCCESS'; export const FAVOURITE_FAIL = 'FAVOURITE_FAIL'; @@ -264,8 +268,10 @@ export function fetchReblogs(id) { dispatch(fetchReblogsRequest(id)); api(getState).get(`/api/v1/statuses/${id}/reblogged_by`).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); dispatch(importFetchedAccounts(response.data)); - dispatch(fetchReblogsSuccess(id, response.data)); + dispatch(fetchReblogsSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); }).catch(error => { dispatch(fetchReblogsFail(id, error)); }); @@ -279,17 +285,62 @@ export function fetchReblogsRequest(id) { }; } -export function fetchReblogsSuccess(id, accounts) { +export function fetchReblogsSuccess(id, accounts, next) { return { type: REBLOGS_FETCH_SUCCESS, id, accounts, + next, }; } export function fetchReblogsFail(id, error) { return { type: REBLOGS_FETCH_FAIL, + id, + error, + }; +} + +export function expandReblogs(id) { + return (dispatch, getState) => { + const url = getState().getIn(['user_lists', 'reblogged_by', id, 'next']); + if (url === null) { + return; + } + + dispatch(expandReblogsRequest(id)); + + api(getState).get(url).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + + dispatch(importFetchedAccounts(response.data)); + dispatch(expandReblogsSuccess(id, response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map(item => item.id))); + }).catch(error => dispatch(expandReblogsFail(id, error))); + }; +} + +export function expandReblogsRequest(id) { + return { + type: REBLOGS_EXPAND_REQUEST, + id, + }; +} + +export function expandReblogsSuccess(id, accounts, next) { + return { + type: REBLOGS_EXPAND_SUCCESS, + id, + accounts, + next, + }; +} + +export function expandReblogsFail(id, error) { + return { + type: REBLOGS_EXPAND_FAIL, + id, error, }; } diff --git a/app/javascript/flavours/glitch/features/reblogs/index.jsx b/app/javascript/flavours/glitch/features/reblogs/index.jsx index 90d10db62..8cc4c004f 100644 --- a/app/javascript/flavours/glitch/features/reblogs/index.jsx +++ b/app/javascript/flavours/glitch/features/reblogs/index.jsx @@ -8,7 +8,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { connect } from 'react-redux'; -import { fetchReblogs } from 'flavours/glitch/actions/interactions'; +import { debounce } from 'lodash'; + +import { fetchReblogs, expandReblogs } from 'flavours/glitch/actions/interactions'; import ColumnHeader from 'flavours/glitch/components/column_header'; import { Icon } from 'flavours/glitch/components/icon'; import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator'; @@ -16,17 +18,15 @@ import ScrollableList from 'flavours/glitch/components/scrollable_list'; import AccountContainer from 'flavours/glitch/containers/account_container'; import Column from 'flavours/glitch/features/ui/components/column'; - - - - const messages = defineMessages({ heading: { id: 'column.reblogged_by', defaultMessage: 'Boosted by' }, refresh: { id: 'refresh', defaultMessage: 'Refresh' }, }); const mapStateToProps = (state, props) => ({ - accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId]), + accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId, 'items']), + hasMore: !!state.getIn(['user_lists', 'reblogged_by', props.params.statusId, 'next']), + isLoading: state.getIn(['user_lists', 'reblogged_by', props.params.statusId, 'isLoading'], true), }); class Reblogs extends ImmutablePureComponent { @@ -35,6 +35,8 @@ class Reblogs extends ImmutablePureComponent { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, accountIds: ImmutablePropTypes.list, + hasMore: PropTypes.bool, + isLoading: PropTypes.bool, multiColumn: PropTypes.bool, intl: PropTypes.object.isRequired, }; @@ -45,12 +47,6 @@ class Reblogs extends ImmutablePureComponent { } } - UNSAFE_componentWillReceiveProps(nextProps) { - if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) { - this.props.dispatch(fetchReblogs(nextProps.params.statusId)); - } - } - handleHeaderClick = () => { this.column.scrollTop(); }; @@ -63,8 +59,12 @@ class Reblogs extends ImmutablePureComponent { this.props.dispatch(fetchReblogs(this.props.params.statusId)); }; + handleLoadMore = debounce(() => { + this.props.dispatch(expandReblogs(this.props.params.statusId)); + }, 300, { leading: true }); + render () { - const { intl, accountIds, multiColumn } = this.props; + const { intl, accountIds, hasMore, isLoading, multiColumn } = this.props; if (!accountIds) { return ( @@ -91,6 +91,9 @@ class Reblogs extends ImmutablePureComponent { diff --git a/app/javascript/flavours/glitch/reducers/user_lists.js b/app/javascript/flavours/glitch/reducers/user_lists.js index a10fed433..d37451d00 100644 --- a/app/javascript/flavours/glitch/reducers/user_lists.js +++ b/app/javascript/flavours/glitch/reducers/user_lists.js @@ -44,7 +44,12 @@ import { FEATURED_TAGS_FETCH_FAIL, } from 'flavours/glitch/actions/featured_tags'; import { + REBLOGS_FETCH_REQUEST, REBLOGS_FETCH_SUCCESS, + REBLOGS_FETCH_FAIL, + REBLOGS_EXPAND_REQUEST, + REBLOGS_EXPAND_SUCCESS, + REBLOGS_EXPAND_FAIL, FAVOURITES_FETCH_REQUEST, FAVOURITES_FETCH_SUCCESS, FAVOURITES_FETCH_FAIL, @@ -138,7 +143,15 @@ export default function userLists(state = initialState, action) { case FOLLOWING_EXPAND_FAIL: return state.setIn(['following', action.id, 'isLoading'], false); case REBLOGS_FETCH_SUCCESS: - return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id))); + return normalizeList(state, ['reblogged_by', action.id], action.accounts, action.next); + case REBLOGS_EXPAND_SUCCESS: + return appendToList(state, ['reblogged_by', action.id], action.accounts, action.next); + case REBLOGS_FETCH_REQUEST: + case REBLOGS_EXPAND_REQUEST: + return state.setIn(['reblogged_by', action.id, 'isLoading'], true); + case REBLOGS_FETCH_FAIL: + case REBLOGS_EXPAND_FAIL: + return state.setIn(['reblogged_by', action.id, 'isLoading'], false); case FAVOURITES_FETCH_SUCCESS: return normalizeList(state, ['favourited_by', action.id], action.accounts, action.next); case FAVOURITES_EXPAND_SUCCESS: From f5bd2014e2fc5960bfeb9bda9735ac75f455b87d Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 30 Aug 2023 16:29:52 +0200 Subject: [PATCH 75/81] [Glitch] Fix sign up steps progress layout in right-to-left locales Port 21ec596dabbe28a5cfc5b29362f46f47b48eb140 to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/styles/forms.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/flavours/glitch/styles/forms.scss b/app/javascript/flavours/glitch/styles/forms.scss index 2b7c02f11..7d2f5c1bd 100644 --- a/app/javascript/flavours/glitch/styles/forms.scss +++ b/app/javascript/flavours/glitch/styles/forms.scss @@ -1187,14 +1187,14 @@ code { } li:first-child .label { - left: auto; inset-inline-start: 0; + inset-inline-end: auto; text-align: start; transform: none; } li:last-child .label { - left: auto; + inset-inline-start: auto; inset-inline-end: 0; text-align: end; transform: none; From 6c05ac4068f2c942331458c00bbb8a835f0f56e5 Mon Sep 17 00:00:00 2001 From: Stanislas Signoud Date: Thu, 31 Aug 2023 12:18:46 +0200 Subject: [PATCH 76/81] [Glitch] Add an explanation banner on switching to single column mode Port 40b69cc1cd62fa57047905a6999985ae6c183f6c to glitch-soc Signed-off-by: Claire --- .../features/ui/components/navigation_panel.jsx | 11 ++++++++--- .../glitch/styles/components/columns.scss | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx index c0726404e..f6984d5ad 100644 --- a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx @@ -29,6 +29,7 @@ const messages = defineMessages({ about: { id: 'navigation_bar.about', defaultMessage: 'About' }, search: { id: 'navigation_bar.search', defaultMessage: 'Search' }, advancedInterface: { id: 'navigation_bar.advanced_interface', defaultMessage: 'Open in advanced web interface' }, + openedInClassicInterface: { id: 'navigation_bar.opened_in_classic_interface', defaultMessage: 'Posts, accounts, and other specific pages are opened by default in the classic web interface.' }, app_settings: { id: 'navigation_bar.app_settings', defaultMessage: 'App settings' }, }); @@ -56,9 +57,13 @@ class NavigationPanel extends Component {
{transientSingleColumn && (
- - {intl.formatMessage(messages.advancedInterface)} - +
+ {intl.formatMessage(messages.openedInClassicInterface)} + {" "} + + {intl.formatMessage(messages.advancedInterface)} + +

)} diff --git a/app/javascript/flavours/glitch/styles/components/columns.scss b/app/javascript/flavours/glitch/styles/components/columns.scss index d4860258e..6c9713796 100644 --- a/app/javascript/flavours/glitch/styles/components/columns.scss +++ b/app/javascript/flavours/glitch/styles/components/columns.scss @@ -228,6 +228,22 @@ $ui-header-height: 55px; top: -48px; } +.switch-to-advanced { + color: $classic-primary-color; + background-color: $classic-base-color; + padding: 15px; + border-radius: 4px; + margin-top: 4px; + margin-bottom: 12px; + font-size: 13px; + line-height: 18px; + + .switch-to-advanced__toggle { + color: $ui-button-tertiary-color; + font-weight: bold; + } +} + .column-link { background: lighten($ui-base-color, 8%); color: $primary-text-color; From 64e661b93058ca55cf1278cd09417d01edfba4b8 Mon Sep 17 00:00:00 2001 From: Santiago Kozak Date: Thu, 31 Aug 2023 09:15:58 -0300 Subject: [PATCH 77/81] [Glitch] Allow filter form in profiles directory to wrap Port f1d250135ccf9be5c4d982a2c48417da89d38eb5 to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/styles/components/single_column.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/flavours/glitch/styles/components/single_column.scss b/app/javascript/flavours/glitch/styles/components/single_column.scss index 2f8f7e2dd..7efcf0c09 100644 --- a/app/javascript/flavours/glitch/styles/components/single_column.scss +++ b/app/javascript/flavours/glitch/styles/components/single_column.scss @@ -120,6 +120,7 @@ .filter-form { display: flex; + flex-wrap: wrap; } .autosuggest-textarea__textarea { From 807c0591e177ce8ec70298aabd1df3c89c38e19d Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 1 Sep 2023 15:13:27 +0200 Subject: [PATCH 78/81] [Glitch] Fix search popout including full-text search instructions when full-text search is disabled Port 6c4c72497a5722870e4432ef41dd4c9ec36a8928 to glitch-soc Signed-off-by: Claire --- .../features/compose/components/search.jsx | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/app/javascript/flavours/glitch/features/compose/components/search.jsx b/app/javascript/flavours/glitch/features/compose/components/search.jsx index 218518a7b..e83c1761e 100644 --- a/app/javascript/flavours/glitch/features/compose/components/search.jsx +++ b/app/javascript/flavours/glitch/features/compose/components/search.jsx @@ -106,7 +106,7 @@ class Search extends PureComponent { handleKeyDown = (e) => { const { selectedOption } = this.state; - const options = this._getOptions().concat(this.defaultOptions); + const options = searchEnabled ? this._getOptions().concat(this.defaultOptions) : this._getOptions(); switch(e.key) { case 'Escape': @@ -362,15 +362,19 @@ class Search extends PureComponent { )} -

+ {searchEnabled && ( + <> +

-
- {this.defaultOptions.map(({ key, label, action }, i) => ( - - ))} -
+
+ {this.defaultOptions.map(({ key, label, action }, i) => ( + + ))} +
+ + )}
); From 4adb12ca9157ae0efc0b813df3a09ef8f6d579de Mon Sep 17 00:00:00 2001 From: Claire Date: Sat, 2 Sep 2023 14:23:18 +0200 Subject: [PATCH 79/81] Fix test failures due to different default settings in glitch-soc --- spec/controllers/api/v1/timelines/tag_controller_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/controllers/api/v1/timelines/tag_controller_spec.rb b/spec/controllers/api/v1/timelines/tag_controller_spec.rb index 1c60798fc..8896f02a7 100644 --- a/spec/controllers/api/v1/timelines/tag_controller_spec.rb +++ b/spec/controllers/api/v1/timelines/tag_controller_spec.rb @@ -22,6 +22,10 @@ describe Api::V1::Timelines::TagController do end context 'when the instance allows public preview' do + before do + Setting.timeline_preview = true + end + context 'when the user is not authenticated' do let(:token) { nil } From 4371df7fbe226179cc2520bfce05d18a1cdfbba1 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 1 Sep 2023 15:41:10 +0200 Subject: [PATCH 80/81] [Glitch] Add `authorized_fetch` server setting in addition to env var Port SCSS changes from 9e26cd55038084638fdf71b75f526494777d2849 to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/styles/accounts.scss | 2 ++ app/javascript/flavours/glitch/styles/forms.scss | 1 + 2 files changed, 3 insertions(+) diff --git a/app/javascript/flavours/glitch/styles/accounts.scss b/app/javascript/flavours/glitch/styles/accounts.scss index ad0dfe017..b0fe21bcf 100644 --- a/app/javascript/flavours/glitch/styles/accounts.scss +++ b/app/javascript/flavours/glitch/styles/accounts.scss @@ -192,6 +192,8 @@ } .account-role, +.information-badge, +.simple_form .overridden, .simple_form .recommended, .simple_form .not_recommended, .simple_form .glitch_only { diff --git a/app/javascript/flavours/glitch/styles/forms.scss b/app/javascript/flavours/glitch/styles/forms.scss index 7d2f5c1bd..b8fc4a653 100644 --- a/app/javascript/flavours/glitch/styles/forms.scss +++ b/app/javascript/flavours/glitch/styles/forms.scss @@ -103,6 +103,7 @@ code { } } + .overridden, .recommended, .not_recommended, .glitch_only { From 5c7df20c13ebd3aef2fa1580d46058b683d30971 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 1 Sep 2023 17:47:07 +0200 Subject: [PATCH 81/81] [Glitch] Add admin notifications for new Mastodon versions Port front-end changes from 16681e0f20e1f8584e11439953c8d59b322571f5 to glitch-soc Signed-off-by: Claire --- .../components/critical_update_banner.tsx | 26 +++++++++++++++++++ .../glitch/features/home_timeline/index.jsx | 14 +++++++--- .../flavours/glitch/initial_state.js | 2 ++ .../flavours/glitch/styles/admin.scss | 5 ++++ .../glitch/styles/components/columns.scss | 18 ++++++++++++- .../flavours/glitch/styles/tables.scss | 5 ++++ 6 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 app/javascript/flavours/glitch/features/home_timeline/components/critical_update_banner.tsx diff --git a/app/javascript/flavours/glitch/features/home_timeline/components/critical_update_banner.tsx b/app/javascript/flavours/glitch/features/home_timeline/components/critical_update_banner.tsx new file mode 100644 index 000000000..d0dd2b6ac --- /dev/null +++ b/app/javascript/flavours/glitch/features/home_timeline/components/critical_update_banner.tsx @@ -0,0 +1,26 @@ +import { FormattedMessage } from 'react-intl'; + +export const CriticalUpdateBanner = () => ( +
+
+

+ +

+

+ {' '} + + + +

+
+
+); diff --git a/app/javascript/flavours/glitch/features/home_timeline/index.jsx b/app/javascript/flavours/glitch/features/home_timeline/index.jsx index e17680d8b..80dae5e4d 100644 --- a/app/javascript/flavours/glitch/features/home_timeline/index.jsx +++ b/app/javascript/flavours/glitch/features/home_timeline/index.jsx @@ -14,7 +14,7 @@ import { fetchAnnouncements, toggleShowAnnouncements } from 'flavours/glitch/act import { IconWithBadge } from 'flavours/glitch/components/icon_with_badge'; import { NotSignedInIndicator } from 'flavours/glitch/components/not_signed_in_indicator'; import AnnouncementsContainer from 'flavours/glitch/features/getting_started/containers/announcements_container'; -import { me } from 'flavours/glitch/initial_state'; +import { me, criticalUpdatesPending } from 'flavours/glitch/initial_state'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { expandHomeTimeline } from '../../actions/timelines'; @@ -23,6 +23,7 @@ import ColumnHeader from '../../components/column_header'; import StatusListContainer from '../ui/containers/status_list_container'; import { ColumnSettings } from './components/column_settings'; +import { CriticalUpdateBanner } from './components/critical_update_banner'; import { ExplorePrompt } from './components/explore_prompt'; const messages = defineMessages({ @@ -158,8 +159,9 @@ class HomeTimeline extends PureComponent { const { intl, hasUnread, columnId, multiColumn, tooSlow, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props; const pinned = !!columnId; const { signedIn } = this.context.identity; + const banners = []; - let announcementsButton, banner; + let announcementsButton; if (hasAnnouncements) { announcementsButton = ( @@ -174,8 +176,12 @@ class HomeTimeline extends PureComponent { ); } + if (criticalUpdatesPending) { + banners.push(); + } + if (tooSlow) { - banner = ; + banners.push(); } return ( @@ -197,7 +203,7 @@ class HomeTimeline extends PureComponent { {signedIn ? ( } accounts * @property {InitialStateLanguage[]} languages + * @property {boolean=} critical_updates_pending * @property {InitialStateMeta} meta * @property {object} local_settings * @property {number} max_toot_chars @@ -160,6 +161,7 @@ export const useBlurhash = getMeta('use_blurhash'); export const usePendingItems = getMeta('use_pending_items'); export const version = getMeta('version'); export const languages = initialState?.languages; +export const criticalUpdatesPending = initialState?.critical_updates_pending; export const statusPageUrl = getMeta('status_page_url'); export const sso_redirect = getMeta('sso_redirect'); diff --git a/app/javascript/flavours/glitch/styles/admin.scss b/app/javascript/flavours/glitch/styles/admin.scss index 7adeaeee0..2f4027b03 100644 --- a/app/javascript/flavours/glitch/styles/admin.scss +++ b/app/javascript/flavours/glitch/styles/admin.scss @@ -143,6 +143,11 @@ $content-width: 840px; } } + .warning a { + color: $gold-star; + font-weight: 700; + } + .simple-navigation-active-leaf a { color: $primary-text-color; background-color: $ui-highlight-color; diff --git a/app/javascript/flavours/glitch/styles/components/columns.scss b/app/javascript/flavours/glitch/styles/components/columns.scss index 6c9713796..39b9bd291 100644 --- a/app/javascript/flavours/glitch/styles/components/columns.scss +++ b/app/javascript/flavours/glitch/styles/components/columns.scss @@ -977,7 +977,8 @@ $ui-header-height: 55px; } } -.dismissable-banner { +.dismissable-banner, +.warning-banner { position: relative; margin: 10px; margin-bottom: 5px; @@ -1055,6 +1056,21 @@ $ui-header-height: 55px; } } +.warning-banner { + border: 1px solid $warning-red; + background: rgba($warning-red, 0.15); + + &__message { + h1 { + color: $warning-red; + } + + a { + color: $primary-text-color; + } + } +} + .hashtag-header { border-bottom: 1px solid lighten($ui-base-color, 8%); padding: 15px; diff --git a/app/javascript/flavours/glitch/styles/tables.scss b/app/javascript/flavours/glitch/styles/tables.scss index b583d3d8e..44ef00ba7 100644 --- a/app/javascript/flavours/glitch/styles/tables.scss +++ b/app/javascript/flavours/glitch/styles/tables.scss @@ -12,6 +12,11 @@ border-top: 1px solid $ui-base-color; text-align: start; background: darken($ui-base-color, 4%); + + &.critical { + font-weight: 700; + color: $gold-star; + } } & > thead > tr > th {