e51f0bfee7
Account incineration could leave orphaned records due to missing cascade declarations and async jobs failing when the account was already gone. Cascade fixes: - Add Account::Searchable concern to clean up Search::Query (delete_all) and Search::Record (destroy_all, respects SQLite FTS dependent: :destroy) - Add before_destroy in Account::Storage to delete storage entries - Suppress storage entry recording during incineration so attachment purge callbacks don't create entries or enqueue materialize jobs - Guard Access#clean_inaccessible_data_later with unless user.destroyed? to avoid enqueuing pointless jobs during user cascade Job tenancy: - Extract AccountTenanted concern from the global ActiveJob initializer into ApplicationJob (include) with targeted prepends for ActionMailer::MailDeliveryJob and Turbo broadcast jobs - Defer account resolution from deserialize to perform so that missing accounts raise DeserializationError inside the execution path where discard_on can handle it Tests: - Comprehensive incineration test covering 35 model types with before/after assertions and full enqueued job processing - Mailer deliver_later test verifying account context survives job serialization for multi-account users - Turbo broadcast test verifying account-scoped URLs in rendered partials
48 lines
1.1 KiB
Ruby
48 lines
1.1 KiB
Ruby
# Serializes the current account into job data so that jobs run
|
|
# within the correct account context via Current.with_account.
|
|
#
|
|
# Account resolution is deferred to an around_perform callback so
|
|
# that missing accounts raise DeserializationError inside the
|
|
# execution path where discard_on can handle it.
|
|
module AccountTenanted
|
|
extend ActiveSupport::Concern
|
|
|
|
prepended do
|
|
attr_reader :account
|
|
around_perform :with_account_context
|
|
end
|
|
|
|
def initialize(...)
|
|
super
|
|
@account = Current.account
|
|
end
|
|
|
|
def serialize
|
|
super.merge({ "account" => @account&.to_gid })
|
|
end
|
|
|
|
def deserialize(job_data)
|
|
super
|
|
@account_gid = job_data["account"]
|
|
end
|
|
|
|
private
|
|
def with_account_context(&block)
|
|
resolve_account!
|
|
|
|
if account.present?
|
|
Current.with_account(account, &block)
|
|
else
|
|
yield
|
|
end
|
|
end
|
|
|
|
def resolve_account!
|
|
if @account_gid
|
|
@account = GlobalID::Locator.locate(@account_gid)
|
|
end
|
|
rescue ActiveRecord::RecordNotFound
|
|
raise ActiveJob::DeserializationError
|
|
end
|
|
end
|