Merge tag 'v4.0.2'

This commit is contained in:
Mike Barnes 2022-12-17 22:55:12 +11:00
commit b0fa7842db
1563 changed files with 59605 additions and 38210 deletions

View file

@ -54,10 +54,10 @@ module Mastodon
option :email, required: true
option :confirmed, type: :boolean
option :role, default: 'user', enum: %w(user moderator admin)
option :role
option :reattach, type: :boolean
option :force, type: :boolean
desc 'create USERNAME', 'Create a new user'
desc 'create USERNAME', 'Create a new user account'
long_desc <<-LONG_DESC
Create a new user account with a given USERNAME and an
e-mail address provided with --email.
@ -65,8 +65,7 @@ module Mastodon
With the --confirmed option, the confirmation e-mail will
be skipped and the account will be active straight away.
With the --role option one of "user", "admin" or "moderator"
can be supplied. Defaults to "user"
With the --role option, the role can be supplied.
With the --reattach option, the new user will be reattached
to a given existing username of an old account. If the old
@ -75,9 +74,22 @@ module Mastodon
username to the new account anyway.
LONG_DESC
def create(username)
role_id = nil
if options[:role]
role = UserRole.find_by(name: options[:role])
if role.nil?
say('Cannot find user role with that name', :red)
exit(1)
end
role_id = role.id
end
account = Account.new(username: username)
password = SecureRandom.hex
user = User.new(email: options[:email], password: password, agreement: true, approved: true, admin: options[:role] == 'admin', moderator: options[:role] == 'moderator', confirmed_at: options[:confirmed] ? Time.now.utc : nil, bypass_invite_request_check: true)
user = User.new(email: options[:email], password: password, agreement: true, approved: true, role_id: role_id, confirmed_at: options[:confirmed] ? Time.now.utc : nil, bypass_invite_request_check: true)
if options[:reattach]
account = Account.find_local(username) || Account.new(username: username)
@ -106,14 +118,15 @@ module Mastodon
user.errors.to_h.each do |key, error|
say('Failure/Error: ', :red)
say(key)
say(' ' + error, :red)
say(" #{error}", :red)
end
exit(1)
end
end
option :role, enum: %w(user moderator admin)
option :role
option :remove_role, type: :boolean
option :email
option :confirm, type: :boolean
option :enable, type: :boolean
@ -121,12 +134,12 @@ module Mastodon
option :disable_2fa, type: :boolean
option :approve, type: :boolean
option :reset_password, type: :boolean
desc 'modify USERNAME', 'Modify a user'
desc 'modify USERNAME', 'Modify a user account'
long_desc <<-LONG_DESC
Modify a user account.
With the --role option, update the user's role to one of "user",
"moderator" or "admin".
With the --role option, update the user's role. To remove the user's
role, i.e. demote to normal user, use --remove-role.
With the --email option, update the user's e-mail address. With
the --confirm option, mark the user's e-mail as confirmed.
@ -152,8 +165,16 @@ module Mastodon
end
if options[:role]
user.admin = options[:role] == 'admin'
user.moderator = options[:role] == 'moderator'
role = UserRole.find_by(name: options[:role])
if role.nil?
say('Cannot find user role with that name', :red)
exit(1)
end
user.role_id = role.id
elsif options[:remove_role]
user.role_id = nil
end
password = SecureRandom.hex if options[:reset_password]
@ -172,7 +193,7 @@ module Mastodon
user.errors.to_h.each do |key, error|
say('Failure/Error: ', :red)
say(key)
say(' ' + error, :red)
say(" #{error}", :red)
end
exit(1)
@ -319,7 +340,7 @@ module Mastodon
unless skip_domains.empty?
say('The following domains were not available during the check:', :yellow)
skip_domains.each { |domain| say(' ' + domain) }
skip_domains.each { |domain| say(" #{domain}") }
end
end

View file

@ -18,17 +18,15 @@ module Mastodon
When suspending a local user, a hash of a "canonical" version of their e-mail
address is stored to prevent them from signing up again.
This command can be used to find whether a known email address is blocked,
and if so, which account it was attached to.
This command can be used to find whether a known email address is blocked.
LONG_DESC
def find(email)
accts = CanonicalEmailBlock.find_blocks(email).map(&:reference_account).map(&:acct).to_a
accts = CanonicalEmailBlock.matching_email(email)
if accts.empty?
say("#{email} is not blocked", :yellow)
say("#{email} is not blocked", :green)
else
accts.each do |acct|
say(acct, :white)
end
say("#{email} is blocked", :red)
end
end
@ -40,24 +38,13 @@ module Mastodon
This command allows removing a canonical email block.
LONG_DESC
def remove(email)
blocks = CanonicalEmailBlock.find_blocks(email)
blocks = CanonicalEmailBlock.matching_email(email)
if blocks.empty?
say("#{email} is not blocked", :yellow)
say("#{email} is not blocked", :green)
else
blocks.destroy_all
say("Removed canonical email block for #{email}", :green)
end
end
private
def color(processed, failed)
if !processed.zero? && failed.zero?
:green
elsif failed.zero?
:yellow
else
:red
say("Unblocked #{email}", :green)
end
end
end

View file

@ -14,7 +14,7 @@ module Mastodon
end
MIN_SUPPORTED_VERSION = 2019_10_01_213028 # rubocop:disable Style/NumericLiterals
MAX_SUPPORTED_VERSION = 2022_03_16_233212 # rubocop:disable Style/NumericLiterals
MAX_SUPPORTED_VERSION = 2022_11_04_133904 # rubocop:disable Style/NumericLiterals
# Stubs to enjoy ActiveRecord queries while not depending on a particular
# version of the code/database
@ -45,6 +45,7 @@ module Mastodon
class FollowRecommendationSuppression < ApplicationRecord; end
class CanonicalEmailBlock < ApplicationRecord; end
class Appeal < ApplicationRecord; end
class Webhook < ApplicationRecord; end
class PreviewCard < ApplicationRecord
self.inheritance_column = false
@ -182,6 +183,7 @@ module Mastodon
deduplicate_accounts!
deduplicate_tags!
deduplicate_webauthn_credentials!
deduplicate_webhooks!
Scenic.database.refresh_materialized_view('instances', concurrently: true, cascade: false) if ActiveRecord::Migrator.current_version >= 2020_12_06_004238
Rails.cache.clear
@ -497,6 +499,7 @@ module Mastodon
def deduplicate_tags!
remove_index_if_exists!(:tags, 'index_tags_on_name_lower')
remove_index_if_exists!(:tags, 'index_tags_on_name_lower_btree')
@prompt.say 'Deduplicating tags…'
ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM tags GROUP BY lower((name)::text) HAVING count(*) > 1").each do |row|
@ -509,11 +512,10 @@ module Mastodon
end
@prompt.say 'Restoring tags indexes…'
ActiveRecord::Base.connection.add_index :tags, 'lower((name)::text)', name: 'index_tags_on_name_lower', unique: true
if ActiveRecord::Base.connection.indexes(:tags).any? { |i| i.name == 'index_tags_on_name_lower_btree' }
@prompt.say 'Reindexing textual indexes on tags…'
ActiveRecord::Base.connection.execute('REINDEX INDEX index_tags_on_name_lower_btree;')
if ActiveRecord::Migrator.current_version < 20210421121431
ActiveRecord::Base.connection.add_index :tags, 'lower((name)::text)', name: 'index_tags_on_name_lower', unique: true
else
ActiveRecord::Base.connection.execute 'CREATE UNIQUE INDEX CONCURRENTLY index_tags_on_name_lower_btree ON tags (lower(name) text_pattern_ops)'
end
end
@ -531,6 +533,20 @@ module Mastodon
ActiveRecord::Base.connection.add_index :webauthn_credentials, ['external_id'], name: 'index_webauthn_credentials_on_external_id', unique: true
end
def deduplicate_webhooks!
return unless ActiveRecord::Base.connection.table_exists?(:webhooks)
remove_index_if_exists!(:webhooks, 'index_webhooks_on_url')
@prompt.say 'Deduplicating webhooks…'
ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM webhooks GROUP BY url HAVING count(*) > 1").each do |row|
Webhooks.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy)
end
@prompt.say 'Restoring webhooks indexes…'
ActiveRecord::Base.connection.add_index :webhooks, ['url'], name: 'index_webhooks_on_url', unique: true
end
def deduplicate_local_accounts!(accounts)
accounts = accounts.sort_by(&:id).reverse

View file

@ -187,6 +187,7 @@ module Mastodon
option :account, type: :string
option :domain, type: :string
option :status, type: :numeric
option :days, type: :numeric
option :concurrency, type: :numeric, default: 5, aliases: [:c]
option :verbose, type: :boolean, default: false, aliases: [:v]
option :dry_run, type: :boolean, default: false
@ -204,6 +205,8 @@ module Mastodon
Use the --domain option to download attachments from a specific domain.
Use the --days option to limit attachments created within days.
By default, attachments that are believed to be already downloaded will
not be re-downloaded. To force re-download of every URL, use --force.
DESC
@ -224,10 +227,16 @@ module Mastodon
scope = MediaAttachment.where(account_id: account.id)
elsif options[:domain]
scope = MediaAttachment.joins(:account).merge(Account.by_domain_and_subdomains(options[:domain]))
elsif options[:days].present?
scope = MediaAttachment.remote
else
exit(1)
end
if options[:days].present?
scope = scope.where('media_attachments.id > ?', Mastodon::Snowflake.id_at(options[:days].days.ago, with_random: false))
end
processed, aggregate = parallelize_with_progress(scope) do |media_attachment|
next if media_attachment.remote_url.blank? || (!options[:force] && media_attachment.file_file_name.present?)
next if DomainBlock.reject_media?(media_attachment.account.domain)

View file

@ -30,7 +30,7 @@ module Mastodon
changed since the last run. Index upgrades erase index data.
Even if creating or upgrading indices is not necessary, data from the
database will be imported into the indices, unless overriden with --no-import.
database will be imported into the indices, unless overridden with --no-import.
LONG_DESC
def deploy
if options[:concurrency] < 1

View file

@ -5,15 +5,15 @@ module Mastodon
module_function
def major
3
4
end
def minor
5
0
end
def patch
5
2
end
def flags