مراية لـ
https://github.com/postalserver/postal.git
تم المزامنة 2026-03-04 23:04:08 +00:00
Compare commits
16 الالتزامات
| المؤلف | SHA1 | التاريخ | |
|---|---|---|---|
|
|
ff901e99f7 | ||
|
|
ce19bf7988 | ||
|
|
8794a2f447 | ||
|
|
6b2bf9062d | ||
|
|
49cceaa6ca | ||
|
|
71f51db3c2 | ||
|
|
4a46f690de | ||
|
|
854aa5ebc8 | ||
|
|
990b575902 | ||
|
|
2bad645d98 | ||
|
|
41f6cf4d90 | ||
|
|
3fb40e4e24 | ||
|
|
0f9882f132 | ||
|
|
26aae298a4 | ||
|
|
88108566f8 | ||
|
|
e156c21dee |
10
.github/workflows/ci.yml
مباع
10
.github/workflows/ci.yml
مباع
@@ -97,17 +97,17 @@ jobs:
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
- uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ vars.KRYSTAL_REGISTRY_HOST }}
|
||||
username: ${{ vars.KRYSTAL_REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.KRYSTAL_REGISTRY_PASSWORD }}
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: docker/build-push-action@v4
|
||||
with:
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/postalserver/postal:stable
|
||||
ghcr.io/postalserver/postal:${{ needs.release-please.outputs.version }}
|
||||
cache-from: type=local,src=/cache/krystal-identity
|
||||
cache-to: type=local,dest=/cache/krystal-identity,mode=max
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
target: full
|
||||
build-args: |
|
||||
VERSION=${{ needs.release-please.outputs.version }}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "2.1.5"
|
||||
".": "2.2.0"
|
||||
}
|
||||
|
||||
38
CHANGELOG.md
38
CHANGELOG.md
@@ -2,6 +2,44 @@
|
||||
|
||||
This file contains all the latest changes and updates to Postal.
|
||||
|
||||
## [2.2.0](https://github.com/postalserver/postal/compare/2.1.6...2.2.0) (2024-02-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* load signing key path from POSTAL_SIGNING_KEY_PATH ([4a46f69](https://github.com/postalserver/postal/commit/4a46f690de3010f1ae4d6c17739530a4eae35c09))
|
||||
* support for configuring postal with environment variables ([854aa5e](https://github.com/postalserver/postal/commit/854aa5ebc87de692b4691d48759aefd6fae9d133))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* don't use indifferent access for job params ([2bad645](https://github.com/postalserver/postal/commit/2bad645d980ad4b712a3c863b5350e4ee2895071)), closes [#2477](https://github.com/postalserver/postal/issues/2477) [#2714](https://github.com/postalserver/postal/issues/2714) [#2476](https://github.com/postalserver/postal/issues/2476) [#2500](https://github.com/postalserver/postal/issues/2500)
|
||||
* extract x-postal-tag before holding ([6b2bf90](https://github.com/postalserver/postal/commit/6b2bf9062d662ede14617c4995ffaacca023a3b1)), closes [#2684](https://github.com/postalserver/postal/issues/2684)
|
||||
* fixes error messages in web ui ([71f51db](https://github.com/postalserver/postal/commit/71f51db3c2515addaf8b280667555427d64796be))
|
||||
* ignore message DB migrations in autoloader ([3fb40e4](https://github.com/postalserver/postal/commit/3fb40e4e247893b314e42affa4604a7a71a52c59))
|
||||
* move tracking middleware before host authorization ([49cceaa](https://github.com/postalserver/postal/commit/49cceaa6ca862965448041279fc439ecba163ff8)), closes [#2415](https://github.com/postalserver/postal/issues/2415)
|
||||
* use utc timestamps when determining raw table names ([ce19bf7](https://github.com/postalserver/postal/commit/ce19bf7988d522bf46aabf68090751427e286ffc))
|
||||
|
||||
|
||||
### Miscellaneous Chores
|
||||
|
||||
* add binstubs for bundle and rspec ([41f6cf4](https://github.com/postalserver/postal/commit/41f6cf4d909518526af55ecb3fcccfa8fb8e1da2))
|
||||
* add script to send html emails to a local SMTP server ([8794a2f](https://github.com/postalserver/postal/commit/8794a2f44783658a075a6f3985079ae4743412b1))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* remove explicit autoload ([0f9882f](https://github.com/postalserver/postal/commit/0f9882f13204124df630606b1b9e36787c9c4011))
|
||||
* remove Postal::Job.perform method ([990b575](https://github.com/postalserver/postal/commit/990b575902c45bb1678cc95f53ef3166c4b7092e))
|
||||
|
||||
## [2.1.6](https://github.com/postalserver/postal/compare/2.1.5...2.1.6) (2024-01-30)
|
||||
|
||||
|
||||
### Miscellaneous Chores
|
||||
|
||||
* **build:** fixes docker login action credentials ([8810856](https://github.com/postalserver/postal/commit/88108566f8ab33f1a4263a36a5c1ffc071645ac3))
|
||||
* update release please to include more categories in changelog ([e156c21](https://github.com/postalserver/postal/commit/e156c21dee304de7d10c2958c493cce73c2d8fea))
|
||||
|
||||
## [2.1.5](https://github.com/postalserver/postal/compare/2.1.4...2.1.5) (2024-01-30)
|
||||
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ class ApplicationController < ActionController::Base
|
||||
def render_form_errors(action_name, object)
|
||||
respond_to do |wants|
|
||||
wants.html { render action_name }
|
||||
wants.json { render json: { form_errors: object.errors.full_messages }, status: :unprocessable_entity }
|
||||
wants.json { render json: { form_errors: object.errors.map(&:full_message) }, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -292,6 +292,12 @@ class UnqueueMessageJob < Postal::Job
|
||||
next
|
||||
end
|
||||
|
||||
# Extract a tag and add it to the message if one doesn't exist
|
||||
if queued_message.message.tag.nil? && tag = queued_message.message.headers["x-postal-tag"]
|
||||
log "#{log_prefix} Added tag #{tag.last}"
|
||||
queued_message.message.update(tag: tag.last)
|
||||
end
|
||||
|
||||
#
|
||||
# If the credentials for this message is marked as holding and this isn't manual, hold it
|
||||
#
|
||||
@@ -312,12 +318,6 @@ class UnqueueMessageJob < Postal::Job
|
||||
next
|
||||
end
|
||||
|
||||
# Extract a tag and add it to the message if one doesn't exist
|
||||
if queued_message.message.tag.nil? && tag = queued_message.message.headers["x-postal-tag"]
|
||||
log "#{log_prefix} Added tag #{tag.last}"
|
||||
queued_message.message.update(tag: tag.last)
|
||||
end
|
||||
|
||||
# Parse the content of the message as appropriate
|
||||
if queued_message.message.should_parse?
|
||||
log "#{log_prefix} Parsing message content as it hasn't been parsed before"
|
||||
|
||||
110
bin/bundle
110
bin/bundle
@@ -1,3 +1,109 @@
|
||||
#!/usr/bin/env ruby
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
load Gem.bin_path("bundler", "bundle")
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'bundle' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "rubygems"
|
||||
|
||||
m = Module.new do
|
||||
module_function
|
||||
|
||||
def invoked_as_script?
|
||||
File.expand_path($0) == File.expand_path(__FILE__)
|
||||
end
|
||||
|
||||
def env_var_version
|
||||
ENV["BUNDLER_VERSION"]
|
||||
end
|
||||
|
||||
def cli_arg_version
|
||||
return unless invoked_as_script? # don't want to hijack other binstubs
|
||||
return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
|
||||
bundler_version = nil
|
||||
update_index = nil
|
||||
ARGV.each_with_index do |a, i|
|
||||
if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
|
||||
bundler_version = a
|
||||
end
|
||||
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
|
||||
bundler_version = $1
|
||||
update_index = i
|
||||
end
|
||||
bundler_version
|
||||
end
|
||||
|
||||
def gemfile
|
||||
gemfile = ENV["BUNDLE_GEMFILE"]
|
||||
return gemfile if gemfile && !gemfile.empty?
|
||||
|
||||
File.expand_path("../Gemfile", __dir__)
|
||||
end
|
||||
|
||||
def lockfile
|
||||
lockfile =
|
||||
case File.basename(gemfile)
|
||||
when "gems.rb" then gemfile.sub(/\.rb$/, ".locked")
|
||||
else "#{gemfile}.lock"
|
||||
end
|
||||
File.expand_path(lockfile)
|
||||
end
|
||||
|
||||
def lockfile_version
|
||||
return unless File.file?(lockfile)
|
||||
lockfile_contents = File.read(lockfile)
|
||||
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
|
||||
Regexp.last_match(1)
|
||||
end
|
||||
|
||||
def bundler_requirement
|
||||
@bundler_requirement ||=
|
||||
env_var_version ||
|
||||
cli_arg_version ||
|
||||
bundler_requirement_for(lockfile_version)
|
||||
end
|
||||
|
||||
def bundler_requirement_for(version)
|
||||
return "#{Gem::Requirement.default}.a" unless version
|
||||
|
||||
bundler_gem_version = Gem::Version.new(version)
|
||||
|
||||
bundler_gem_version.approximate_recommendation
|
||||
end
|
||||
|
||||
def load_bundler!
|
||||
ENV["BUNDLE_GEMFILE"] ||= gemfile
|
||||
|
||||
activate_bundler
|
||||
end
|
||||
|
||||
def activate_bundler
|
||||
gem_error = activation_error_handling do
|
||||
gem "bundler", bundler_requirement
|
||||
end
|
||||
return if gem_error.nil?
|
||||
require_error = activation_error_handling do
|
||||
require "bundler/version"
|
||||
end
|
||||
return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
|
||||
warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
|
||||
exit 42
|
||||
end
|
||||
|
||||
def activation_error_handling
|
||||
yield
|
||||
nil
|
||||
rescue StandardError, LoadError => e
|
||||
e
|
||||
end
|
||||
end
|
||||
|
||||
m.load_bundler!
|
||||
|
||||
if m.invoked_as_script?
|
||||
load Gem.bin_path("bundler", "bundle")
|
||||
end
|
||||
|
||||
27
bin/rspec
Executable file
27
bin/rspec
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'rspec' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
|
||||
load(bundle_binstub)
|
||||
else
|
||||
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||
end
|
||||
end
|
||||
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
load Gem.bin_path("rspec-core", "rspec")
|
||||
@@ -27,14 +27,14 @@ module Postal
|
||||
end
|
||||
|
||||
# Include from lib
|
||||
config.eager_load_namespaces << Postal
|
||||
config.eager_load_paths << Rails.root.join("lib")
|
||||
|
||||
# Disable field_with_errors
|
||||
config.action_view.field_error_proc = proc { |t, i| t }
|
||||
|
||||
# Load the tracking server middleware
|
||||
require "postal/tracking_middleware"
|
||||
config.middleware.use Postal::TrackingMiddleware
|
||||
config.middleware.insert_before ActionDispatch::HostAuthorization, Postal::TrackingMiddleware
|
||||
|
||||
config.logger = Postal.logger_for(:rails)
|
||||
|
||||
|
||||
@@ -12,13 +12,17 @@
|
||||
|
||||
# These inflection rules are supported but not enabled by default:
|
||||
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
||||
inflect.acronym "API"
|
||||
inflect.acronym "SMTP"
|
||||
inflect.acronym "IP"
|
||||
inflect.acronym "DNS"
|
||||
inflect.acronym "UUID"
|
||||
inflect.acronym "HTTP"
|
||||
inflect.acronym "DB"
|
||||
inflect.acronym "MX"
|
||||
|
||||
inflect.acronym "DKIM"
|
||||
inflect.acronym "HTTP"
|
||||
inflect.acronym "SMTP"
|
||||
inflect.acronym "UUID"
|
||||
|
||||
inflect.acronym "API"
|
||||
inflect.acronym "DNS"
|
||||
|
||||
inflect.acronym "DB"
|
||||
inflect.acronym "IP"
|
||||
inflect.acronym "MQ"
|
||||
inflect.acronym "MX"
|
||||
end
|
||||
|
||||
5
config/initializers/zeitwerk.rb
Normal file
5
config/initializers/zeitwerk.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
Rails.autoloaders.each do |autoloader|
|
||||
# Ignore the message DB migrations directory as it doesn't follow
|
||||
# Zeitwerk's conventions and is always loaded and executed in order.
|
||||
autoloader.ignore(Rails.root.join('lib/postal/message_db/migrations'))
|
||||
end
|
||||
@@ -5,126 +5,122 @@
|
||||
# You can refer to this for a complete listing all available configuration options.
|
||||
|
||||
web:
|
||||
host: postal.example.com
|
||||
protocol: https
|
||||
host: <%= ENV.fetch('POSTAL_HOST', 'postal.example.com') %>
|
||||
protocol: <%= ENV.fetch('POSTAL_PROTOCOL', 'https') %>
|
||||
|
||||
general:
|
||||
use_ip_pools: false
|
||||
exception_url:
|
||||
maximum_delivery_attempts: 18
|
||||
maximum_hold_expiry_days: 7
|
||||
suppression_list_removal_delay: 30
|
||||
use_local_ns_for_domains: false
|
||||
default_spam_threshold: 5.0
|
||||
default_spam_failure_threshold: 20.0
|
||||
use_resent_sender_header: true
|
||||
use_ip_pools: <%= ENV.fetch('POSTAL_USE_IP_POOLS', 'false') %>
|
||||
exception_url: <%= ENV.fetch('POSTAL_EXCEPTION_URL', '') %>
|
||||
maximum_delivery_attempts: <%= ENV.fetch('POSTAL_MAXIMUM_DELIVERY_ATTEMPTS', '18') %>
|
||||
maximum_hold_expiry_days: <%= ENV.fetch('POSTAL_MAXIMUM_HOLD_EXPIRY_DAYS', '7') %>
|
||||
suppression_list_removal_delay: <%= ENV.fetch('POSTAL_SUPPRESSION_LIST_REMOVAL_DELAY', '30') %>
|
||||
use_local_ns_for_domains: <%= ENV.fetch('POSTAL_USE_LOCAL_NS_FOR_DOMAINS', 'false') %>
|
||||
default_spam_threshold: <%= ENV.fetch('POSTAL_DEFAULT_SPAM_THRESHOLD', '5.0') %>
|
||||
default_spam_failure_threshold: <%= ENV.fetch('POSTAL_DEFAULT_SPAM_FAILURE_THRESHOLD', '20.0') %>
|
||||
use_resent_sender_header: <%= ENV.fetch('POSTAL_USE_RESENT_SENDER_HEADER', 'true') %>
|
||||
|
||||
web_server:
|
||||
bind_address: 127.0.0.1
|
||||
port: 5000
|
||||
max_threads: 5
|
||||
bind_address: <%= ENV.fetch('WEB_SERVER_BIND_ADDRESS', '0.0.0.0') %>
|
||||
port: <%= ENV.fetch('WEB_SERVER_PORT', '5000') %>
|
||||
max_threads: <%= ENV.fetch('WEB_SERVER_MAX_THREADS', '5') %>
|
||||
|
||||
main_db:
|
||||
host: 127.0.0.1
|
||||
port: 3306
|
||||
username: postal
|
||||
password:
|
||||
database: postal
|
||||
pool_size: 5
|
||||
|
||||
logging:
|
||||
stdout: false
|
||||
root: # Automatically determined based on config root
|
||||
max_log_file_size: 20
|
||||
max_log_files: 10
|
||||
graylog:
|
||||
host:
|
||||
port: 12201
|
||||
host: <%= ENV.fetch('MAIN_DB_HOST', '127.0.0.1') %>
|
||||
port: <%= ENV.fetch('MAIN_DB_PORT', '3306') %>
|
||||
username: <%= ENV.fetch('MAIN_DB_USERNAME', 'postal') %>
|
||||
password: <%= ENV.fetch('MAIN_DB_PASSWORD', '') %>
|
||||
database: <%= ENV.fetch('MAIN_DB_DATABASE', 'postal') %>
|
||||
pool_size: <%= ENV.fetch('MAIN_DB_POOL_SIZE', '5') %>
|
||||
|
||||
message_db:
|
||||
host: 127.0.0.1
|
||||
port: 3306
|
||||
username: postal
|
||||
password:
|
||||
prefix: postal
|
||||
host: <%= ENV.fetch('MESSAGE_DB_HOST', '127.0.0.1') %>
|
||||
port: <%= ENV.fetch('MESSAGE_DB_PORT', '3306') %>
|
||||
username: <%= ENV.fetch('MESSAGE_DB_USERNAME', 'postal') %>
|
||||
password: <%= ENV.fetch('MESSAGE_DB_PASSWORD', '') %>
|
||||
prefix: <%= ENV.fetch('MESSAGE_DB_PREFIX', 'postal') %>
|
||||
|
||||
rabbitmq:
|
||||
host: 127.0.0.1
|
||||
port: 5672
|
||||
tls: false
|
||||
verify_peer: true
|
||||
tls_ca_certificates:
|
||||
- /etc/ssl/certs/ca-certificates.crt
|
||||
username: postal
|
||||
password:
|
||||
vhost: /postal
|
||||
host: <%= ENV.fetch('RABBITMQ_HOST', '127.0.0.1') %>
|
||||
port: <%= ENV.fetch('RABBITMQ_PORT', '5672') %>
|
||||
username: <%= ENV.fetch('RABBITMQ_USERNAME', 'postal') %>
|
||||
password: <%= ENV.fetch('RABBITMQ_PASSWORD', '') %>
|
||||
vhost: <%= ENV.fetch('RABBITMQ_VHOST', '/postal') %>
|
||||
tls: <%= ENV.fetch('RABBITMQ_TLS', 'false') %>
|
||||
verify_peer: <%= ENV.fetch('RABBITMQ_VERIFY_PEER', 'true') %>
|
||||
tls_ca_certificates: <%= ENV.fetch('RABBITMQ_TLS_CA_CERTIFICATES', '/etc/ssl/certs/ca-certificates.crt'.split(',').inspect) %>
|
||||
|
||||
logging:
|
||||
stdout: <%= ENV.fetch('LOGGING_STDOUT', 'false') %>
|
||||
root: <%= ENV.fetch('LOGGING_ROOT', '') %>
|
||||
max_log_file_size: <%= ENV.fetch('LOGGING_MAX_LOG_FILES', '20') %>
|
||||
max_log_files: <%= ENV.fetch('LOGGING_MAX_LOG_FILES', '10') %>
|
||||
graylog:
|
||||
host: <%= ENV.fetch('GRAYLOG_HOST', '') %>
|
||||
port: <%= ENV.fetch('GRAYLOG_PORT', '12201') %>
|
||||
|
||||
workers:
|
||||
quantity: 1
|
||||
threads: 4
|
||||
threads: <%= ENV.fetch('WORKER_THREADS', '4') %>
|
||||
|
||||
smtp_server:
|
||||
port: 25
|
||||
bind_address: '::'
|
||||
tls_enabled: false
|
||||
tls_certificate_path: # Defaults to config/smtp.cert
|
||||
tls_private_key_path: # Defaults to config/smtp.key
|
||||
tls_ciphers:
|
||||
ssl_version: SSLv23
|
||||
proxy_protocol: false
|
||||
log_connect: true
|
||||
strip_received_headers: false
|
||||
max_message_size: 14 # size in Megabytes
|
||||
port: <%= ENV.fetch('SMTP_SERVER_PORT', '25') %>
|
||||
bind_address: "<%= ENV.fetch('SMTP_SERVER_BIND_ADDRESS', '::') %>"
|
||||
tls_enabled: <%= ENV.fetch('SMTP_SERVER_TLS_ENABLED', 'false') %>
|
||||
tls_certificate_path: <%= ENV.fetch('SMTP_SERVER_TLS_CERTIFICATE_PATH', '') %> # Defaults to config/smtp.cert
|
||||
tls_private_key_path: <%= ENV.fetch('SMTP_SERVER_TLS_PRIVATE_KEY_PATH', '') %> # Defaults to config/smtp.key
|
||||
tls_ciphers: <%= ENV.fetch('SMTP_SERVER_TLS_CIPHERS', '') %>
|
||||
ssl_version: <%= ENV.fetch('SMTP_SERVER_SSL_VERSION', 'SSLv23') %>
|
||||
proxy_protocol: <%= ENV.fetch('SMTP_SERVER_PROXY_PROTOCOL', 'false') %>
|
||||
log_connect: <%= ENV.fetch('SMTP_SERVER_LOG_CONNECT', 'false') %>
|
||||
strip_received_headers: <%= ENV.fetch('SMTP_SERVER_STRIP_RECEIVED_HEADERS', 'false') %>
|
||||
max_message_size: <%= ENV.fetch('SMTP_SERVER_MAX_MESSAGE_SIZE', '14') %> # size in Megabytes
|
||||
|
||||
smtp_relays:
|
||||
-
|
||||
hostname:
|
||||
- hostname:
|
||||
port: 25
|
||||
ssl_mode: Auto
|
||||
|
||||
dns:
|
||||
mx_records:
|
||||
- mx.postal.example.com
|
||||
smtp_server_hostname: postal.example.com
|
||||
spf_include: spf.postal.example.com
|
||||
return_path: rp.postal.example.com
|
||||
route_domain: routes.postal.example.com
|
||||
track_domain: track.postal.example.com
|
||||
helo_hostname: # By default, this will be the same as the `smtp_server_hostname`
|
||||
dkim_identifier: postal
|
||||
domain_verify_prefix: postal-verification
|
||||
custom_return_path_prefix: psrp
|
||||
mx_records: <%= ENV.fetch('DNS_MX_RECORDS', 'mx.postal.example.com'.split(',').inspect) %>
|
||||
smtp_server_hostname: <%= ENV.fetch('DNS_SMTP_SERVER_HOSTNAME', 'postal.example.com') %>
|
||||
spf_include: <%= ENV.fetch('DNS_SPF_INCLUDE', 'spf.postal.example.com') %>
|
||||
return_path: <%= ENV.fetch('DNS_RETURN_PATH', 'rp.postal.example.com') %>
|
||||
route_domain: <%= ENV.fetch('DNS_ROUTE_DOMAIN', 'routes.postal.example.com') %>
|
||||
track_domain: <%= ENV.fetch('DNS_TRACK_DOMAIN', 'track.postal.example.com') %>
|
||||
helo_hostname: <%= ENV.fetch('DNS_HELO_HOSTNAME', '') %> # By default, this will be the same as the `smtp_server_hostname`
|
||||
dkim_identifier: <%= ENV.fetch('DNS_DKIM_IDENTIFIER', 'postal') %>
|
||||
domain_verify_prefix: <%= ENV.fetch('DNS_DOMAIN_VERIFY_PREFIX', 'postal-verification') %>
|
||||
custom_return_path_prefix: <%= ENV.fetch('DNS_CUSTOM_RETURN_PATH_PREFIX', 'psrp') %>
|
||||
|
||||
smtp:
|
||||
host: 127.0.0.1
|
||||
port: 25
|
||||
username: # Complete when Postal is running and you can
|
||||
password: # generate the credentials within the interface.
|
||||
from_name: Postal
|
||||
from_address: postal@yourdomain.com
|
||||
host: <%= ENV.fetch('SMTP_HOST', '127.0.0.1') %>
|
||||
port: <%= ENV.fetch('SMTP_PORT', '25') %>
|
||||
username: <%= ENV.fetch('SMTP_USERNAME', '') %> # Complete when Postal is running and you can
|
||||
password: <%= ENV.fetch('SMTP_PASSWORD', '') %> # generate the credentials within the interface.
|
||||
from_name: <%= ENV.fetch('SMTP_FROM_NAME', 'Postal') %>
|
||||
from_address: <%= ENV.fetch('SMTP_FROM_ADDRESS', 'postal@example.com') %>
|
||||
|
||||
rails:
|
||||
environment: production
|
||||
secret_key:
|
||||
environment: <%= ENV.fetch('RAILS_ENV', 'production') %>
|
||||
secret_key: <%= ENV.fetch('RAILS_SECRET_KEY_BASE', '') %>
|
||||
|
||||
rspamd:
|
||||
enabled: false
|
||||
host: 127.0.0.1
|
||||
port: 11334
|
||||
ssl: false
|
||||
password: null
|
||||
flags: null
|
||||
enabled: <%= ENV.fetch('RSPAMD_ENABLED', 'false') %>
|
||||
host: <%= ENV.fetch('RSPAMD_HOST', '127.0.0.1') %>
|
||||
port: <%= ENV.fetch('RSPAMD_PORT', '11334') %>
|
||||
ssl: <%= ENV.fetch('RSPAMD_SSL', 'false') %>
|
||||
password: <%= ENV.fetch('RSPAMD_PASSWORD', '') %>
|
||||
flags: <%= ENV.fetch('RSPAMD_FLAGS', '') %>
|
||||
|
||||
spamd:
|
||||
enabled: false
|
||||
host: 127.0.0.1
|
||||
port: 783
|
||||
enabled: <%= ENV.fetch('SPAMD_ENABLED', 'false') %>
|
||||
host: <%= ENV.fetch('SPAMD_HOST', '127.0.0.1') %>
|
||||
port: <%= ENV.fetch('SPAMD_PORT', '783') %>
|
||||
|
||||
clamav:
|
||||
enabled: false
|
||||
host: 127.0.0.1
|
||||
port: 2000
|
||||
enabled: <%= ENV.fetch('CLAMAV_ENABLED', 'false') %>
|
||||
host: <%= ENV.fetch('CLAMAV_HOST', '127.0.0.1') %>
|
||||
port: <%= ENV.fetch('CLAMAV_PORT', '2000') %>
|
||||
|
||||
smtp_client:
|
||||
open_timeout: 30
|
||||
read_timeout: 60
|
||||
open_timeout: <%= ENV.fetch('SMTP_CLIENT_OPEN_TIMEOUT', '30') %>
|
||||
read_timeout: <%= ENV.fetch('SMTP_CLIENT_READ_TIMEOUT', '60') %>
|
||||
|
||||
@@ -1,45 +1,2 @@
|
||||
module Postal
|
||||
|
||||
extend ActiveSupport::Autoload
|
||||
|
||||
eager_autoload do
|
||||
autoload :AppLogger
|
||||
autoload :BounceMessage
|
||||
autoload :Config
|
||||
autoload :Countries
|
||||
autoload :DKIMHeader
|
||||
autoload :Error
|
||||
autoload :Helpers
|
||||
autoload :HTTP
|
||||
autoload :HTTPSender
|
||||
autoload :Job
|
||||
autoload :MessageDB
|
||||
autoload :MessageInspection
|
||||
autoload :MessageInspector
|
||||
autoload :MessageInspectors
|
||||
autoload :MessageParser
|
||||
autoload :MessageRequeuer
|
||||
autoload :MXLookup
|
||||
autoload :QueryString
|
||||
autoload :RabbitMQ
|
||||
autoload :ReplySeparator
|
||||
autoload :RspecHelpers
|
||||
autoload :Sender
|
||||
autoload :SendResult
|
||||
autoload :SMTPSender
|
||||
autoload :SMTPServer
|
||||
autoload :SpamCheck
|
||||
autoload :TrackingMiddleware
|
||||
autoload :UserCreator
|
||||
autoload :Version
|
||||
autoload :Worker
|
||||
end
|
||||
|
||||
def self.eager_load!
|
||||
super
|
||||
Postal::MessageDB.eager_load!
|
||||
Postal::SMTPServer.eager_load!
|
||||
Postal::MessageInspectors.eager_load!
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
require "erb"
|
||||
require "yaml"
|
||||
require "pathname"
|
||||
require "cgi"
|
||||
@@ -78,7 +79,11 @@ module Postal
|
||||
end
|
||||
|
||||
def self.defaults
|
||||
@defaults ||= YAML.load_file(defaults_file_path)
|
||||
@defaults ||= begin
|
||||
file = File.read(defaults_file_path)
|
||||
yaml = ERB.new(file).result
|
||||
YAML.safe_load(yaml)
|
||||
end
|
||||
end
|
||||
|
||||
def self.database_url
|
||||
@@ -152,7 +157,7 @@ module Postal
|
||||
end
|
||||
|
||||
def self.signing_key_path
|
||||
config_root.join("signing.key")
|
||||
ENV.fetch("POSTAL_SIGNING_KEY_PATH") { config_root.join("signing.key") }
|
||||
end
|
||||
|
||||
def self.signing_key
|
||||
|
||||
@@ -5,7 +5,7 @@ module Postal
|
||||
|
||||
def initialize(id, params = {})
|
||||
@id = id
|
||||
@params = params.with_indifferent_access
|
||||
@params = params
|
||||
on_initialize
|
||||
end
|
||||
|
||||
@@ -38,9 +38,5 @@ module Postal
|
||||
job_id
|
||||
end
|
||||
|
||||
def self.perform(params = {})
|
||||
new(nil, params).perform
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,20 +1,4 @@
|
||||
module Postal
|
||||
module MessageDB
|
||||
|
||||
extend ActiveSupport::Autoload
|
||||
eager_autoload do
|
||||
autoload :Click
|
||||
autoload :Database
|
||||
autoload :Delivery
|
||||
autoload :LiveStats
|
||||
autoload :Load
|
||||
autoload :Message
|
||||
autoload :Migration
|
||||
autoload :Provisioner
|
||||
autoload :Statistics
|
||||
autoload :SuppressionList
|
||||
autoload :Webhooks
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -107,7 +107,7 @@ module Postal
|
||||
#
|
||||
# Insert a new raw message into a table (creating it if needed)
|
||||
#
|
||||
def insert_raw_message(data, date = Date.today)
|
||||
def insert_raw_message(data, date = Time.now.utc.to_date)
|
||||
table_name = raw_table_name_for_date(date)
|
||||
begin
|
||||
headers, body = data.split(/\r?\n\r?\n/, 2)
|
||||
|
||||
@@ -263,7 +263,7 @@ module Postal
|
||||
return unless @pending_raw_message
|
||||
|
||||
self.size = @pending_raw_message.bytesize
|
||||
date = Date.today
|
||||
date = Time.now.utc.to_date
|
||||
table_name, headers_id, body_id = @database.insert_raw_message(@pending_raw_message, date)
|
||||
self.raw_table = table_name
|
||||
self.raw_headers_id = headers_id
|
||||
|
||||
@@ -94,7 +94,7 @@ module Postal
|
||||
# Return a list of raw message tables that are older than the given date
|
||||
#
|
||||
def raw_tables(max_age = 30)
|
||||
earliest_date = max_age ? Date.today - max_age : nil
|
||||
earliest_date = max_age ? Time.now.utc.to_date - max_age : nil
|
||||
[].tap do |tables|
|
||||
@database.query("SHOW TABLES FROM `#{@database.database_name}` LIKE 'raw-%'").each do |tbl|
|
||||
tbl_name = tbl.to_a.first.last
|
||||
@@ -128,7 +128,7 @@ module Postal
|
||||
# Remove messages from the messages table that are too old to retain
|
||||
#
|
||||
def remove_messages(max_age = 60)
|
||||
time = (Date.today - max_age.days).to_time.end_of_day
|
||||
time = (Time.now.utc.to_date - max_age.days).to_time.end_of_day
|
||||
return unless newest_message_to_remove = @database.select(:messages, where: { timestamp: { less_than_or_equal_to: time.to_f } }, limit: 1, order: :id, direction: "DESC", fields: [:id]).first
|
||||
|
||||
id = newest_message_to_remove["id"]
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
module Postal
|
||||
module MessageInspectors
|
||||
|
||||
extend ActiveSupport::Autoload
|
||||
eager_autoload do
|
||||
autoload :Clamav
|
||||
autoload :Rspamd
|
||||
autoload :SpamAssassin
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
module Postal
|
||||
module SMTPServer
|
||||
|
||||
extend ActiveSupport::Autoload
|
||||
eager_autoload do
|
||||
autoload :Client
|
||||
autoload :Server
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,7 +8,57 @@
|
||||
"bump-patch-for-minor-pre-major": true,
|
||||
"draft": false,
|
||||
"prerelease": false,
|
||||
"include-v-in-tag": false
|
||||
"include-v-in-tag": false,
|
||||
"changelog-sections": [
|
||||
{
|
||||
"type": "feat",
|
||||
"section": "Features"
|
||||
},
|
||||
{
|
||||
"type": "feature",
|
||||
"section": "Features"
|
||||
},
|
||||
{
|
||||
"type": "fix",
|
||||
"section": "Bug Fixes"
|
||||
},
|
||||
{
|
||||
"type": "perf",
|
||||
"section": "Performance Improvements"
|
||||
},
|
||||
{
|
||||
"type": "revert",
|
||||
"section": "Reverts"
|
||||
},
|
||||
{
|
||||
"type": "docs",
|
||||
"section": "Documentation"
|
||||
},
|
||||
{
|
||||
"type": "style",
|
||||
"section": "Styles"
|
||||
},
|
||||
{
|
||||
"type": "chore",
|
||||
"section": "Miscellaneous Chores"
|
||||
},
|
||||
{
|
||||
"type": "refactor",
|
||||
"section": "Code Refactoring"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"section": "Tests"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"section": "Build System"
|
||||
},
|
||||
{
|
||||
"type": "ci",
|
||||
"section": "Continuous Integration"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
|
||||
|
||||
50
script/send-html-email.rb
Normal file
50
script/send-html-email.rb
Normal file
@@ -0,0 +1,50 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# This script will automatically send an HTML email to the
|
||||
# SMTP server given.
|
||||
|
||||
require 'mail'
|
||||
require 'net/smtp'
|
||||
|
||||
from = ARGV[0]
|
||||
to = ARGV[1]
|
||||
|
||||
if from.nil? || to.nil?
|
||||
puts "Usage: ruby send-html-email.rb <from> <to>"
|
||||
exit 1
|
||||
end
|
||||
|
||||
|
||||
mail = Mail.new
|
||||
mail.to = to
|
||||
mail.from = from
|
||||
mail.subject = "A test email from #{Time.now.to_s}"
|
||||
mail['X-Postal-Tag'] = 'send-html-email-script'
|
||||
mail.text_part = Mail::Part.new do
|
||||
body <<~BODY
|
||||
Hello there.
|
||||
|
||||
This is an example. It doesn't do all that much.
|
||||
|
||||
Some other characters: őúéáűí
|
||||
|
||||
There is a link here through... https://postalserver.io/test-plain-text-link?foo=bar&baz=qux
|
||||
BODY
|
||||
end
|
||||
mail.html_part = Mail::Part.new do
|
||||
content_type 'text/html; charset=UTF-8'
|
||||
body <<~BODY
|
||||
<p>Hello there</p>
|
||||
<p>This is an example email. It doesn't do all that much.</p>
|
||||
<p>Some other characters: őúéáűí</p>
|
||||
<p>There is a <a href='https://postalserver.io/test-plain-text-link?foo=bar&baz=qux'>link here</a> though...</p>
|
||||
BODY
|
||||
end
|
||||
|
||||
#puts mail.to_s
|
||||
|
||||
Net::SMTP.start('127.0.0.1', 2525) do |smtp|
|
||||
smtp.send_message mail.to_s, mail.from.first, mail.to.first
|
||||
end
|
||||
|
||||
puts "Sent"
|
||||
المرجع في مشكلة جديدة
حظر مستخدم