This rule was added in rubocop-rails 2.26.0. Applying it allows us to anticipate the deprecation of the current enum syntax using keyword arguments, which is set to be removed in Rails 8.0, as mentioned in the rule's own documentation: https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsenumsyntax
201 lines
4.2 KiB
Ruby
201 lines
4.2 KiB
Ruby
class Tenant < ApplicationRecord
|
|
enum :schema_type, %w[subdomain domain]
|
|
|
|
validates :schema,
|
|
presence: true,
|
|
uniqueness: true,
|
|
exclusion: { in: ->(*) { excluded_subdomains }},
|
|
format: { with: URI::DEFAULT_PARSER.regexp[:HOST] }
|
|
validates :name, presence: true, uniqueness: true
|
|
|
|
after_create :create_schema
|
|
after_update :rename_schema
|
|
after_destroy :destroy_schema
|
|
|
|
scope :only_hidden, -> { where.not(hidden_at: nil) }
|
|
|
|
def self.find_by_domain(host)
|
|
domain.find_by(schema: host)
|
|
end
|
|
|
|
def self.resolve_host(host)
|
|
return nil if Rails.application.config.multitenancy.blank?
|
|
return nil if host.blank? || host.match?(Resolv::AddressRegex)
|
|
|
|
schema = schema_for(host)
|
|
|
|
if schema && only_hidden.find_by(schema: schema)
|
|
raise Apartment::TenantNotFound
|
|
else
|
|
schema
|
|
end
|
|
end
|
|
|
|
def self.schema_for(host)
|
|
host_without_www = host.delete_prefix("www.")
|
|
|
|
if find_by_domain(host)
|
|
host
|
|
elsif find_by_domain(host_without_www)
|
|
host_without_www
|
|
else
|
|
host_domain = allowed_domains.find { |domain| host == domain || host.ends_with?(".#{domain}") }
|
|
schema = host_without_www.sub(/\.?#{host_domain}\Z/, "").presence
|
|
|
|
if find_by_domain(schema)
|
|
raise Apartment::TenantNotFound
|
|
else
|
|
schema
|
|
end
|
|
end
|
|
end
|
|
|
|
def self.allowed_domains
|
|
dev_domains = %w[localhost lvh.me example.com]
|
|
dev_domains + [default_host]
|
|
end
|
|
|
|
def self.excluded_subdomains
|
|
%w[mail public shared_extensions www]
|
|
end
|
|
|
|
def self.default_url_options
|
|
ActionMailer::Base.default_url_options
|
|
end
|
|
|
|
def self.default_host
|
|
default_url_options[:host]
|
|
end
|
|
|
|
def self.default_domain
|
|
if default_host == "localhost"
|
|
"lvh.me"
|
|
else
|
|
default_host
|
|
end
|
|
end
|
|
|
|
def self.current_url_options
|
|
default_url_options.merge(host: current_host)
|
|
end
|
|
|
|
def self.current_host
|
|
host_for(current_schema)
|
|
end
|
|
|
|
def self.host_for(schema)
|
|
if schema == "public"
|
|
default_host
|
|
elsif find_by_domain(schema)
|
|
schema
|
|
else
|
|
"#{schema}.#{default_domain}"
|
|
end
|
|
end
|
|
|
|
def self.current_secrets
|
|
if default?
|
|
Rails.application.secrets
|
|
else
|
|
@secrets ||= {}
|
|
@cached_rails_secrets ||= Rails.application.secrets
|
|
|
|
if @cached_rails_secrets != Rails.application.secrets
|
|
@secrets = {}
|
|
@cached_rails_secrets = Rails.application.secrets
|
|
end
|
|
|
|
@secrets[current_schema] ||= Rails.application.secrets.merge(
|
|
Rails.application.secrets.dig(:tenants, current_schema.to_sym).to_h
|
|
)
|
|
end
|
|
end
|
|
|
|
def self.subfolder_path
|
|
subfolder_path_for(current_schema)
|
|
end
|
|
|
|
def self.subfolder_path_for(schema)
|
|
if schema == "public"
|
|
""
|
|
else
|
|
File.join("tenants", schema)
|
|
end
|
|
end
|
|
|
|
def self.path_with_subfolder(filename_or_folder)
|
|
File.join(subfolder_path, filename_or_folder).delete_prefix("/")
|
|
end
|
|
|
|
def self.default?
|
|
current_schema == "public"
|
|
end
|
|
|
|
def self.current_schema
|
|
Apartment::Tenant.current
|
|
end
|
|
|
|
def self.current
|
|
find_by(schema: current_schema)
|
|
end
|
|
|
|
def self.switch(...)
|
|
Apartment::Tenant.switch(...)
|
|
end
|
|
|
|
def self.run_on_each(&)
|
|
["public"].union(Apartment.tenant_names).each do |schema|
|
|
switch(schema, &)
|
|
end
|
|
end
|
|
|
|
def host
|
|
self.class.host_for(schema)
|
|
end
|
|
|
|
def hide
|
|
update_attribute(:hidden_at, Time.current)
|
|
end
|
|
|
|
def restore
|
|
update_attribute(:hidden_at, nil)
|
|
end
|
|
|
|
def hidden?
|
|
hidden_at.present?
|
|
end
|
|
|
|
private
|
|
|
|
def create_schema
|
|
Apartment::Tenant.create(schema)
|
|
end
|
|
|
|
def rename_schema
|
|
if saved_change_to_schema?
|
|
ActiveRecord::Base.connection.execute(
|
|
"ALTER SCHEMA \"#{schema_before_last_save}\" RENAME TO \"#{schema}\";"
|
|
)
|
|
|
|
rename_storage
|
|
end
|
|
end
|
|
|
|
def rename_storage
|
|
service = ActiveStorage::Blob.service
|
|
|
|
return unless service.respond_to?(:tenant_root_for)
|
|
|
|
old_storage = service.tenant_root_for(schema_before_last_save)
|
|
|
|
return unless File.directory?(old_storage)
|
|
|
|
new_storage = service.tenant_root_for(schema)
|
|
File.rename(old_storage, new_storage)
|
|
end
|
|
|
|
def destroy_schema
|
|
Apartment::Tenant.drop(schema)
|
|
end
|
|
end
|