mirror of
https://github.com/yingziwu/mastodon.git
synced 2026-02-22 10:13:18 +00:00
Merge tag 'v3.5.3'
This commit is contained in:
commit
86feff686d
292 changed files with 11461 additions and 3968 deletions
|
|
@ -109,7 +109,8 @@ class Account < ApplicationRecord
|
|||
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}%")) }
|
||||
scope :searchable, -> { without_suspended.where(moved_to_account_id: nil) }
|
||||
scope :without_unapproved, -> { left_outer_joins(:user).remote.or(left_outer_joins(:user).merge(User.approved.confirmed)) }
|
||||
scope :searchable, -> { without_unapproved.without_suspended.where(moved_to_account_id: nil) }
|
||||
scope :discoverable, -> { searchable.without_silenced.where(discoverable: true).left_outer_joins(:account_stat) }
|
||||
scope :followable_by, ->(account) { joins(arel_table.join(Follow.arel_table, Arel::Nodes::OuterJoin).on(arel_table[:id].eq(Follow.arel_table[:target_account_id]).and(Follow.arel_table[:account_id].eq(account.id))).join_sources).where(Follow.arel_table[:id].eq(nil)).joins(arel_table.join(FollowRequest.arel_table, Arel::Nodes::OuterJoin).on(arel_table[:id].eq(FollowRequest.arel_table[:target_account_id]).and(FollowRequest.arel_table[:account_id].eq(account.id))).join_sources).where(FollowRequest.arel_table[:id].eq(nil)) }
|
||||
scope :by_recent_status, -> { order(Arel.sql('(case when account_stats.last_status_at is null then 1 else 0 end) asc, account_stats.last_status_at desc, accounts.id desc')) }
|
||||
|
|
@ -193,7 +194,7 @@ class Account < ApplicationRecord
|
|||
end
|
||||
|
||||
def searchable?
|
||||
!(suspended? || moved?)
|
||||
!(suspended? || moved?) && (!local? || (approved? && confirmed?))
|
||||
end
|
||||
|
||||
def possibly_stale?
|
||||
|
|
@ -461,9 +462,11 @@ class Account < ApplicationRecord
|
|||
accounts.*,
|
||||
ts_rank_cd(#{TEXTSEARCH}, to_tsquery('simple', :tsquery), 32) AS rank
|
||||
FROM accounts
|
||||
LEFT JOIN users ON accounts.id = users.account_id
|
||||
WHERE to_tsquery('simple', :tsquery) @@ #{TEXTSEARCH}
|
||||
AND accounts.suspended_at IS NULL
|
||||
AND accounts.moved_to_account_id IS NULL
|
||||
AND (accounts.domain IS NOT NULL OR (users.approved = TRUE AND users.confirmed_at IS NOT NULL))
|
||||
ORDER BY rank DESC
|
||||
LIMIT :limit OFFSET :offset
|
||||
SQL
|
||||
|
|
@ -539,9 +542,11 @@ class Account < ApplicationRecord
|
|||
(count(f.id) + 1) * ts_rank_cd(#{TEXTSEARCH}, to_tsquery('simple', :tsquery), 32) AS rank
|
||||
FROM accounts
|
||||
LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = :id) OR (accounts.id = f.target_account_id AND f.account_id = :id)
|
||||
LEFT JOIN users ON accounts.id = users.account_id
|
||||
WHERE to_tsquery('simple', :tsquery) @@ #{TEXTSEARCH}
|
||||
AND accounts.suspended_at IS NULL
|
||||
AND accounts.moved_to_account_id IS NULL
|
||||
AND (accounts.domain IS NOT NULL OR (users.approved = TRUE AND users.confirmed_at IS NOT NULL))
|
||||
GROUP BY accounts.id
|
||||
ORDER BY rank DESC
|
||||
LIMIT :limit OFFSET :offset
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
class AccountMigration < ApplicationRecord
|
||||
include Redisable
|
||||
include Lockable
|
||||
|
||||
COOLDOWN_PERIOD = 30.days.freeze
|
||||
|
||||
|
|
@ -41,12 +42,8 @@ class AccountMigration < ApplicationRecord
|
|||
|
||||
return false unless errors.empty?
|
||||
|
||||
RedisLock.acquire(lock_options) do |lock|
|
||||
if lock.acquired?
|
||||
save
|
||||
else
|
||||
raise Mastodon::RaceConditionError
|
||||
end
|
||||
with_lock("account_migration:#{account.id}") do
|
||||
save
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -83,8 +80,4 @@ class AccountMigration < ApplicationRecord
|
|||
def validate_migration_cooldown
|
||||
errors.add(:base, I18n.t('migrations.errors.on_cooldown')) if account.migrations.within_cooldown.exists?
|
||||
end
|
||||
|
||||
def lock_options
|
||||
{ redis: redis, key: "account_migration:#{account.id}" }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,4 +20,16 @@ class AccountStat < ApplicationRecord
|
|||
belongs_to :account, inverse_of: :account_stat
|
||||
|
||||
update_index('accounts', :account)
|
||||
|
||||
def following_count
|
||||
[attributes['following_count'], 0].max
|
||||
end
|
||||
|
||||
def followers_count
|
||||
[attributes['followers_count'], 0].max
|
||||
end
|
||||
|
||||
def statuses_count
|
||||
[attributes['statuses_count'], 0].max
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -92,6 +92,10 @@ class Admin::AccountAction
|
|||
text: text_for_warning,
|
||||
status_ids: status_ids
|
||||
)
|
||||
|
||||
# A log entry is only interesting if the warning contains
|
||||
# custom text from someone. Otherwise it's just noise.
|
||||
log_action(:create, @warning) if @warning.text.present? && type == 'none'
|
||||
end
|
||||
|
||||
def process_reports!
|
||||
|
|
@ -160,8 +164,8 @@ class Admin::AccountAction
|
|||
|
||||
def reports
|
||||
@reports ||= begin
|
||||
if type == 'none' && with_report?
|
||||
[report]
|
||||
if type == 'none'
|
||||
with_report? ? [report] : []
|
||||
else
|
||||
Report.where(target_account: target_account).unresolved
|
||||
end
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ class Admin::StatusBatchAction
|
|||
|
||||
def handle_report!
|
||||
@report = Report.new(report_params) unless with_report?
|
||||
@report.status_ids = (@report.status_ids + status_ids.map(&:to_i)).uniq
|
||||
@report.status_ids = (@report.status_ids + allowed_status_ids).uniq
|
||||
@report.save!
|
||||
|
||||
@report_id = @report.id
|
||||
|
|
@ -135,4 +135,8 @@ class Admin::StatusBatchAction
|
|||
def report_params
|
||||
{ account: current_account, target_account: target_account }
|
||||
end
|
||||
|
||||
def allowed_status_ids
|
||||
AccountStatusesFilter.new(@report.target_account, current_account).results.with_discarded.where(id: status_ids).pluck(:id)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
19
app/models/concerns/lockable.rb
Normal file
19
app/models/concerns/lockable.rb
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Lockable
|
||||
# @param [String] lock_name
|
||||
# @param [ActiveSupport::Duration] autorelease Automatically release the lock after this time
|
||||
# @param [Boolean] raise_on_failure Raise an error if a lock cannot be acquired, or fail silently
|
||||
# @raise [Mastodon::RaceConditionError]
|
||||
def with_lock(lock_name, autorelease: 15.minutes, raise_on_failure: true)
|
||||
with_redis do |redis|
|
||||
RedisLock.acquire(redis: redis, key: "lock:#{lock_name}", autorelease: autorelease.seconds) do |lock|
|
||||
if lock.acquired?
|
||||
yield
|
||||
elsif raise_on_failure
|
||||
raise Mastodon::RaceConditionError, "Could not acquire lock for #{lock_name}, try again later"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Redisable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
private
|
||||
|
||||
def redis
|
||||
Thread.current[:redis] ||= RedisConfiguration.pool.checkout
|
||||
end
|
||||
|
||||
def with_redis(&block)
|
||||
RedisConfiguration.with(&block)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -39,13 +39,12 @@ class Poll < ApplicationRecord
|
|||
|
||||
before_validation :prepare_options, if: :local?
|
||||
before_validation :prepare_votes_count
|
||||
|
||||
after_initialize :prepare_cached_tallies
|
||||
before_validation :prepare_cached_tallies
|
||||
|
||||
after_commit :reset_parent_cache, on: :update
|
||||
|
||||
def loaded_options
|
||||
options.map.with_index { |title, key| Option.new(self, key.to_s, title, show_totals_now? ? cached_tallies[key] : nil) }
|
||||
options.map.with_index { |title, key| Option.new(self, key.to_s, title, show_totals_now? ? (cached_tallies[key] || 0) : nil) }
|
||||
end
|
||||
|
||||
def possibly_stale?
|
||||
|
|
|
|||
|
|
@ -454,7 +454,7 @@ class Status < ApplicationRecord
|
|||
end
|
||||
|
||||
def set_poll_id
|
||||
update_column(:poll_id, poll.id) unless poll.nil?
|
||||
update_column(:poll_id, poll.id) if association(:poll).loaded? && poll.present?
|
||||
end
|
||||
|
||||
def set_visibility
|
||||
|
|
|
|||
|
|
@ -17,6 +17,18 @@ class StatusStat < ApplicationRecord
|
|||
|
||||
after_commit :reset_parent_cache
|
||||
|
||||
def replies_count
|
||||
[attributes['replies_count'], 0].max
|
||||
end
|
||||
|
||||
def reblogs_count
|
||||
[attributes['reblogs_count'], 0].max
|
||||
end
|
||||
|
||||
def favourites_count
|
||||
[attributes['favourites_count'], 0].max
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def reset_parent_cache
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ class Trends::History
|
|||
end
|
||||
|
||||
def uses
|
||||
redis.mget(*@days.map { |day| day.key_for(:uses) }).map(&:to_i).sum
|
||||
with_redis { |redis| redis.mget(*@days.map { |day| day.key_for(:uses) }).map(&:to_i).sum }
|
||||
end
|
||||
|
||||
def accounts
|
||||
redis.pfcount(*@days.map { |day| day.key_for(:accounts) })
|
||||
with_redis { |redis| redis.pfcount(*@days.map { |day| day.key_for(:accounts) }) }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -33,19 +33,21 @@ class Trends::History
|
|||
attr_reader :day
|
||||
|
||||
def accounts
|
||||
redis.pfcount(key_for(:accounts))
|
||||
with_redis { |redis| redis.pfcount(key_for(:accounts)) }
|
||||
end
|
||||
|
||||
def uses
|
||||
redis.get(key_for(:uses))&.to_i || 0
|
||||
with_redis { |redis| redis.get(key_for(:uses))&.to_i || 0 }
|
||||
end
|
||||
|
||||
def add(account_id)
|
||||
redis.pipelined do
|
||||
redis.incrby(key_for(:uses), 1)
|
||||
redis.pfadd(key_for(:accounts), account_id)
|
||||
redis.expire(key_for(:uses), EXPIRE_AFTER)
|
||||
redis.expire(key_for(:accounts), EXPIRE_AFTER)
|
||||
with_redis do |redis|
|
||||
redis.pipelined do |pipeline|
|
||||
pipeline.incrby(key_for(:uses), 1)
|
||||
pipeline.pfadd(key_for(:accounts), account_id)
|
||||
pipeline.expire(key_for(:uses), EXPIRE_AFTER)
|
||||
pipeline.expire(key_for(:accounts), EXPIRE_AFTER)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ class User < ApplicationRecord
|
|||
include Settings::Extend
|
||||
include UserRoles
|
||||
include Redisable
|
||||
include LanguagesHelper
|
||||
|
||||
# The home and list feeds will be stored in Redis for this amount
|
||||
# of time, and status fan-out to followers will include only people
|
||||
|
|
@ -248,7 +249,7 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
def preferred_posting_language
|
||||
settings.default_language || locale
|
||||
valid_locale_cascade(settings.default_language, locale)
|
||||
end
|
||||
|
||||
def setting_default_privacy
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue