1
0
مراية لـ https://github.com/postalserver/postal.git تم المزامنة 2025-11-30 21:32:30 +00:00

feat: new configuration system (and schema) (#2819)

هذا الالتزام موجود في:
Adam Cooke
2024-02-26 12:41:57 +00:00
ملتزم من قبل GitHub
الأصل 1c5ff5a9a6
التزام 0163ac3d10
77 ملفات معدلة مع 1840 إضافات و593 حذوفات

عرض الملف

@@ -7,8 +7,8 @@
class MigrationWaiter
ATTEMPTS = ENV.fetch("MIGRATION_WAITER_ATTEMPTS", 120)
SLEEP_TIME = ENV.fetch("MIGRATION_WAITER_SLEEP_TIME", 2)
ATTEMPTS = Postal::Config.migration_waiter.attempts
SLEEP_TIME = Postal::Config.migration_waiter.sleep_time
class << self
@@ -35,7 +35,7 @@ class MigrationWaiter
def wait_if_appropriate
# Don't wait if not configured
return unless ENV.fetch("MIGRATION_WAITER_ENABLED", "false") == "true"
return unless Postal::Config.migration_waiter.enabled?
# Don't wait in the console, rake tasks or rails commands
return if console? || rake_task? || rails_command?

عرض الملف

@@ -6,195 +6,128 @@ require "pathname"
require "cgi"
require "openssl"
require "fileutils"
require "konfig"
require "konfig/sources/environment"
require "konfig/sources/yaml"
require "dotenv"
require "klogger"
require_relative "error"
require_relative "version"
require_relative "config_schema"
require_relative "legacy_config_source"
module Postal
# rubocop:disable Lint/EmptyClass
class Config
end
# rubocop:enable Lint/EmptyClass
class << self
def self.host
@host ||= config.web.host || "localhost:5000"
end
def self.protocol
@protocol ||= config.web.protocol || "http"
end
def self.host_with_protocol
@host_with_protocol ||= "#{protocol}://#{host}"
end
def self.app_root
@app_root ||= Pathname.new(File.expand_path("../..", __dir__))
end
def self.config
@config ||= begin
require "hashie/mash"
config = Hashie::Mash.new(defaults)
config = config.deep_merge(yaml_config)
config.deep_merge(local_yaml_config)
end
end
def self.config_root
if ENV["POSTAL_CONFIG_ROOT"]
@config_root ||= Pathname.new(ENV["POSTAL_CONFIG_ROOT"])
else
@config_root ||= Pathname.new(File.expand_path("../../config/postal", __dir__))
end
end
def self.config_file_path
if env == "default"
@config_file_path ||= File.join(config_root, "postal.yml")
else
@config_file_path ||= File.join(config_root, "postal.#{env}.yml")
end
end
def self.env
@env ||= ENV.fetch("POSTAL_ENV", "default")
end
def self.yaml_config
@yaml_config ||= File.exist?(config_file_path) ? YAML.load_file(config_file_path) : {}
end
def self.local_config_file_path
@local_config_file_path ||= File.join(config_root, "postal.local.yml")
end
def self.local_yaml_config
@local_yaml_config ||= File.exist?(local_config_file_path) ? YAML.load_file(local_config_file_path) : {}
end
def self.defaults_file_path
@defaults_file_path ||= app_root.join("config", "postal.defaults.yml")
end
def self.defaults
@defaults ||= begin
file = File.read(defaults_file_path)
yaml = ERB.new(file).result
YAML.safe_load(yaml)
end
end
# Return a generic logger for use generally throughout Postal.
#
# @return [Klogger::Logger] A logger instance
def self.logger
@logger ||= begin
k = Klogger.new(nil, destination: Rails.env.test? ? "/dev/null" : $stdout, highlight: Rails.env.development?)
k.add_destination(graylog_logging_destination) if config.logging&.graylog&.host.present?
k
end
end
def self.process_name
@process_name ||= begin
string = "host:#{Socket.gethostname} pid:#{Process.pid}"
string += " procname:#{ENV['PROC_NAME']}" if ENV["PROC_NAME"]
string
rescue StandardError
"pid:#{Process.pid}"
end
end
def self.locker_name
string = process_name.dup
string += " job:#{Thread.current[:job_id]}" if Thread.current[:job_id]
string += " thread:#{Thread.current.native_thread_id}"
string
end
def self.locker_name_with_suffix(suffix)
"#{locker_name} #{suffix}"
end
def self.smtp_from_name
config.smtp&.from_name || "Postal"
end
def self.smtp_from_address
config.smtp&.from_address || "postal@example.com"
end
def self.smtp_private_key_path
config.smtp_server.tls_private_key_path || config_root.join("smtp.key")
end
def self.smtp_private_key
@smtp_private_key ||= OpenSSL::PKey.read(File.read(smtp_private_key_path))
end
def self.smtp_certificate_path
config.smtp_server.tls_certificate_path || config_root.join("smtp.cert")
end
def self.smtp_certificate_data
@smtp_certificate_data ||= File.read(smtp_certificate_path)
end
def self.smtp_certificates
@smtp_certificates ||= begin
certs = smtp_certificate_data.scan(/-----BEGIN CERTIFICATE-----.+?-----END CERTIFICATE-----/m)
certs.map do |c|
OpenSSL::X509::Certificate.new(c)
end.freeze
end
end
def self.signing_key_path
ENV.fetch("POSTAL_SIGNING_KEY_PATH") { config_root.join("signing.key") }
end
def self.signing_key
@signing_key ||= OpenSSL::PKey::RSA.new(File.read(signing_key_path))
end
def self.rp_dkim_dns_record
public_key = signing_key.public_key.to_s.gsub(/-+[A-Z ]+-+\n/, "").gsub(/\n/, "")
"v=DKIM1; t=s; h=sha256; p=#{public_key};"
end
class ConfigError < Postal::Error
end
def self.check_config!
return if ENV["POSTAL_SKIP_CONFIG_CHECK"].to_i == 1
unless File.exist?(config_file_path)
raise ConfigError, "No config found at #{config_file_path}"
# Return the path to the config file
#
# @return [String]
def config_file_path
ENV.fetch("POSTAL_CONFIG_FILE_PATH", "config/postal/postal.yml")
end
return if File.exist?(signing_key_path)
def initialize_config
sources = []
raise ConfigError, "No signing key found at #{signing_key_path}"
end
# Load environment variables to begin with. Any config provided
# by an environment variable will override any provided in the
# config file.
Dotenv.load(".env")
sources << Konfig::Sources::Environment.new(ENV)
def self.ip_pools?
config.general.use_ip_pools?
end
# If a config file exists, we need to load that. Config files can
# either be legacy (v1) or new (v2). Any file without a 'version'
# key is a legacy file whereas new-style config files will include
# the 'version: 2' key/value.
if File.file?(config_file_path)
puts "Loading config from #{config_file_path}"
def self.graylog_logging_destination
@graylog_logging_destination ||= begin
notifier = GELF::Notifier.new(config.logging.graylog.host, config.logging.graylog.port, "WAN")
proc do |_logger, payload, group_ids|
short_message = payload.delete(:message) || "[message missing]"
notifier.notify!(short_message: short_message, **{
facility: config.logging.graylog.facility,
_environment: Rails.env.to_s,
_version: Postal::VERSION.to_s,
_group_ids: group_ids.join(" ")
}.merge(payload.transform_keys { |k| "_#{k}".to_sym }.transform_values(&:to_s)))
config_file = File.read(config_file_path)
yaml = YAML.safe_load(config_file)
config_version = yaml["version"] || 1
case config_version
when 1
puts "WARNING: Using legacy config file format. Upgrade your postal.yml to use"
puts "version 2 of the Postal configuration or configure using environment"
puts "variables. See https://postalserver.io/config-v2 for details."
sources << LegacyConfigSource.new(yaml)
when 2
sources << Konfig::Sources::YAML.new(config_file)
else
raise "Invalid version specified in Postal config file. Must be 1 or 2."
end
else
puts "No configuration file found at #{config_file_path}"
puts "Only using environment variables for configuration"
end
# Build configuration with the provided sources.
Konfig::Config.build(ConfigSchema, sources: sources)
end
def host_with_protocol
@host_with_protocol ||= "#{Config.postal.web_protocol}://#{Config.postal.web_hostname}"
end
def logger
@logger ||= begin
k = Klogger.new(nil, destination: Config.logging.enabled? ? $stdout : "/dev/null", highlight: Config.logging.highlighting_enabled?)
k.add_destination(graylog_logging_destination) if Config.gelf.host.present?
k
end
end
def process_name
@process_name ||= begin
"host:#{Socket.gethostname} pid:#{Process.pid}"
rescue StandardError
"pid:#{Process.pid}"
end
end
def locker_name
string = process_name.dup
string += " job:#{Thread.current[:job_id]}" if Thread.current[:job_id]
string += " thread:#{Thread.current.native_thread_id}"
string
end
def locker_name_with_suffix(suffix)
"#{locker_name} #{suffix}"
end
def signing_key
@signing_key ||= OpenSSL::PKey::RSA.new(File.read(Config.postal.signing_key_path))
end
def rp_dkim_dns_record
public_key = signing_key.public_key.to_s.gsub(/-+[A-Z ]+-+\n/, "").gsub(/\n/, "")
"v=DKIM1; t=s; h=sha256; p=#{public_key};"
end
def ip_pools?
Config.postal.use_ip_pools?
end
def graylog_logging_destination
@graylog_logging_destination ||= begin
notifier = GELF::Notifier.new(Config.gelf.host, Config.gelf.port, "WAN")
proc do |_logger, payload, group_ids|
short_message = payload.delete(:message) || "[message missing]"
notifier.notify!(short_message: short_message, **{
facility: Config.gelf.facility,
_environment: Config.rails.environment,
_version: Postal::VERSION.to_s,
_group_ids: group_ids.join(" ")
}.merge(payload.transform_keys { |k| "_#{k}".to_sym }.transform_values(&:to_s)))
end
end
end
end
Config = initialize_config
end

464
lib/postal/config_schema.rb Normal file
عرض الملف

@@ -0,0 +1,464 @@
# frozen_string_literal: true
require "uri"
module Postal
# REMEMBER: If you change the schema, remember to regenerate the configuration docs
# using the rake command below:
#
# rake postal:generate_config_docs
ConfigSchema = Konfig::Schema.draw do
group :postal do
string :web_hostname do
description "The hostname that the Postal web interface runs on"
default "postal.example.com"
end
string :web_protocol do
description "The HTTP protocol to use for the Postal web interface"
default "https"
end
string :smtp_hostname do
description "The hostname that the Postal SMTP server runs on"
default "postal.example.com"
end
boolean :use_ip_pools do
description "Should IP pools be enabled for this installation?"
default false
end
integer :default_maximum_delivery_attempts do
description "The maximum number of delivery attempts"
default 18
end
integer :default_maximum_hold_expiry_days do
description "The number of days to hold a message before they will be expired"
default 7
end
integer :default_suppression_list_automatic_removal_days do
description "The number of days an address will remain in a suppression list before being removed"
default 30
end
integer :default_spam_threshold do
description "The default threshold at which a message should be treated as spam"
default 5
end
integer :default_spam_failure_threshold do
description "The default threshold at which a message should be treated as spam failure"
default 20
end
boolean :use_local_ns_for_domain_verification do
description "Domain verification and checking usually checks with a domain's nameserver. Enable this to check with the server's local nameservers."
default false
end
boolean :use_resent_sender_header do
description "Append a Resend-Sender header to all outgoing e-mails"
default true
end
string :signing_key_path do
description "Path to the private key used for signing"
default "config/postal/signing.key"
end
string :smtp_relays do
array
description "An array of SMTP relays in the format of smtp://host:port"
transform do |value|
uri = URI.parse(value)
query = uri.query ? CGI.parse(uri.query) : {}
{
host: uri.host,
port: uri.port || 25,
ssl_mode: query["ssl_mode"]&.first || "Auto"
}
end
end
end
group :web_server do
integer :default_port do
description "The default port the web server should listen on unless overriden by the PORT environment variable"
default 5000
end
string :default_bind_address do
description "The default bind address the web server should listen on unless overriden by the BIND_ADDRESS environment variable"
default "127.0.0.1"
end
integer :max_threads do
description "The maximum number of threads which can be used by the web server"
default 5
end
end
group :worker do
integer :default_health_server_port do
description "The default port for the worker health server to listen on"
default 9090
end
string :default_health_server_bind_address do
description "The default bind address for the worker health server to listen on"
default "127.0.0.1"
end
end
group :main_db do
string :host do
description "Hostname for the main MariaDB server"
default "localhost"
end
integer :port do
description "The MariaDB port to connect to"
default 3306
end
string :username do
description "The MariaDB username"
default "postal"
end
string :password do
description "The MariaDB password"
end
string :database do
description "The MariaDB database name"
default "postal"
end
integer :pool_size do
description "The maximum size of the MariaDB connection pool"
default 5
end
string :encoding do
description "The encoding to use when connecting to the MariaDB database"
default "utf8mb4"
end
end
group :message_db do
string :host do
description "Hostname for the MariaDB server which stores the mail server databases"
default "localhost"
end
integer :port do
description "The MariaDB port to connect to"
default 3306
end
string :username do
description "The MariaDB username"
default "postal"
end
string :password do
description "The MariaDB password"
end
string :database_name_prefix do
description "The MariaDB prefix to add to database names"
default "postal"
end
end
group :logging do
boolean :rails_log_enabled do
description "Enable the default Rails logger"
default false
end
string :sentry_dsn do
description "A DSN which should be used to report exceptions to Sentry"
end
boolean :enabled do
description "Enable the Postal logger to log to STDOUT"
default true
end
boolean :highlighting_enabled do
description "Enable highlighting of log lines"
default false
end
end
group :gelf do
string :host do
description "GELF-capable host to send logs to"
end
integer :port do
description "GELF port to send logs to"
default 12_201
end
string :facility do
description "The facility name to add to all log entries sent to GELF"
default "postal"
end
end
group :smtp_server do
integer :default_port do
description "The default port the SMTP server should listen on unless overriden by the PORT environment variable"
default 25
end
string :default_bind_address do
description "The default bind address the SMTP server should listen on unless overriden by the BIND_ADDRESS environment variable"
default "127.0.0.1"
end
integer :default_health_server_port do
description "The default port for the SMTP server health server to listen on"
default 9091
end
string :default_health_server_bind_address do
description "The default bind address for the SMTP server health server to listen on"
default "127.0.0.1"
end
boolean :tls_enabled do
description "Enable TLS for the SMTP server (requires certificate)"
default false
end
string :tls_certificate_path do
description "The path to the SMTP server's TLS certificate"
default "config/postal/smtp.cert"
end
string :tls_private_key_path do
description "The path to the SMTP server's TLS private key"
default "config/postal/smtp.key"
end
string :tls_ciphers do
description "Override ciphers to use for SSL"
end
string :ssl_version do
description "The SSL versions which are supported"
default "SSLv23"
end
boolean :proxy_protocol do
description "Enable proxy protocol for use behind some load balancers"
default false
end
boolean :log_connections do
description "Enable connection logging"
default false
end
integer :max_message_size do
description "The maximum message size to accept from the SMTP server (in MB)"
default 14
end
string :log_ip_address_exclusion_matcher do
description "A regular expression to use to exclude connections from logging"
end
end
group :dns do
string :mx_records do
description "The names of the default MX records"
array
default ["mx1.postal.example.com", "mx2.postal.example.com"]
end
string :spf_include do
description "The location of the SPF record"
default "spf.postal.example.com"
end
string :return_path_domain do
description "The return path hostname"
default "rp.postal.example.com"
end
string :route_domain do
description "The domain to use for hosting route-specific addresses"
default "routes.postal.example.com"
end
string :track_domain do
description "The CNAME which tracking domains should be pointed to"
default "track.postal.example.com"
end
string :helo_hostname do
description "The hostname to use in HELO/EHLO when connecting to external SMTP servers"
end
string :dkim_identifier do
description "The identifier to use for DKIM keys in DNS records"
default "postal"
end
string :domain_verify_prefix do
description "The prefix to add before TXT record verification string"
default "postal-verification"
end
string :custom_return_path_prefix do
description "The domain to use on external domains which points to the Postal return path domain"
default "psrp"
end
end
group :smtp do
string :host do
description "The hostname to send application-level e-mails to"
default "127.0.0.1"
end
integer :port do
description "The port number to send application-level e-mails to"
default 25
end
string :username do
description "The username to use when authentication to the SMTP server"
end
string :password do
description "The password to use when authentication to the SMTP server"
end
string :from_name do
description "The name to use as the from name outgoing emails from Postal"
default "Postal"
end
string :from_address do
description "The e-mail to use as the from address outgoing emails from Postal"
default "postal@example.com"
end
end
group :rails do
string :environment do
description "The Rails environment to run the application in"
default "production"
end
string :secret_key do
description "The secret key used to sign and encrypt cookies and session data in the application"
end
end
group :rspamd do
boolean :enabled do
description "Enable rspamd for message inspection"
default false
end
string :host do
description "The hostname of the rspamd server"
default "127.0.0.1"
end
integer :port do
description "The port of the rspamd server"
default 11_334
end
boolean :ssl do
description "Enable SSL for the rspamd connection"
default false
end
string :password do
description "The password for the rspamd server"
end
string :flags do
description "Any flags for the rspamd server"
end
end
group :spamd do
boolean :enabled do
description "Enable SpamAssassin for message inspection"
default false
end
string :host do
description "The hostname for the SpamAssassin server"
default "127.0.0.1"
end
integer :port do
description "The port of the SpamAssassin server"
default 783
end
end
group :clamav do
boolean :enabled do
description "Enable ClamAV for message inspection"
default false
end
string :host do
description "The host of the ClamAV server"
default "127.0.0.1"
end
integer :port do
description "The port of the ClamAV server"
default 2000
end
end
group :smtp_client do
integer :open_timeout do
description "The open timeout for outgoing SMTP connections"
default 30
end
integer :read_timeout do
description "The read timeout for outgoing SMTP connections"
default 30
end
end
group :migration_waiter do
boolean :enabled do
description "Wait for all migrations to run before starting a process"
default false
end
integer :attempts do
description "The number of attempts to try waiting for migrations to complete before start"
default 120
end
integer :sleep_time do
description "The number of seconds to wait between each migration check"
default 2
end
end
end
end

عرض الملف

@@ -0,0 +1,122 @@
# frozen_string_literal: true
require "konfig/sources/abstract"
require "konfig/error"
module Postal
class LegacyConfigSource < Konfig::Sources::Abstract
# This maps all the new configuration values to where they
# exist in the old YAML file. The source will load any YAML
# file that has been provided to this source in order. A
# warning will be generated to the console for configuration
# loaded from this format.
MAPPING = {
"postal.web_hostname" => -> (c) { c.dig("web", "host") },
"postal.web_protocol" => -> (c) { c.dig("web", "protocol") },
"postal.smtp_hostname" => -> (c) { c.dig("dns", "smtp_server_hostname") },
"postal.use_ip_pools" => -> (c) { c.dig("general", "use_ip_pools") },
"logging.sentry_dsn" => -> (c) { c.dig("general", "exception_url") },
"postal.default_maximum_delivery_attempts" => -> (c) { c.dig("general", "maximum_delivery_attempts") },
"postal.default_maximum_hold_expiry_days" => -> (c) { c.dig("general", "maximum_hold_expiry_days") },
"postal.default_suppression_list_automatic_removal_days" => -> (c) { c.dig("general", "suppression_list_removal_delay") },
"postal.use_local_ns_for_domain_verification" => -> (c) { c.dig("general", "use_local_ns_for_domains") },
"postal.default_spam_threshold" => -> (c) { c.dig("general", "default_spam_threshold") },
"postal.default_spam_failure_threshold" => -> (c) { c.dig("general", "default_spam_failure_threshold") },
"postal.use_resent_sender_header" => -> (c) { c.dig("general", "use_resent_sender_header") },
# SMTP relays must be converted to the new URI style format and they'll
# then be transformed back to a hash by the schema transform.
"postal.smtp_relays" => -> (c) { c["smtp_relays"]&.map { |r| "smtp://#{r['hostname']}:#{r['port']}?ssl_mode=#{r['ssl_mode']}" } },
"web_server.default_bind_address" => -> (c) { c.dig("web_server", "bind_address") },
"web_server.default_port" => -> (c) { c.dig("web_server", "port") },
"web_server.max_threads" => -> (c) { c.dig("web_server", "max_threads") },
"main_db.host" => -> (c) { c.dig("main_db", "host") },
"main_db.port" => -> (c) { c.dig("main_db", "port") },
"main_db.username" => -> (c) { c.dig("main_db", "username") },
"main_db.password" => -> (c) { c.dig("main_db", "password") },
"main_db.database" => -> (c) { c.dig("main_db", "database") },
"main_db.pool_size" => -> (c) { c.dig("main_db", "pool_size") },
"main_db.encoding" => -> (c) { c.dig("main_db", "encoding") },
"message_db.host" => -> (c) { c.dig("message_db", "host") },
"message_db.port" => -> (c) { c.dig("message_db", "port") },
"message_db.username" => -> (c) { c.dig("message_db", "username") },
"message_db.password" => -> (c) { c.dig("message_db", "password") },
"message_db.database_name_prefix" => -> (c) { c.dig("message_db", "prefix") },
"logging.rails_log_enabled" => -> (c) { c.dig("logging", "rails_log") },
"gelf.host" => -> (c) { c.dig("logging", "graylog", "host") },
"gelf.port" => -> (c) { c.dig("logging", "graylog", "port") },
"gelf.facility" => -> (c) { c.dig("logging", "graylog", "facility") },
"smtp_server.default_port" => -> (c) { c.dig("smtp_server", "port") },
"smtp_server.default_bind_address" => -> (c) { c.dig("smtp_server", "bind_address") },
"smtp_server.tls_enabled" => -> (c) { c.dig("smtp_server", "tls_enabled") },
"smtp_server.tls_certificate_path" => -> (c) { c.dig("smtp_server", "tls_certificate_path") },
"smtp_server.tls_private_key_path" => -> (c) { c.dig("smtp_server", "tls_private_key_path") },
"smtp_server.tls_ciphers" => -> (c) { c.dig("smtp_server", "tls_ciphers") },
"smtp_server.ssl_version" => -> (c) { c.dig("smtp_server", "ssl_version") },
"smtp_server.proxy_protocol" => -> (c) { c.dig("smtp_server", "proxy_protocol") },
"smtp_server.log_connections" => -> (c) { c.dig("smtp_server", "log_connect") },
"smtp_server.max_message_size" => -> (c) { c.dig("smtp_server", "max_message_size") },
"dns.mx_records" => -> (c) { c.dig("dns", "mx_records") },
"dns.spf_include" => -> (c) { c.dig("dns", "spf_include") },
"dns.return_path_domain" => -> (c) { c.dig("dns", "return_path") },
"dns.route_domain" => -> (c) { c.dig("dns", "route_domain") },
"dns.track_domain" => -> (c) { c.dig("dns", "track_domain") },
"dns.helo_hostname" => -> (c) { c.dig("dns", "helo_hostname") },
"dns.dkim_identifier" => -> (c) { c.dig("dns", "dkim_identifier") },
"dns.domain_verify_prefix" => -> (c) { c.dig("dns", "domain_verify_prefix") },
"dns.custom_return_path_prefix" => -> (c) { c.dig("dns", "custom_return_path_prefix") },
"smtp.host" => -> (c) { c.dig("smtp", "host") },
"smtp.port" => -> (c) { c.dig("smtp", "port") },
"smtp.username" => -> (c) { c.dig("smtp", "username") },
"smtp.password" => -> (c) { c.dig("smtp", "password") },
"smtp.from_name" => -> (c) { c.dig("smtp", "from_name") },
"smtp.from_address" => -> (c) { c.dig("smtp", "from_address") },
"rails.environment" => -> (c) { c.dig("rails", "environment") },
"rails.secret_key" => -> (c) { c.dig("rails", "secret_key") },
"rspamd.enabled" => -> (c) { c.dig("rspamd", "enabled") },
"rspamd.host" => -> (c) { c.dig("rspamd", "host") },
"rspamd.port" => -> (c) { c.dig("rspamd", "port") },
"rspamd.ssl" => -> (c) { c.dig("rspamd", "ssl") },
"rspamd.password" => -> (c) { c.dig("rspamd", "password") },
"rspamd.flags" => -> (c) { c.dig("rspamd", "flags") },
"spamd.enabled" => -> (c) { c.dig("spamd", "enabled") },
"spamd.host" => -> (c) { c.dig("spamd", "host") },
"spamd.port" => -> (c) { c.dig("spamd", "port") },
"clamav.enabled" => -> (c) { c.dig("clamav", "enabled") },
"clamav.host" => -> (c) { c.dig("clamav", "host") },
"clamav.port" => -> (c) { c.dig("clamav", "port") },
"smtp_client.open_timeout" => -> (c) { c.dig("smtp_client", "open_timeout") },
"smtp_client.read_timeout" => -> (c) { c.dig("smtp_client", "read_timeout") }
}.freeze
def initialize(config)
super()
@config = config
end
def get(path, attribute: nil)
path_string = path.join(".")
raise Konfig::ValueNotPresentError unless MAPPING.key?(path_string)
legacy_value = MAPPING[path_string].call(@config)
raise Konfig::ValueNotPresentError if legacy_value.nil?
legacy_value
end
end
end

عرض الملف

@@ -63,11 +63,11 @@ module Postal
def establish_connection
Mysql2::Client.new(
host: Postal.config.message_db.host,
username: Postal.config.message_db.username,
password: Postal.config.message_db.password,
port: Postal.config.message_db.port,
encoding: Postal.config.message_db.encoding || "utf8mb4"
host: Postal::Config.message_db.host,
username: Postal::Config.message_db.username,
password: Postal::Config.message_db.password,
port: Postal::Config.message_db.port,
encoding: Postal::Config.message_db.encoding
)
end

عرض الملف

@@ -272,7 +272,7 @@ module Postal
# Return the correct database name
#
def database_name
@database_name ||= "#{Postal.config.message_db.prefix}-server-#{@server_id}"
@database_name ||= "#{Postal::Config.message_db.database_name_prefix}-server-#{@server_id}"
end
#

عرض الملف

@@ -128,7 +128,7 @@ module Postal
#
def create_delivery(status, options = {})
delivery = Delivery.create(self, options.merge(status: status))
hold_expiry = status == "Held" ? Postal.config.general.maximum_hold_expiry_days.days.from_now.to_f : nil
hold_expiry = status == "Held" ? Postal::Config.postal.default_maximum_hold_expiry_days.days.from_now.to_f : nil
update(status: status, last_delivery_attempt: delivery.timestamp.to_f, held: status == "Held", hold_expiry: hold_expiry)
delivery
end
@@ -511,7 +511,7 @@ module Postal
# Was thsi message sent to a return path?
#
def rcpt_to_return_path?
!!(rcpt_to =~ /@#{Regexp.escape(Postal.config.dns.custom_return_path_prefix)}\./)
!!(rcpt_to =~ /@#{Regexp.escape(Postal::Config.dns.custom_return_path_prefix)}\./)
end
#

عرض الملف

@@ -9,7 +9,7 @@ module Postal
end
def add(type, address, options = {})
keep_until = (options[:days] || Postal.config.general.suppression_list_removal_delay).days.from_now.to_f
keep_until = (options[:days] || Postal::Config.postal.default_suppression_list_automatic_removal_days).days.from_now.to_f
if existing = @database.select("suppressions", where: { type: type, address: address }, limit: 1).first
reason = options[:reason] || existing["reason"]
@database.update("suppressions", { reason: reason, keep_until: keep_until }, where: { id: existing["id"] })

عرض الملف

@@ -24,14 +24,14 @@ module Postal
# installation.
def inspectors
[].tap do |inspectors|
if Postal.config.rspamd&.enabled
inspectors << MessageInspectors::Rspamd.new(Postal.config.rspamd)
elsif Postal.config.spamd&.enabled
inspectors << MessageInspectors::SpamAssassin.new(Postal.config.spamd)
if Postal::Config.rspamd.enabled?
inspectors << MessageInspectors::Rspamd.new(Postal::Config.rspamd)
elsif Postal::Config.spamd.enabled?
inspectors << MessageInspectors::SpamAssassin.new(Postal::Config.spamd)
end
if Postal.config.clamav&.enabled
inspectors << MessageInspectors::Clamav.new(Postal.config.clamav)
if Postal::Config.clamav.enabled?
inspectors << MessageInspectors::Clamav.new(Postal::Config.clamav)
end
end
end

عرض الملف

@@ -0,0 +1,37 @@
# frozen_string_literal: true
require "konfig/exporters/abstract"
module Postal
class YamlConfigExporter < Konfig::Exporters::Abstract
def export
contents = []
contents << "version: 2"
contents << ""
@schema.groups.each do |group_name, group|
contents << "#{group_name}:"
group.attributes.each do |name, attr|
contents << " # #{attr.description}"
if attr.array?
if attr.default.blank?
contents << " #{name}: []"
else
contents << " #{name}:"
attr.default.each do |d|
contents << " - #{d}"
end
end
else
contents << " #{name}: #{attr.default}"
end
end
contents << ""
end
contents.join("\n")
end
end
end

عرض الملف

@@ -10,6 +10,18 @@ namespace :postal do
server.message_db.provisioner.migrate
end
end
desc "Generate configuration documentation"
task generate_config_docs: :environment do
require "konfig/exporters/env_vars_as_markdown"
FileUtils.mkdir_p("doc/config")
output = Konfig::Exporters::EnvVarsAsMarkdown.new(Postal::ConfigSchema).export
File.write("doc/config/environment-variables.md", output)
output = Postal::YamlConfigExporter.new(Postal::ConfigSchema).export
File.write("doc/config/yaml.yml", output)
end
end
Rake::Task["db:migrate"].enhance do