1
0
مراية لـ https://github.com/postalserver/postal.git تم المزامنة 2025-11-30 21:32:30 +00:00
الملفات
postal/app/models/route.rb
2024-02-10 23:03:28 +00:00

235 أسطر
6.3 KiB
Ruby
خام اللوم التاريخ

هذا الملف يحتوي على أحرف Unicode غير مرئية

هذا الملف يحتوي على أحرف Unicode غير مرئية لا يمكن التمييز بينها بعين الإنسان ولكن قد تتم معالجتها بشكل مختلف بواسطة الحاسوب. إذا كنت تعتقد أن هذا مقصود، يمكنك تجاهل هذا التحذير بأمان. استخدم زر الهروب للكشف عنها.

# frozen_string_literal: true
# == Schema Information
#
# Table name: routes
#
# id :integer not null, primary key
# uuid :string(255)
# server_id :integer
# domain_id :integer
# endpoint_id :integer
# endpoint_type :string(255)
# name :string(255)
# spam_mode :string(255)
# created_at :datetime
# updated_at :datetime
# token :string(255)
# mode :string(255)
#
# Indexes
#
# index_routes_on_token (token)
#
class Route < ApplicationRecord
MODES = %w[Endpoint Accept Hold Bounce Reject].freeze
SPAM_MODES = %w[Mark Quarantine Fail].freeze
ENDPOINT_TYPES = %w[SMTPEndpoint HTTPEndpoint AddressEndpoint].freeze
include HasUUID
belongs_to :server
belongs_to :domain, optional: true
belongs_to :endpoint, polymorphic: true, optional: true
has_many :additional_route_endpoints, dependent: :destroy
validates :name, presence: true, format: /\A(([a-z0-9\-.]*)|(\*)|(__returnpath__))\z/
validates :spam_mode, inclusion: { in: SPAM_MODES }
validates :endpoint, presence: { if: proc { mode == "Endpoint" } }
validates :domain_id, presence: { unless: :return_path? }
validate :validate_route_is_routed
validate :validate_domain_belongs_to_server
validate :validate_endpoint_belongs_to_server
validate :validate_name_uniqueness
validate :validate_return_path_route_endpoints
validate :validate_no_additional_routes_on_non_endpoint_route
after_save :save_additional_route_endpoints
random_string :token, type: :chars, length: 8, unique: true
def return_path?
name == "__returnpath__"
end
def description
if return_path?
"Return Path"
else
"#{name}@#{domain.name}"
end
end
def _endpoint
if mode == "Endpoint"
@endpoint ||= endpoint ? "#{endpoint.class}##{endpoint.uuid}" : nil
else
@endpoint ||= mode
end
end
def _endpoint=(value)
if value.blank?
self.endpoint = nil
self.mode = nil
elsif value =~ /\#/
class_name, id = value.split("#", 2)
unless ENDPOINT_TYPES.include?(class_name)
raise Postal::Error, "Invalid endpoint class name '#{class_name}'"
end
self.endpoint = class_name.constantize.find_by_uuid(id)
self.mode = "Endpoint"
else
self.endpoint = nil
self.mode = value
end
end
def forward_address
@forward_address ||= "#{token}@#{Postal.config.dns.route_domain}"
end
def wildcard?
name == "*"
end
def additional_route_endpoints_array
@additional_route_endpoints_array ||= additional_route_endpoints.map(&:_endpoint)
end
def additional_route_endpoints_array=(array)
@additional_route_endpoints_array = array.reject(&:blank?)
end
def save_additional_route_endpoints
return unless @additional_route_endpoints_array
seen = []
@additional_route_endpoints_array.each do |item|
if existing = additional_route_endpoints.find_by_endpoint(item)
seen << existing.id
else
route = additional_route_endpoints.build(_endpoint: item)
if route.save
seen << route.id
else
route.errors.each do |_, message|
errors.add :base, message
end
raise ActiveRecord::RecordInvalid
end
end
end
additional_route_endpoints.where.not(id: seen).destroy_all
end
#
# This message will create a suitable number of message objects for messages that
#  are destined for this route. It receives a block which can set the message content
# but most information is specified already.
#
# Returns an array of created messages.
#
def create_messages(&block)
messages = []
message = build_message
if mode == "Endpoint" && server.message_db.schema_version >= 18
message.endpoint_type = endpoint_type
message.endpoint_id = endpoint_id
end
block.call(message)
message.save
messages << message
# Also create any messages for additional endpoints that might exist
if mode == "Endpoint" && server.message_db.schema_version >= 18
additional_route_endpoints.each do |endpoint|
next unless endpoint.endpoint
message = build_message
message.endpoint_id = endpoint.endpoint_id
message.endpoint_type = endpoint.endpoint_type
block.call(message)
message.save
messages << message
end
end
messages
end
def build_message
message = server.message_db.new_message
message.scope = "incoming"
message.rcpt_to = description
message.domain_id = domain&.id
message.route_id = id
message
end
private
def validate_route_is_routed
return unless mode.nil?
errors.add :endpoint, "must be chosen"
end
def validate_domain_belongs_to_server
if domain && ![server, server.organization].include?(domain.owner)
errors.add :domain, :invalid
end
return unless domain && !domain.verified?
errors.add :domain, "has not been verified yet"
end
def validate_endpoint_belongs_to_server
return unless endpoint && endpoint&.server != server
errors.add :endpoint, :invalid
end
def validate_name_uniqueness
return if server.nil?
if domain
if route = Route.includes(:domain).where(domains: { name: domain.name }, name: name).where.not(id: id).first
errors.add :name, "is configured on the #{route.server.full_permalink} mail server"
end
elsif Route.where(name: "__returnpath__").where.not(id: id).exists?
errors.add :base, "A return path route already exists for this server"
end
end
def validate_return_path_route_endpoints
return unless return_path?
return unless mode != "Endpoint" || endpoint_type != "HTTPEndpoint"
errors.add :base, "Return path routes must point to an HTTP endpoint"
end
def validate_no_additional_routes_on_non_endpoint_route
return unless mode != "Endpoint" && !additional_route_endpoints_array.empty?
errors.add :base, "Additional routes are not permitted unless the primary route is an actual endpoint"
end
class << self
def find_by_name_and_domain(name, domain)
route = Route.includes(:domain).where(name: name, domains: { name: domain }).first
if route.nil?
route = Route.includes(:domain).where(name: "*", domains: { name: domain }).first
end
route
end
end
end