Merge remote-tracking branch 'parent/main' into upstream-20241113
This commit is contained in:
commit
910eafda63
177 changed files with 1625 additions and 659 deletions
|
@ -94,6 +94,8 @@ class Account < ApplicationRecord
|
|||
include Account::Interactions
|
||||
include Account::Merging
|
||||
include Account::Search
|
||||
include Account::Sensitizes
|
||||
include Account::Silences
|
||||
include Account::StatusesSearch
|
||||
include Account::OtherSettings
|
||||
include Account::MasterSettings
|
||||
|
@ -136,9 +138,6 @@ class Account < ApplicationRecord
|
|||
scope :remote, -> { where.not(domain: nil) }
|
||||
scope :local, -> { where(domain: nil) }
|
||||
scope :partitioned, -> { order(Arel.sql('row_number() over (partition by domain)')) }
|
||||
scope :silenced, -> { where.not(silenced_at: nil) }
|
||||
scope :sensitized, -> { where.not(sensitized_at: nil) }
|
||||
scope :without_silenced, -> { where(silenced_at: nil) }
|
||||
scope :without_instance_actor, -> { where.not(id: INSTANCE_ACTOR_ID) }
|
||||
scope :recent, -> { reorder(id: :desc) }
|
||||
scope :bots, -> { where(actor_type: AUTOMATED_ACTOR_TYPES) }
|
||||
|
@ -271,30 +270,6 @@ class Account < ApplicationRecord
|
|||
ResolveAccountService.new.call(acct) unless local?
|
||||
end
|
||||
|
||||
def silenced?
|
||||
silenced_at.present?
|
||||
end
|
||||
|
||||
def silence!(date = Time.now.utc)
|
||||
update!(silenced_at: date)
|
||||
end
|
||||
|
||||
def unsilence!
|
||||
update!(silenced_at: nil)
|
||||
end
|
||||
|
||||
def sensitized?
|
||||
sensitized_at.present?
|
||||
end
|
||||
|
||||
def sensitize!(date = Time.now.utc)
|
||||
update!(sensitized_at: date)
|
||||
end
|
||||
|
||||
def unsensitize!
|
||||
update!(sensitized_at: nil)
|
||||
end
|
||||
|
||||
def memorialize!
|
||||
update!(memorial: true)
|
||||
end
|
||||
|
|
|
@ -136,7 +136,7 @@ class AccountStatusesCleanupPolicy < ApplicationRecord
|
|||
end
|
||||
|
||||
def without_direct_scope
|
||||
Status.where.not(visibility: :direct)
|
||||
Status.not_direct_visibility
|
||||
end
|
||||
|
||||
def old_enough_scope(max_id = nil)
|
||||
|
|
|
@ -33,6 +33,7 @@ class Admin::ActionLogFilter
|
|||
create_domain_block: { target_type: 'DomainBlock', action: 'create' }.freeze,
|
||||
create_email_domain_block: { target_type: 'EmailDomainBlock', action: 'create' }.freeze,
|
||||
create_ip_block: { target_type: 'IpBlock', action: 'create' }.freeze,
|
||||
create_relay: { target_type: 'Relay', action: 'create' }.freeze,
|
||||
create_unavailable_domain: { target_type: 'UnavailableDomain', action: 'create' }.freeze,
|
||||
create_user_role: { target_type: 'UserRole', action: 'create' }.freeze,
|
||||
create_canonical_email_block: { target_type: 'CanonicalEmailBlock', action: 'create' }.freeze,
|
||||
|
@ -42,6 +43,7 @@ class Admin::ActionLogFilter
|
|||
destroy_domain_allow: { target_type: 'DomainAllow', action: 'destroy' }.freeze,
|
||||
destroy_domain_block: { target_type: 'DomainBlock', action: 'destroy' }.freeze,
|
||||
destroy_ip_block: { target_type: 'IpBlock', action: 'destroy' }.freeze,
|
||||
destroy_relay: { target_type: 'Relay', action: 'destroy' }.freeze,
|
||||
destroy_email_domain_block: { target_type: 'EmailDomainBlock', action: 'destroy' }.freeze,
|
||||
destroy_instance: { target_type: 'Instance', action: 'destroy' }.freeze,
|
||||
destroy_unavailable_domain: { target_type: 'UnavailableDomain', action: 'destroy' }.freeze,
|
||||
|
@ -51,8 +53,10 @@ class Admin::ActionLogFilter
|
|||
disable_2fa_user: { target_type: 'User', action: 'disable_2fa' }.freeze,
|
||||
disable_custom_emoji: { target_type: 'CustomEmoji', action: 'disable' }.freeze,
|
||||
disable_user: { target_type: 'User', action: 'disable' }.freeze,
|
||||
disable_relay: { target_type: 'Relay', action: 'disable' }.freeze,
|
||||
enable_custom_emoji: { target_type: 'CustomEmoji', action: 'enable' }.freeze,
|
||||
enable_user: { target_type: 'User', action: 'enable' }.freeze,
|
||||
enable_relay: { target_type: 'Relay', action: 'enable' }.freeze,
|
||||
memorialize_account: { target_type: 'Account', action: 'memorialize' }.freeze,
|
||||
promote_user: { target_type: 'User', action: 'promote' }.freeze,
|
||||
remove_avatar_user: { target_type: 'User', action: 'remove_avatar' }.freeze,
|
||||
|
|
|
@ -32,7 +32,7 @@ class Admin::StatusFilter
|
|||
def scope_for(key, _value)
|
||||
case key.to_s
|
||||
when 'media'
|
||||
Status.joins(:media_attachments).merge(@account.media_attachments).group(:id).reorder('statuses.id desc')
|
||||
Status.joins(:media_attachments).merge(@account.media_attachments).group(:id).recent
|
||||
else
|
||||
raise Mastodon::InvalidParameterError, "Unknown filter: #{key}"
|
||||
end
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
class BulkImport < ApplicationRecord
|
||||
self.inheritance_column = false
|
||||
|
||||
ARCHIVE_PERIOD = 1.week
|
||||
CONFIRM_PERIOD = 10.minutes
|
||||
|
||||
belongs_to :account
|
||||
has_many :rows, class_name: 'BulkImportRow', inverse_of: :bulk_import, dependent: :delete_all
|
||||
|
||||
|
@ -42,6 +45,9 @@ class BulkImport < ApplicationRecord
|
|||
|
||||
validates :type, presence: true
|
||||
|
||||
scope :archival_completed, -> { where(created_at: ..ARCHIVE_PERIOD.ago) }
|
||||
scope :confirmation_missed, -> { state_unconfirmed.where(created_at: ..CONFIRM_PERIOD.ago) }
|
||||
|
||||
def self.progress!(bulk_import_id, imported: false)
|
||||
# Use `increment_counter` so that the incrementation is done atomically in the database
|
||||
BulkImport.increment_counter(:processed_items, bulk_import_id)
|
||||
|
|
21
app/models/concerns/account/sensitizes.rb
Normal file
21
app/models/concerns/account/sensitizes.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Account::Sensitizes
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
scope :sensitized, -> { where.not(sensitized_at: nil) }
|
||||
end
|
||||
|
||||
def sensitized?
|
||||
sensitized_at.present?
|
||||
end
|
||||
|
||||
def sensitize!(date = Time.now.utc)
|
||||
update!(sensitized_at: date)
|
||||
end
|
||||
|
||||
def unsensitize!
|
||||
update!(sensitized_at: nil)
|
||||
end
|
||||
end
|
22
app/models/concerns/account/silences.rb
Normal file
22
app/models/concerns/account/silences.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Account::Silences
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
scope :silenced, -> { where.not(silenced_at: nil) }
|
||||
scope :without_silenced, -> { where(silenced_at: nil) }
|
||||
end
|
||||
|
||||
def silenced?
|
||||
silenced_at.present?
|
||||
end
|
||||
|
||||
def silence!(date = Time.now.utc)
|
||||
update!(silenced_at: date)
|
||||
end
|
||||
|
||||
def unsilence!
|
||||
update!(silenced_at: nil)
|
||||
end
|
||||
end
|
10
app/models/concerns/inet_container.rb
Normal file
10
app/models/concerns/inet_container.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module InetContainer
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
scope :containing, ->(value) { where('ip >>= ?', value) }
|
||||
scope :contained_by, ->(value) { where('ip <<= ?', value) }
|
||||
end
|
||||
end
|
|
@ -15,7 +15,9 @@ module Status::SafeReblogInsert
|
|||
#
|
||||
# The code is kept similar to ActiveRecord::Persistence code and calls it
|
||||
# directly when we are not handling a reblog.
|
||||
def _insert_record(values, returning)
|
||||
#
|
||||
# https://github.com/rails/rails/blob/v7.2.1.1/activerecord/lib/active_record/persistence.rb#L238-L263
|
||||
def _insert_record(connection, values, returning)
|
||||
return super unless values.is_a?(Hash) && values['reblog_of_id']&.value.present?
|
||||
|
||||
primary_key = self.primary_key
|
||||
|
@ -30,14 +32,19 @@ module Status::SafeReblogInsert
|
|||
|
||||
# The following line departs from stock ActiveRecord
|
||||
# Original code was:
|
||||
# im.insert(values.transform_keys { |name| arel_table[name] })
|
||||
# im = Arel::InsertManager.new(arel_table)
|
||||
# Instead, we use a custom builder when a reblog is happening:
|
||||
im = _compile_reblog_insert(values)
|
||||
|
||||
connection.insert(im, "#{self} Create", primary_key || false, primary_key_value, returning: returning).tap do |result|
|
||||
# Since we are using SELECT instead of VALUES, a non-error `nil` return is possible.
|
||||
# For our purposes, it's equivalent to a foreign key constraint violation
|
||||
raise ActiveRecord::InvalidForeignKey, "(reblog_of_id)=(#{values['reblog_of_id'].value}) is not present in table \"statuses\"" if result.nil?
|
||||
with_connection do |_c|
|
||||
connection.insert(
|
||||
im, "#{self} Create", primary_key || false, primary_key_value,
|
||||
returning: returning
|
||||
).tap do |result|
|
||||
# Since we are using SELECT instead of VALUES, a non-error `nil` return is possible.
|
||||
# For our purposes, it's equivalent to a foreign key constraint violation
|
||||
raise ActiveRecord::InvalidForeignKey, "(reblog_of_id)=(#{values['reblog_of_id'].value}) is not present in table \"statuses\"" if result.nil?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@ class EmailDomainBlock < ApplicationRecord
|
|||
|
||||
validates :domain, presence: true, uniqueness: true, domain: true
|
||||
|
||||
scope :parents, -> { where(parent_id: nil) }
|
||||
|
||||
# Used for adding multiple blocks at once
|
||||
attr_accessor :other_domains
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ class IpBlock < ApplicationRecord
|
|||
CACHE_KEY = 'blocked_ips'
|
||||
|
||||
include Expireable
|
||||
include InetContainer
|
||||
include Paginable
|
||||
|
||||
enum :severity, {
|
||||
|
|
|
@ -25,6 +25,10 @@ class Relay < ApplicationRecord
|
|||
|
||||
alias enabled? accepted?
|
||||
|
||||
def to_log_human_identifier
|
||||
inbox_url
|
||||
end
|
||||
|
||||
def enable!
|
||||
activity_id = ActivityPub::TagManager.instance.generate_uri_for(nil)
|
||||
payload = Oj.dump(follow_activity(activity_id))
|
||||
|
|
|
@ -15,6 +15,7 @@ class ScheduledStatus < ApplicationRecord
|
|||
|
||||
TOTAL_LIMIT = 300
|
||||
DAILY_LIMIT = 25
|
||||
MINIMUM_OFFSET = 5.minutes.freeze
|
||||
|
||||
belongs_to :account, inverse_of: :scheduled_statuses
|
||||
has_many :media_attachments, inverse_of: :scheduled_status, dependent: :nullify
|
||||
|
@ -26,7 +27,7 @@ class ScheduledStatus < ApplicationRecord
|
|||
private
|
||||
|
||||
def validate_future_date
|
||||
errors.add(:scheduled_at, I18n.t('scheduled_statuses.too_soon')) if scheduled_at.present? && scheduled_at <= Time.now.utc + PostStatusService::MIN_SCHEDULE_OFFSET
|
||||
errors.add(:scheduled_at, I18n.t('scheduled_statuses.too_soon')) if scheduled_at.present? && scheduled_at <= Time.now.utc + MINIMUM_OFFSET
|
||||
end
|
||||
|
||||
def validate_total_limit
|
||||
|
|
|
@ -30,6 +30,8 @@ class SessionActivation < ApplicationRecord
|
|||
|
||||
DEFAULT_SCOPES = %w(read write follow).freeze
|
||||
|
||||
scope :latest, -> { order(id: :desc) }
|
||||
|
||||
class << self
|
||||
def active?(id)
|
||||
id && exists?(session_id: id)
|
||||
|
@ -48,7 +50,7 @@ class SessionActivation < ApplicationRecord
|
|||
end
|
||||
|
||||
def purge_old
|
||||
order('created_at desc').offset(Rails.configuration.x.max_session_activations).destroy_all
|
||||
latest.offset(Rails.configuration.x.max_session_activations).destroy_all
|
||||
end
|
||||
|
||||
def exclusive(id)
|
||||
|
|
|
@ -24,7 +24,7 @@ class SoftwareUpdate < ApplicationRecord
|
|||
|
||||
class << self
|
||||
def check_enabled?
|
||||
ENV['UPDATE_CHECK_URL'] != ''
|
||||
Rails.configuration.x.mastodon.software_update_url.present?
|
||||
end
|
||||
|
||||
def pending_to_a
|
||||
|
|
|
@ -163,6 +163,7 @@ class Status < ApplicationRecord
|
|||
scope :distributable_visibility, -> { where(visibility: %i(public public_unlisted login unlisted)) }
|
||||
scope :distributable_visibility_for_anonymous, -> { where(visibility: %i(public public_unlisted unlisted)) }
|
||||
scope :list_eligible_visibility, -> { where(visibility: %i(public public_unlisted login unlisted private)) }
|
||||
scope :not_direct_visibility, -> { where.not(visibility: :direct) }
|
||||
|
||||
after_create_commit :trigger_create_webhooks
|
||||
after_update_commit :trigger_update_webhooks
|
||||
|
|
|
@ -130,7 +130,7 @@ class User < ApplicationRecord
|
|||
scope :signed_in_recently, -> { where(current_sign_in_at: ACTIVE_DURATION.ago..) }
|
||||
scope :not_signed_in_recently, -> { where(current_sign_in_at: ...ACTIVE_DURATION.ago) }
|
||||
scope :matches_email, ->(value) { where(arel_table[:email].matches("#{value}%")) }
|
||||
scope :matches_ip, ->(value) { left_joins(:ips).where('user_ips.ip <<= ?', value).group('users.id') }
|
||||
scope :matches_ip, ->(value) { left_joins(:ips).merge(IpBlock.contained_by(value)).group('users.id') }
|
||||
|
||||
before_validation :sanitize_role
|
||||
before_create :set_approved
|
||||
|
@ -363,7 +363,7 @@ class User < ApplicationRecord
|
|||
Doorkeeper::AccessGrant.by_resource_owner(self).update_all(revoked_at: Time.now.utc)
|
||||
|
||||
Doorkeeper::AccessToken.by_resource_owner(self).in_batches do |batch|
|
||||
batch.update_all(revoked_at: Time.now.utc)
|
||||
batch.touch_all(:revoked_at)
|
||||
Web::PushSubscription.where(access_token_id: batch).delete_all
|
||||
|
||||
# Revoke each access token for the Streaming API, since `update_all``
|
||||
|
@ -467,7 +467,7 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
def sign_up_from_ip_requires_approval?
|
||||
sign_up_ip.present? && IpBlock.severity_sign_up_requires_approval.exists?(['ip >>= ?', sign_up_ip.to_s])
|
||||
sign_up_ip.present? && IpBlock.severity_sign_up_requires_approval.containing(sign_up_ip.to_s).exists?
|
||||
end
|
||||
|
||||
def sign_up_email_requires_approval?
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
class UserIp < ApplicationRecord
|
||||
include DatabaseViewRecord
|
||||
include InetContainer
|
||||
|
||||
self.primary_key = :user_id
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue