diff options
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/account.rb | 22 | ||||
-rw-r--r-- | app/models/account_filter.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/domain_materializable.rb | 13 | ||||
-rw-r--r-- | app/models/domain_allow.rb | 1 | ||||
-rw-r--r-- | app/models/domain_block.rb | 21 | ||||
-rw-r--r-- | app/models/favourite.rb | 2 | ||||
-rw-r--r-- | app/models/follow.rb | 2 | ||||
-rw-r--r-- | app/models/follow_request.rb | 2 | ||||
-rw-r--r-- | app/models/form/admin_settings.rb | 2 | ||||
-rw-r--r-- | app/models/import.rb | 1 | ||||
-rw-r--r-- | app/models/instance.rb | 63 | ||||
-rw-r--r-- | app/models/instance_filter.rb | 31 | ||||
-rw-r--r-- | app/models/list.rb | 13 | ||||
-rw-r--r-- | app/models/poll.rb | 2 | ||||
-rw-r--r-- | app/models/report.rb | 1 | ||||
-rw-r--r-- | app/models/status.rb | 12 | ||||
-rw-r--r-- | app/models/unavailable_domain.rb | 2 | ||||
-rw-r--r-- | app/models/user.rb | 13 |
18 files changed, 134 insertions, 71 deletions
diff --git a/app/models/account.rb b/app/models/account.rb index b70978d2b..f6aba74c6 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -67,6 +67,7 @@ class Account < ApplicationRecord include Paginable include AccountCounters include DomainNormalizable + include DomainMaterializable include AccountMerging MAX_DISPLAY_NAME_LENGTH = (ENV['MAX_DISPLAY_NAME_CHARS'] || 30).to_i @@ -103,11 +104,11 @@ class Account < ApplicationRecord scope :sensitized, -> { where.not(sensitized_at: nil) } scope :without_suspended, -> { where(suspended_at: nil) } scope :without_silenced, -> { where(silenced_at: nil) } + scope :without_instance_actor, -> { where.not(id: -99) } scope :recent, -> { reorder(id: :desc) } scope :bots, -> { where(actor_type: %w(Application Service)) } scope :groups, -> { where(actor_type: 'Group') } scope :alphabetic, -> { order(domain: :asc, username: :asc) } - scope :by_domain_accounts, -> { group(:domain).select(:domain, 'COUNT(*) AS accounts_count').order('accounts_count desc') } scope :matches_username, ->(value) { where(arel_table[:username].matches("#{value}%")) } scope :matches_display_name, ->(value) { where(arel_table[:display_name].matches("#{value}%")) } scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) } @@ -226,7 +227,7 @@ class Account < ApplicationRecord end def suspended? - suspended_at.present? + suspended_at.present? && !instance_actor? end def suspended_permanently? @@ -440,10 +441,6 @@ class Account < ApplicationRecord super - %w(statuses_count following_count followers_count) end - def domains - reorder(nil).pluck(Arel.sql('distinct accounts.domain')) - end - def inboxes urls = reorder(nil).where(protocol: :activitypub).group(:preferred_inbox_url).pluck(Arel.sql("coalesce(nullif(accounts.shared_inbox_url, ''), accounts.inbox_url) AS preferred_inbox_url")) DeliveryFailureTracker.without_unavailable(urls) @@ -583,17 +580,6 @@ class Account < ApplicationRecord end def clean_feed_manager - reblog_key = FeedManager.instance.key(:home, id, 'reblogs') - reblogged_id_set = Redis.current.zrange(reblog_key, 0, -1) - - Redis.current.pipelined do - Redis.current.del(FeedManager.instance.key(:home, id)) - Redis.current.del(reblog_key) - - reblogged_id_set.each do |reblogged_id| - reblog_set_key = FeedManager.instance.key(:home, id, "reblogs:#{reblogged_id}") - Redis.current.del(reblog_set_key) - end - end + FeedManager.instance.clean_feeds!(:home, [id]) end end diff --git a/app/models/account_filter.rb b/app/models/account_filter.rb index 7b6012e0f..2b001385f 100644 --- a/app/models/account_filter.rb +++ b/app/models/account_filter.rb @@ -45,7 +45,7 @@ class AccountFilter def scope_for(key, value) case key.to_s when 'local' - Account.local + Account.local.without_instance_actor when 'remote' Account.remote when 'by_domain' diff --git a/app/models/concerns/domain_materializable.rb b/app/models/concerns/domain_materializable.rb new file mode 100644 index 000000000..88337f8c0 --- /dev/null +++ b/app/models/concerns/domain_materializable.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module DomainMaterializable + extend ActiveSupport::Concern + + included do + after_create_commit :refresh_instances_view + end + + def refresh_instances_view + Instance.refresh unless domain.nil? || Instance.where(domain: domain).exists? + end +end diff --git a/app/models/domain_allow.rb b/app/models/domain_allow.rb index 5fe0e3a29..4b0a89c18 100644 --- a/app/models/domain_allow.rb +++ b/app/models/domain_allow.rb @@ -12,6 +12,7 @@ class DomainAllow < ApplicationRecord include DomainNormalizable + include DomainMaterializable validates :domain, presence: true, uniqueness: true, domain: true diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb index 2b18e01fa..bba04c603 100644 --- a/app/models/domain_block.rb +++ b/app/models/domain_block.rb @@ -12,10 +12,12 @@ # reject_reports :boolean default(FALSE), not null # private_comment :text # public_comment :text +# obfuscate :boolean default(FALSE), not null # class DomainBlock < ApplicationRecord include DomainNormalizable + include DomainMaterializable enum severity: [:silence, :suspend, :noop] @@ -72,4 +74,23 @@ class DomainBlock < ApplicationRecord scope = suspend? ? accounts.where(suspended_at: created_at) : accounts.where(silenced_at: created_at) scope.count end + + def public_domain + return domain unless obfuscate? + + length = domain.size + visible_ratio = length / 4 + + domain.chars.map.with_index do |chr, i| + if i > visible_ratio && i < length - visible_ratio && chr != '.' + '*' + else + chr + end + end.join + end + + def domain_digest + Digest::SHA256.hexdigest(domain) + end end diff --git a/app/models/favourite.rb b/app/models/favourite.rb index bf0ec4449..35028b7dd 100644 --- a/app/models/favourite.rb +++ b/app/models/favourite.rb @@ -36,7 +36,7 @@ class Favourite < ApplicationRecord end def decrement_cache_counters - return if association(:status).loaded? && (status.marked_for_destruction? || status.marked_for_mass_destruction?) + return if association(:status).loaded? && status.marked_for_destruction? status&.decrement_count!(:favourites_count) end end diff --git a/app/models/follow.rb b/app/models/follow.rb index 55a9da792..69a1722b3 100644 --- a/app/models/follow.rb +++ b/app/models/follow.rb @@ -26,7 +26,7 @@ class Follow < ApplicationRecord has_one :notification, as: :activity, dependent: :destroy validates :account_id, uniqueness: { scope: :target_account_id } - validates_with FollowLimitValidator, on: :create + validates_with FollowLimitValidator, on: :create, if: :rate_limit? scope :recent, -> { reorder(id: :desc) } diff --git a/app/models/follow_request.rb b/app/models/follow_request.rb index c1f19149b..2d2a77b59 100644 --- a/app/models/follow_request.rb +++ b/app/models/follow_request.rb @@ -26,7 +26,7 @@ class FollowRequest < ApplicationRecord has_one :notification, as: :activity, dependent: :destroy validates :account_id, uniqueness: { scope: :target_account_id } - validates_with FollowLimitValidator, on: :create + validates_with FollowLimitValidator, on: :create, if: :rate_limit? def authorize! account.follow!(target_account, reblogs: show_reblogs, notify: notify, uri: uri) diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index fcec3e686..999d835e6 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -42,6 +42,7 @@ class Form::AdminSettings show_domain_blocks_rationale noindex outgoing_spoilers + require_invite_text ).freeze BOOLEAN_KEYS = %i( @@ -62,6 +63,7 @@ class Form::AdminSettings trends trendable_by_default noindex + require_invite_text ).freeze UPLOAD_KEYS = %i( diff --git a/app/models/import.rb b/app/models/import.rb index 702453289..00a54892e 100644 --- a/app/models/import.rb +++ b/app/models/import.rb @@ -27,6 +27,7 @@ class Import < ApplicationRecord enum type: [:following, :blocking, :muting, :domain_blocking, :bookmarks] validates :type, presence: true + validates_with ImportValidator, on: :create has_attached_file :data validates_attachment_content_type :data, content_type: FILE_TYPES diff --git a/app/models/instance.rb b/app/models/instance.rb index 3c740f8a2..29be03662 100644 --- a/app/models/instance.rb +++ b/app/models/instance.rb @@ -1,26 +1,63 @@ # frozen_string_literal: true +# == Schema Information +# +# Table name: instances +# +# domain :string primary key +# accounts_count :bigint(8) +# -class Instance - include ActiveModel::Model +class Instance < ApplicationRecord + self.primary_key = :domain - attr_accessor :domain, :accounts_count, :domain_block + has_many :accounts, foreign_key: :domain, primary_key: :domain - def initialize(resource) - @domain = resource.domain - @accounts_count = resource.respond_to?(:accounts_count) ? resource.accounts_count : nil - @domain_block = resource.is_a?(DomainBlock) ? resource : DomainBlock.rule_for(domain) - @domain_allow = resource.is_a?(DomainAllow) ? resource : DomainAllow.rule_for(domain) + belongs_to :domain_block, foreign_key: :domain, primary_key: :domain + belongs_to :domain_allow, foreign_key: :domain, primary_key: :domain + + scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) } + + def self.refresh + Scenic.database.refresh_materialized_view(table_name, concurrently: true, cascade: false) end - def countable? - @accounts_count.present? + def readonly? + true end - def to_param - domain + def delivery_failure_tracker + @delivery_failure_tracker ||= DeliveryFailureTracker.new(domain) + end + + def following_count + @following_count ||= Follow.where(account: accounts).count + end + + def followers_count + @followers_count ||= Follow.where(target_account: accounts).count + end + + def reports_count + @reports_count ||= Report.where(target_account: accounts).count end - def cache_key + def blocks_count + @blocks_count ||= Block.where(target_account: accounts).count + end + + def public_comment + domain_block&.public_comment + end + + def private_comment + domain_block&.private_comment + end + + def media_storage + @media_storage ||= MediaAttachment.where(account: accounts).sum(:file_file_size) + end + + def to_param domain end end diff --git a/app/models/instance_filter.rb b/app/models/instance_filter.rb index 9c467bc27..0598d8fea 100644 --- a/app/models/instance_filter.rb +++ b/app/models/instance_filter.rb @@ -13,18 +13,27 @@ class InstanceFilter end def results - if params[:limited].present? - scope = DomainBlock - scope = scope.matches_domain(params[:by_domain]) if params[:by_domain].present? - scope.order(id: :desc) - elsif params[:allowed].present? - scope = DomainAllow - scope = scope.matches_domain(params[:by_domain]) if params[:by_domain].present? - scope.order(id: :desc) + scope = Instance.includes(:domain_block, :domain_allow).order(accounts_count: :desc) + + params.each do |key, value| + scope.merge!(scope_for(key, value.to_s.strip)) if value.present? + end + + scope + end + + private + + def scope_for(key, value) + case key.to_s + when 'limited' + Instance.joins(:domain_block).reorder(Arel.sql('domain_blocks.id desc')) + when 'allowed' + Instance.joins(:domain_allow).reorder(Arel.sql('domain_allows.id desc')) + when 'by_domain' + Instance.matches_domain(value) else - scope = Account.remote - scope = scope.matches_domain(params[:by_domain]) if params[:by_domain].present? - scope.by_domain_accounts + raise "Unknown filter: #{key}" end end end diff --git a/app/models/list.rb b/app/models/list.rb index 655d55ff6..cdc6ebdb3 100644 --- a/app/models/list.rb +++ b/app/models/list.rb @@ -34,17 +34,6 @@ class List < ApplicationRecord private def clean_feed_manager - reblog_key = FeedManager.instance.key(:list, id, 'reblogs') - reblogged_id_set = Redis.current.zrange(reblog_key, 0, -1) - - Redis.current.pipelined do - Redis.current.del(FeedManager.instance.key(:list, id)) - Redis.current.del(reblog_key) - - reblogged_id_set.each do |reblogged_id| - reblog_set_key = FeedManager.instance.key(:list, id, "reblogs:#{reblogged_id}") - Redis.current.del(reblog_set_key) - end - end + FeedManager.instance.clean_feeds!(:list, [id]) end end diff --git a/app/models/poll.rb b/app/models/poll.rb index b5deafcc2..e1ca55252 100644 --- a/app/models/poll.rb +++ b/app/models/poll.rb @@ -25,7 +25,7 @@ class Poll < ApplicationRecord belongs_to :account belongs_to :status - has_many :votes, class_name: 'PollVote', inverse_of: :poll, dependent: :destroy + has_many :votes, class_name: 'PollVote', inverse_of: :poll, dependent: :delete_all has_many :notifications, as: :activity, dependent: :destroy diff --git a/app/models/report.rb b/app/models/report.rb index f31bcfd2e..cd08120e4 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -14,6 +14,7 @@ # target_account_id :bigint(8) not null # assigned_account_id :bigint(8) # uri :string +# forwarded :boolean # class Report < ApplicationRecord diff --git a/app/models/status.rb b/app/models/status.rb index d1ac2e4f2..0d15304b6 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -234,14 +234,6 @@ class Status < ApplicationRecord @emojis = CustomEmoji.from_text(fields.join(' '), account.domain) end - def mark_for_mass_destruction! - @marked_for_mass_destruction = true - end - - def marked_for_mass_destruction? - @marked_for_mass_destruction - end - def replies_count status_stat&.replies_count || 0 end @@ -498,7 +490,7 @@ class Status < ApplicationRecord end def decrement_counter_caches - return if direct_visibility? || marked_for_mass_destruction? + return if direct_visibility? account&.decrement_count!(:statuses_count) reblog&.decrement_count!(:reblogs_count) if reblog? @@ -508,7 +500,7 @@ class Status < ApplicationRecord def unlink_from_conversations return unless direct_visibility? - mentioned_accounts = mentions.includes(:account).map(&:account) + mentioned_accounts = (association(:mentions).loaded? ? mentions : mentions.includes(:account)).map(&:account) inbox_owners = mentioned_accounts.select(&:local?) + (account.local? ? [account] : []) inbox_owners.each do |inbox_owner| diff --git a/app/models/unavailable_domain.rb b/app/models/unavailable_domain.rb index e2918b586..5e8870bde 100644 --- a/app/models/unavailable_domain.rb +++ b/app/models/unavailable_domain.rb @@ -12,6 +12,8 @@ class UnavailableDomain < ApplicationRecord include DomainNormalizable + validates :domain, presence: true, uniqueness: true + after_commit :reset_cache! private diff --git a/app/models/user.rb b/app/models/user.rb index 984f04b4e..9316eb228 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -82,7 +82,8 @@ class User < ApplicationRecord has_many :webauthn_credentials, dependent: :destroy has_one :invite_request, class_name: 'UserInviteRequest', inverse_of: :user, dependent: :destroy - accepts_nested_attributes_for :invite_request, reject_if: ->(attributes) { attributes['text'].blank? } + accepts_nested_attributes_for :invite_request, reject_if: ->(attributes) { attributes['text'].blank? && !Setting.require_invite_text } + validates :invite_request, presence: true, on: :create, if: :invite_text_required? validates :locale, inclusion: I18n.available_locales.map(&:to_s), if: :locale? validates_with BlacklistedEmailValidator, on: :create @@ -127,7 +128,7 @@ class User < ApplicationRecord to: :settings, prefix: :setting, allow_nil: false attr_reader :invite_code, :sign_in_token_attempt - attr_writer :external + attr_writer :external, :bypass_invite_request_check def confirmed? confirmed_at.present? @@ -428,6 +429,10 @@ class User < ApplicationRecord !!@external end + def bypass_invite_request_check? + @bypass_invite_request_check + end + def sanitize_languages return if chosen_languages.nil? chosen_languages.reject!(&:blank?) @@ -465,4 +470,8 @@ class User < ApplicationRecord def validate_email_dns? email_changed? && !(Rails.env.test? || Rails.env.development?) end + + def invite_text_required? + Setting.require_invite_text && !invited? && !external? && !bypass_invite_request_check? + end end |