Merge branch 'chinwag-next'

This commit is contained in:
Mike Barnes 2025-09-17 20:31:48 +10:00
commit 095ce4fb34
2690 changed files with 96825 additions and 58372 deletions

View file

@ -1,6 +1,5 @@
# frozen_string_literal: true
require 'set'
require_relative 'base'
module Mastodon::CLI
@ -80,7 +79,14 @@ module Mastodon::CLI
account = Account.new(username: username)
password = SecureRandom.hex
user = User.new(email: options[:email], password: password, agreement: true, role_id: role_id, confirmed_at: options[:confirmed] ? Time.now.utc : nil, bypass_invite_request_check: true)
user = User.new(
email: options[:email],
password: password,
agreement: true,
role_id: role_id,
confirmed_at: options[:confirmed] ? Time.now.utc : nil,
bypass_registration_checks: true
)
if options[:reattach]
account = Account.find_local(username) || Account.new(username: username)
@ -165,7 +171,7 @@ module Mastodon::CLI
user.disabled = false if options[:enable]
user.disabled = true if options[:disable]
user.approved = true if options[:approve]
user.otp_required_for_login = false if options[:disable_2fa]
user.disable_two_factor! if options[:disable_2fa]
if user.save
user.confirm if options[:confirm]
@ -305,7 +311,7 @@ module Mastodon::CLI
begin
code = Request.new(:head, account.uri).perform(&:code)
rescue HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, Mastodon::PrivateNetworkAddressError
rescue *Mastodon::HTTP_CONNECTION_ERRORS, Mastodon::PrivateNetworkAddressError
skip_domains << account.domain
end
@ -322,7 +328,9 @@ module Mastodon::CLI
unless skip_domains.empty?
say('The following domains were not available during the check:', :yellow)
skip_domains.each { |domain| say(" #{domain}") }
shell.indent(2) do
skip_domains.each { |domain| say(domain) }
end
end
end

View file

@ -52,7 +52,7 @@ module Mastodon::CLI
account.account_stat.tap do |account_stat|
account_stat.following_count = account.active_relationships.count
account_stat.followers_count = account.passive_relationships.count
account_stat.statuses_count = account.statuses.where.not(visibility: :direct).count
account_stat.statuses_count = account.statuses.not_direct_visibility.count
account_stat.save if account_stat.changed?
end
@ -60,7 +60,7 @@ module Mastodon::CLI
def recount_status_stats(status)
status.status_stat.tap do |status_stat|
status_stat.replies_count = status.replies.where.not(visibility: :direct).count
status_stat.replies_count = status.replies.not_direct_visibility.count
status_stat.reblogs_count = status.reblogs.count
status_stat.favourites_count = status.favourites.count

View file

@ -7,11 +7,13 @@ module Mastodon::CLI
class EmailDomainBlocks < Base
desc 'list', 'List blocked e-mail domains'
def list
EmailDomainBlock.where(parent_id: nil).find_each do |entry|
say(entry.domain.to_s, :white)
EmailDomainBlock.parents.find_each do |parent|
say(parent.domain.to_s, :white)
EmailDomainBlock.where(parent_id: entry.id).find_each do |child|
say(" #{child.domain}", :cyan)
shell.indent do
EmailDomainBlock.where(parent_id: parent.id).find_each do |child|
say(child.domain, :cyan)
end
end
end
end
@ -43,12 +45,7 @@ module Mastodon::CLI
end
other_domains = []
if options[:with_dns_records]
Resolv::DNS.open do |dns|
dns.timeouts = 5
other_domains = dns.getresources(domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s }.compact_blank
end
end
other_domains = DomainResource.new(domain).mx if options[:with_dns_records]
email_domain_block = EmailDomainBlock.new(domain: domain, other_domains: other_domains)
email_domain_block.save!

View file

@ -62,7 +62,9 @@ module Mastodon::CLI
failed += 1
say('Failure/Error: ', :red)
say(entry.full_name)
say(" #{custom_emoji.errors[:image].join(', ')}", :red)
shell.indent(2) do
say(custom_emoji.errors[:image].join(', '), :red)
end
end
end
end

View file

@ -76,7 +76,7 @@ module Mastodon::CLI
def self_destruct_value
Rails
.application
.message_verifier('self-destruct')
.message_verifier(SelfDestructHelper::VERIFY_PURPOSE)
.generate(Rails.configuration.x.local_domain)
end
end

View file

@ -11,17 +11,21 @@ module Mastodon::CLI
option :concurrency, type: :numeric, default: 5, aliases: [:c]
option :verbose, type: :boolean, aliases: [:v]
option :dry_run, type: :boolean, default: false
option :skip_filled_timelines
desc 'build [USERNAME]', 'Build home and list feeds for one or all users'
long_desc <<-LONG_DESC
Build home and list feeds that are stored in Redis from the database.
With the --skip-filled-timelines, timelines which contain more than half
the maximum number of posts will be skipped.
With the --all option, all active users will be processed.
Otherwise, a single user specified by USERNAME.
LONG_DESC
def build(username = nil)
if options[:all] || username.nil?
processed, = parallelize_with_progress(active_user_accounts) do |account|
PrecomputeFeedService.new.call(account) unless dry_run?
PrecomputeFeedService.new.call(account, skip_filled_timelines: options[:skip_filled_timelines]) unless dry_run?
end
say("Regenerated feeds for #{processed} accounts #{dry_run_mode_suffix}", :green, true)
@ -30,7 +34,7 @@ module Mastodon::CLI
fail_with_message 'No such account' if account.nil?
PrecomputeFeedService.new.call(account) unless dry_run?
PrecomputeFeedService.new.call(account, skip_filled_timelines: options[:skip_filled_timelines]) unless dry_run?
say("OK #{dry_run_mode_suffix}", :green, true)
else

View file

@ -80,9 +80,9 @@ module Mastodon::CLI
end
ip_blocks = if options[:force]
IpBlock.where('ip >>= ?', address)
IpBlock.containing(address)
else
IpBlock.where('ip <<= ?', address)
IpBlock.contained_by(address)
end
if ip_blocks.empty?

View file

@ -192,6 +192,7 @@ module Mastodon::CLI
verify_schema_version!
verify_sidekiq_not_active!
verify_backup_warning!
disable_timeout!
end
def process_deduplications
@ -251,6 +252,13 @@ module Mastodon::CLI
fail_with_message 'Maintenance process stopped.' unless yes?('Continue? (Yes/No)')
end
def disable_timeout!
# Remove server-configured timeout if present
database_connection.execute(<<~SQL.squish)
SET statement_timeout = 0
SQL
end
def deduplicate_accounts!
remove_index_if_exists!(:accounts, 'index_accounts_on_username_and_domain_lower')

View file

@ -6,6 +6,8 @@ module Mastodon::CLI
class Media < Base
include ActionView::Helpers::NumberHelper
class UnrecognizedOrphanType < StandardError; end
VALID_PATH_SEGMENTS_SIZE = [7, 10].freeze
option :days, type: :numeric, default: 7, aliases: [:d]
@ -120,23 +122,10 @@ module Mastodon::CLI
object.acl.put(acl: s3_permissions) if options[:fix_permissions] && !dry_run?
path_segments = object.key.split('/')
path_segments.delete('cache')
unless VALID_PATH_SEGMENTS_SIZE.include?(path_segments.size)
progress.log(pastel.yellow("Unrecognized file found: #{object.key}"))
next
end
model_name = path_segments.first.classify
attachment_name = path_segments[1].singularize
record_id = path_segments[2...-2].join.to_i
file_name = path_segments.last
record = record_map.dig(model_name, record_id)
attachment = record&.public_send(attachment_name)
progress.increment
next unless attachment.blank? || !attachment.variant?(file_name)
next unless orphaned_file?(path_segments, record_map)
begin
object.delete unless dry_run?
@ -148,6 +137,8 @@ module Mastodon::CLI
rescue => e
progress.log(pastel.red("Error processing #{object.key}: #{e}"))
end
rescue UnrecognizedOrphanType
progress.log(pastel.yellow("Unrecognized file found: #{object.key}"))
end
end
when :fog
@ -165,26 +156,10 @@ module Mastodon::CLI
key = path.gsub("#{root_path}#{File::SEPARATOR}", '')
path_segments = key.split(File::SEPARATOR)
path_segments.delete('cache')
unless VALID_PATH_SEGMENTS_SIZE.include?(path_segments.size)
progress.log(pastel.yellow("Unrecognized file found: #{key}"))
next
end
model_name = path_segments.first.classify
record_id = path_segments[2...-2].join.to_i
attachment_name = path_segments[1].singularize
file_name = path_segments.last
next unless PRELOAD_MODEL_WHITELIST.include?(model_name)
record = model_name.constantize.find_by(id: record_id)
attachment = record&.public_send(attachment_name)
progress.increment
next unless attachment.blank? || !attachment.variant?(file_name)
next unless orphaned_file?(path_segments)
begin
size = File.size(path)
@ -205,6 +180,8 @@ module Mastodon::CLI
rescue => e
progress.log(pastel.red("Error processing #{key}: #{e}"))
end
rescue UnrecognizedOrphanType
progress.log(pastel.yellow("Unrecognized file found: #{path}"))
end
end
@ -278,14 +255,10 @@ module Mastodon::CLI
desc 'usage', 'Calculate disk space consumed by Mastodon'
def usage
say("Attachments:\t#{number_to_human_size(media_attachment_storage_size)} (#{number_to_human_size(local_media_attachment_storage_size)} local)")
say("Custom emoji:\t#{number_to_human_size(CustomEmoji.sum(:image_file_size))} (#{number_to_human_size(CustomEmoji.local.sum(:image_file_size))} local)")
say("Preview cards:\t#{number_to_human_size(PreviewCard.sum(:image_file_size))}")
say("Avatars:\t#{number_to_human_size(Account.sum(:avatar_file_size))} (#{number_to_human_size(Account.local.sum(:avatar_file_size))} local)")
say("Headers:\t#{number_to_human_size(Account.sum(:header_file_size))} (#{number_to_human_size(Account.local.sum(:header_file_size))} local)")
say("Backups:\t#{number_to_human_size(Backup.sum(:dump_file_size))}")
say("Imports:\t#{number_to_human_size(Import.sum(:data_file_size))}")
say("Settings:\t#{number_to_human_size(SiteUpload.sum(:file_file_size))}")
print_table [
%w(Object Total Local),
*object_storage_summary,
]
end
desc 'lookup URL', 'Lookup where media is displayed by passing a media URL'
@ -300,7 +273,7 @@ module Mastodon::CLI
model_name = path_segments.first.classify
record_id = path_segments[2...-2].join.to_i
fail_with_message "Cannot find corresponding model: #{model_name}" unless PRELOAD_MODEL_WHITELIST.include?(model_name)
fail_with_message "Cannot find corresponding model: #{model_name}" unless PRELOADED_MODELS.include?(model_name)
record = model_name.constantize.find_by(id: record_id)
record = record.status if record.respond_to?(:status)
@ -316,34 +289,35 @@ module Mastodon::CLI
fail_with_message 'Invalid URL'
end
private
def media_attachment_storage_size
MediaAttachment.sum(file_and_thumbnail_size_sql)
end
def local_media_attachment_storage_size
MediaAttachment.where(account: Account.local).sum(file_and_thumbnail_size_sql)
end
def file_and_thumbnail_size_sql
Arel.sql(
<<~SQL.squish
COALESCE(file_file_size, 0) + COALESCE(thumbnail_file_size, 0)
SQL
)
end
PRELOAD_MODEL_WHITELIST = %w(
PRELOADED_MODELS = %w(
Account
Backup
CustomEmoji
Import
MediaAttachment
PreviewCard
SiteUpload
).freeze
private
def object_storage_summary
[
[:attachments, MediaAttachment.sum(combined_media_sum), MediaAttachment.where(account: Account.local).sum(combined_media_sum)],
[:custom_emoji, CustomEmoji.sum(:image_file_size), CustomEmoji.local.sum(:image_file_size)],
[:avatars, Account.sum(:avatar_file_size), Account.local.sum(:avatar_file_size)],
[:headers, Account.sum(:header_file_size), Account.local.sum(:header_file_size)],
[:preview_cards, PreviewCard.sum(:image_file_size), nil],
[:backups, Backup.sum(:dump_file_size), nil],
[:settings, SiteUpload.sum(:file_file_size), nil],
].map { |label, total, local| [label.to_s.titleize, number_to_human_size(total), local.present? ? number_to_human_size(local) : nil] }
end
def combined_media_sum
Arel.sql(<<~SQL.squish)
COALESCE(file_file_size, 0) + COALESCE(thumbnail_file_size, 0)
SQL
end
def preload_records_from_mixed_objects(objects)
preload_map = Hash.new { |hash, key| hash[key] = [] }
@ -356,7 +330,7 @@ module Mastodon::CLI
model_name = segments.first.classify
record_id = segments[2...-2].join.to_i
next unless PRELOAD_MODEL_WHITELIST.include?(model_name)
next unless PRELOADED_MODELS.include?(model_name)
preload_map[model_name] << record_id
end
@ -365,5 +339,23 @@ module Mastodon::CLI
model_map[model_name] = model_name.constantize.where(id: record_ids).index_by(&:id)
end
end
def orphaned_file?(path_segments, record_map = nil)
path_segments.delete('cache')
raise UnrecognizedOrphanType unless VALID_PATH_SEGMENTS_SIZE.include?(path_segments.size)
model_name = path_segments.first.classify
record_id = path_segments[2...-2].join.to_i
attachment_name = path_segments[1].singularize
file_name = path_segments.last
raise UnrecognizedOrphanType unless PRELOADED_MODELS.include?(model_name)
record = record_map.present? ? record_map.dig(model_name, record_id) : model_name.constantize.find_by(id: record_id)
attachment = record&.public_send(attachment_name)
attachment.blank? || !attachment.variant?(file_name)
end
end
end

View file

@ -1,11 +1,11 @@
# frozen_string_literal: true
dev_null = Logger.new('/dev/null')
dev_null = Logger.new(File::NULL)
Rails.logger = dev_null
ActiveRecord::Base.logger = dev_null
ActiveJob::Base.logger = dev_null
HttpLog.configuration.logger = dev_null
HttpLog.configuration.logger = dev_null if defined?(HttpLog)
Paperclip.options[:log] = false
Chewy.logger = dev_null

View file

@ -20,6 +20,7 @@ module Mastodon::CLI
option :import, type: :boolean, default: true, desc: 'Import data from the database to the index'
option :clean, type: :boolean, default: true, desc: 'Remove outdated documents from the index'
option :reset_chewy, type: :boolean, default: false, desc: "Reset Chewy's internal index"
option :only_mapping, type: :boolean, default: false, desc: 'Update the index specification without re-index'
desc 'deploy', 'Create or upgrade Elasticsearch indices and populate them'
long_desc <<~LONG_DESC
If Elasticsearch is empty, this command will create the necessary indices
@ -52,6 +53,20 @@ module Mastodon::CLI
Chewy::Stash::Specification.reset! if options[:reset_chewy]
if options[:only_mapping]
indices.select { |index| index.specification.changed? }.each do |index|
progress.title = "Updating mapping for #{index} "
index.update_specification
index.specification.lock!
end
progress.title = 'Done! '
progress.finish
say('Updated index mappings', :green, true)
return
end
# First, ensure all indices are created and have the correct
# structure, so that live data can already be written
indices.select { |index| index.specification.changed? }.each do |index|

View file

@ -40,17 +40,11 @@ module Mastodon::CLI
def remove_statuses
return if options[:skip_status_remove]
say('Creating temporary database indices...')
ActiveRecord::Base.connection.add_index(:media_attachments, :remote_url, name: :index_media_attachments_remote_url, where: 'remote_url is not null', algorithm: :concurrently, if_not_exists: true)
max_id = Mastodon::Snowflake.id_at(options[:days].days.ago, with_random: false)
start_at = Time.now.to_f
unless options[:continue] && ActiveRecord::Base.connection.table_exists?('statuses_to_be_deleted')
ActiveRecord::Base.connection.add_index(:accounts, :id, name: :index_accounts_local, where: 'domain is null', algorithm: :concurrently, if_not_exists: true)
ActiveRecord::Base.connection.add_index(:status_pins, :status_id, name: :index_status_pins_status_id, algorithm: :concurrently, if_not_exists: true)
max_id = Mastodon::Snowflake.id_at(options[:days].days.ago, with_random: false)
unless options[:continue] && ActiveRecord::Base.connection.table_exists?('statuses_to_be_deleted')
say('Extract the deletion target from statuses... This might take a while...')
ActiveRecord::Base.connection.create_table('statuses_to_be_deleted', force: true)
@ -72,9 +66,6 @@ module Mastodon::CLI
SQL
say('Removing temporary database indices to restore write performance...')
ActiveRecord::Base.connection.remove_index(:accounts, name: :index_accounts_local, if_exists: true)
ActiveRecord::Base.connection.remove_index(:status_pins, name: :index_status_pins_status_id, if_exists: true)
end
say('Beginning statuses removal... This might take a while...')
@ -102,12 +93,6 @@ module Mastodon::CLI
ActiveRecord::Base.connection.drop_table('statuses_to_be_deleted')
say("Done after #{Time.now.to_f - start_at}s, removed #{removed} out of #{processed} statuses.", :green)
ensure
say('Removing temporary database indices to restore write performance...')
ActiveRecord::Base.connection.remove_index(:accounts, name: :index_accounts_local, if_exists: true)
ActiveRecord::Base.connection.remove_index(:status_pins, name: :index_status_pins_status_id, if_exists: true)
ActiveRecord::Base.connection.remove_index(:media_attachments, name: :index_media_attachments_remote_url, if_exists: true)
end
def remove_orphans_media_attachments

46
lib/mastodon/database.rb Normal file
View file

@ -0,0 +1,46 @@
# frozen_string_literal: true
# This file is entirely lifted from GitLab.
# The original file:
# https://gitlab.com/gitlab-org/gitlab/-/blob/69127d59467185cf4ff1109d88ceec1f499f354f/lib/gitlab/database.rb#L244-258
# Copyright (c) 2011-present GitLab B.V.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
module Mastodon
module Database
def self.add_post_migrate_path_to_rails(force: false)
return if ENV['SKIP_POST_DEPLOYMENT_MIGRATIONS'] && !force
Rails.application.config.paths['db'].each do |db_path|
path = Rails.root.join(db_path, 'post_migrate').to_s
next if Rails.application.config.paths['db/migrate'].include?(path)
Rails.application.config.paths['db/migrate'] << path
# Rails memoizes migrations at certain points where it won't read the above
# path just yet. As such we must also update the following list of paths.
ActiveRecord::Migrator.migrations_paths << path
end
end
end
end

View file

@ -0,0 +1,33 @@
# frozen_string_literal: true
module Mastodon
module EmailConfigurationHelper
module_function
# Convert smtp settings from environment variables (or defaults in
# `config/email.yml`) into the format that `ActionMailer` understands
def convert_smtp_settings(config)
enable_starttls = nil
enable_starttls_auto = nil
case config[:enable_starttls]
when 'always'
enable_starttls = true
when 'never'
enable_starttls = false
when 'auto'
enable_starttls_auto = true
else
enable_starttls_auto = config[:enable_starttls_auto] != 'false'
end
authentication = config[:authentication] == 'none' ? nil : (config[:authentication] || 'plain')
config.merge(
authentication:,
enable_starttls:,
enable_starttls_auto:
)
end
end
end

26
lib/mastodon/feature.rb Normal file
View file

@ -0,0 +1,26 @@
# frozen_string_literal: true
module Mastodon::Feature
class << self
def enabled_features
@enabled_features ||=
(Rails.configuration.x.mastodon.experimental_features || '').split(',').map(&:strip)
end
def method_missing(name)
if respond_to_missing?(name)
feature = name.to_s.delete_suffix('_enabled?')
enabled = enabled_features.include?(feature)
define_singleton_method(name) { enabled }
return enabled
end
super
end
def respond_to_missing?(name, include_all = false)
name.to_s.end_with?('_enabled?') || super
end
end
end

View file

@ -0,0 +1,24 @@
# frozen_string_literal: true
module Mastodon
module Middleware
class PrometheusQueueTime < ::PrometheusExporter::Middleware
# Overwrite to only collect the queue time metric
def call(env)
queue_time = measure_queue_time(env)
result = @app.call(env)
result
ensure
obj = {
type: 'web',
queue_time: queue_time,
default_labels: {},
}
@client.send_json(obj)
end
end
end
end

View file

@ -0,0 +1,51 @@
# frozen_string_literal: true
require 'action_dispatch/middleware/static'
module Mastodon
module Middleware
class PublicFileServer
SERVICE_WORKER_TTL = 7.days.to_i
CACHE_TTL = 28.days.to_i
def initialize(app)
@app = app
@file_handler = ActionDispatch::FileHandler.new(Rails.application.paths['public'].first)
end
def call(env)
file = @file_handler.attempt(env)
# If the request is not a static file, move on!
return @app.call(env) if file.nil?
status, headers, response = file
# Set cache headers on static files. Some paths require different cache headers
request = Rack::Request.new env
headers['cache-control'] = begin
if request.path.start_with?('/sw.js')
"public, max-age=#{SERVICE_WORKER_TTL}, must-revalidate"
elsif request.path.start_with?(paperclip_root_url)
"public, max-age=#{CACHE_TTL}, immutable"
else
"public, max-age=#{CACHE_TTL}, must-revalidate"
end
end
# Override the default CSP header set by the CSP middleware
headers['content-security-policy'] = "default-src 'none'; form-action 'none'" if request.path.start_with?(paperclip_root_url)
headers['x-content-type-options'] = 'nosniff'
[status, headers, response]
end
private
def paperclip_root_url
ENV.fetch('PAPERCLIP_ROOT_URL', '/system')
end
end
end
end

View file

@ -0,0 +1,34 @@
# frozen_string_literal: true
module Mastodon
module Middleware
class SocketCleanup
def initialize(app)
@app = app
end
def call(env)
@app.call(env)
ensure
clean_up_sockets!
end
private
def clean_up_sockets!
clean_up_redis_socket!
clean_up_statsd_socket!
end
def clean_up_redis_socket!
RedisConnection.pool.checkin if Thread.current[:redis]
Thread.current[:redis] = nil
end
def clean_up_statsd_socket!
Thread.current[:statsd_socket]&.close
Thread.current[:statsd_socket] = nil
end
end
end
end

View file

@ -4,7 +4,7 @@ module Mastodon
module MigrationWarning
WARNING_SECONDS = 10
DEFAULT_WARNING = <<~WARNING_MESSAGE
DEFAULT_WARNING = <<~WARNING_MESSAGE.freeze
WARNING: This migration may take a *long* time for large instances.
It will *not* lock tables for any significant time, but it may run
for a very long time. We will pause for #{WARNING_SECONDS} seconds to allow you to
@ -23,7 +23,7 @@ module Mastodon
def announce_countdown
WARNING_SECONDS.downto(1) do |i|
say "Continuing in #{i} second#{i == 1 ? '' : 's'}...", true
say "Continuing in #{i} second#{'s' unless i == 1}...", true
sleep 1
end
end

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
require 'prometheus_exporter/server'
require 'prometheus_exporter/client'
module Mastodon::PrometheusExporter
module LocalServer
mattr_accessor :bind, :port
def self.setup!
server = PrometheusExporter::Server::WebServer.new(bind:, port:)
server.start
# wire up a default local client
PrometheusExporter::Client.default = PrometheusExporter::LocalClient.new(collector: server.collector)
end
end
end

View file

@ -1,30 +0,0 @@
# frozen_string_literal: true
class Mastodon::RackMiddleware
def initialize(app)
@app = app
end
def call(env)
@app.call(env)
ensure
clean_up_sockets!
end
private
def clean_up_sockets!
clean_up_redis_socket!
clean_up_statsd_socket!
end
def clean_up_redis_socket!
RedisConnection.pool.checkin if Thread.current[:redis]
Thread.current[:redis] = nil
end
def clean_up_statsd_socket!
Thread.current[:statsd_socket]&.close
Thread.current[:statsd_socket] = nil
end
end

View file

@ -9,22 +9,20 @@ class Mastodon::RedisConfiguration
def base
@base ||= setup_config(prefix: nil, defaults: DEFAULTS)
.merge(namespace: base_namespace)
end
def sidekiq
@sidekiq ||= setup_config(prefix: 'SIDEKIQ_')
.merge(namespace: sidekiq_namespace)
end
def cache
@cache ||= setup_config(prefix: 'CACHE_')
.merge({
namespace: cache_namespace,
namespace: 'cache',
expires_in: 10.minutes,
connect_timeout: 5,
pool: {
size: Sidekiq.server? ? Sidekiq[:concurrency] : Integer(ENV['MAX_THREADS'] || 5),
size: Sidekiq.server? ? Sidekiq.default_configuration[:concurrency] : Integer(ENV['MAX_THREADS'] || 5),
timeout: 5,
},
})
@ -36,24 +34,6 @@ class Mastodon::RedisConfiguration
ENV['REDIS_DRIVER'] == 'ruby' ? :ruby : :hiredis
end
def namespace
@namespace ||= ENV.fetch('REDIS_NAMESPACE', nil)
end
def base_namespace
return "mastodon_test#{ENV.fetch('TEST_ENV_NUMBER', nil)}" if Rails.env.test?
namespace
end
def sidekiq_namespace
namespace
end
def cache_namespace
namespace ? "#{namespace}_cache" : 'cache'
end
def setup_config(prefix: nil, defaults: {})
prefix = "#{prefix}REDIS_"
@ -62,7 +42,7 @@ class Mastodon::RedisConfiguration
password = ENV.fetch("#{prefix}PASSWORD", nil)
host = ENV.fetch("#{prefix}HOST", defaults[:host])
port = ENV.fetch("#{prefix}PORT", defaults[:port])
db = ENV.fetch("#{prefix}DB", defaults[:db])
db = Rails.env.test? ? ENV.fetch('TEST_ENV_NUMBER', defaults[:db]).to_i + 1 : ENV.fetch("#{prefix}DB", defaults[:db])
return { url:, driver: } if url

View file

@ -3,8 +3,10 @@
class Mastodon::SidekiqMiddleware
BACKTRACE_LIMIT = 3
def call(*, &block)
Chewy.strategy(:mastodon, &block)
def call(_worker_class, job, _queue, &block)
setup_query_log_tags(job) do
Chewy.strategy(:mastodon, &block)
end
rescue Mastodon::HostValidationError
# Do not retry
rescue => e
@ -61,4 +63,14 @@ class Mastodon::SidekiqMiddleware
Thread.current[:statsd_socket]&.close
Thread.current[:statsd_socket] = nil
end
def setup_query_log_tags(job, &block)
if Rails.configuration.active_record.query_log_tags_enabled
# If `wrapped` is set, this is an `ActiveJob` which is already in the execution context
sidekiq_job_class = job['wrapped'].present? ? nil : job['class'].to_s
ActiveSupport::ExecutionContext.set(sidekiq_job_class: sidekiq_job_class, &block)
else
yield
end
end
end

View file

@ -9,11 +9,11 @@ module Mastodon
end
def minor
3
4
end
def patch
8
4
end
def default_prerelease
@ -21,7 +21,7 @@ module Mastodon
end
def prerelease
ENV['MASTODON_VERSION_PRERELEASE'].presence || default_prerelease
version_configuration[:prerelease].presence || default_prerelease
end
def build_metadata
@ -45,7 +45,7 @@ module Mastodon
def api_versions
{
mastodon: 2,
mastodon: 6,
}
end
@ -59,7 +59,7 @@ module Mastodon
# specify git tag or commit hash here
def source_tag
ENV.fetch('SOURCE_TAG', nil)
source_configuration[:tag]
end
def source_url
@ -70,8 +70,24 @@ module Mastodon
end
end
def source_commit
ENV.fetch('SOURCE_COMMIT', nil)
end
def user_agent
@user_agent ||= "Mastodon/#{Version} (#{HTTP::Request::USER_AGENT}; +http#{Rails.configuration.x.use_https ? 's' : ''}://#{Rails.configuration.x.web_domain}/)"
@user_agent ||= "Mastodon/#{Version} (#{HTTP::Request::USER_AGENT}; +http#{'s' if Rails.configuration.x.use_https}://#{Rails.configuration.x.web_domain}/)"
end
def version_configuration
mastodon_configuration.version
end
def source_configuration
mastodon_configuration.source
end
def mastodon_configuration
Rails.configuration.x.mastodon
end
end
end