mirror of
https://github.com/postalserver/postal.git
synced 2026-10-19 10:46:32 +00:00
Merge pull request #2797 from postalserver/rubocop
Code Quality Improvements
This commit is contained in:
+151
-12
@@ -1,35 +1,174 @@
|
||||
inherit_from:
|
||||
- https://dev.k.io/rubocop/rubocop.rails.yml
|
||||
AllCops:
|
||||
TargetRubyVersion: 3.0
|
||||
NewCops: enable
|
||||
Exclude:
|
||||
- "bin/*"
|
||||
- "db/schema.rb"
|
||||
# Fixes missing gem exception when running Rubocop on GitHub Actions.
|
||||
- "vendor/bundle/**/*"
|
||||
|
||||
# Always use double quotes
|
||||
Style/StringLiterals:
|
||||
EnforcedStyle: double_quotes
|
||||
|
||||
Style/WordArray:
|
||||
# We prefer arrays of symbols to look like an array of symbols.
|
||||
# For example: [:one, :two, :three] as opposed to %i[one two three]
|
||||
Style/SymbolArray:
|
||||
EnforcedStyle: brackets
|
||||
|
||||
Layout/LineLength:
|
||||
# There should always be empty lines inside a class. For example
|
||||
#
|
||||
# class MyExample
|
||||
#
|
||||
# def some_method
|
||||
# end
|
||||
#
|
||||
# end
|
||||
Layout/EmptyLinesAroundClassBody:
|
||||
EnforcedStyle: empty_lines
|
||||
|
||||
# We want to keep attr_* definitions separated on their own lines, rather than
|
||||
# all of them collapsed into a single attr_* call. The collapsed/grouped variant
|
||||
# is harder to read, and harder to see what's been changed in diffs.
|
||||
Style/AccessorGrouping:
|
||||
Enabled: false
|
||||
|
||||
# Blocks are slightly different to classes, in these cases there should
|
||||
# not be new lines around the contents of the block.
|
||||
#
|
||||
# proc do
|
||||
# # Do something
|
||||
# end
|
||||
Layout/EmptyLinesAroundBlockBody:
|
||||
EnforcedStyle: no_empty_lines
|
||||
|
||||
# Modules are the same as classes unless they're being used for namespacing
|
||||
# purposes in which case there should not be new lines.
|
||||
Layout/EmptyLinesAroundModuleBody:
|
||||
EnforcedStyle: empty_lines_except_namespace
|
||||
|
||||
# Space is required following -> when writing a lambda:
|
||||
#
|
||||
# somethign = -> (var) { block }
|
||||
Layout/SpaceInLambdaLiteral:
|
||||
EnforcedStyle: require_space
|
||||
|
||||
Layout/FirstHashElementIndentation:
|
||||
Enabled: false
|
||||
|
||||
# We don't mind setting assignments in conditions so this has been disabled to
|
||||
# allow `if something = something_else` without worrying about brackets.
|
||||
Lint/AssignmentInCondition:
|
||||
Enabled: false
|
||||
|
||||
# Top level documentation is quite rare...
|
||||
Style/Documentation:
|
||||
Enabled: false
|
||||
|
||||
# We want to allow inner slashes in a regexp to be used when using /xxx/ form.
|
||||
Style/RegexpLiteral:
|
||||
AllowInnerSlashes: true
|
||||
|
||||
# Blocks of if statements are perfectly fine and usually more readable than
|
||||
# putting everything onto a single line just because we can.
|
||||
Style/IfUnlessModifier:
|
||||
Enabled: false
|
||||
|
||||
# We prefer assignments to happen within the condition rather than setting a
|
||||
# variable to the result of a condition.
|
||||
Style/ConditionalAssignment:
|
||||
EnforcedStyle: assign_inside_condition
|
||||
IncludeTernaryExpressions: false
|
||||
|
||||
# Empty methods should not be compacted onto a single line
|
||||
Style/EmptyMethod:
|
||||
EnforcedStyle: expanded
|
||||
|
||||
# As above, just flag them.
|
||||
Lint/UnusedBlockArgument:
|
||||
AutoCorrect: false
|
||||
|
||||
# While we don't want to make heavy use of get_ or set_ methods we do often need
|
||||
# to use these when we want to refer to actually getting or setting something
|
||||
# (usually from another API).
|
||||
Naming/AccessorMethodName:
|
||||
Enabled: false
|
||||
|
||||
# If we want a boolean called :true, we should be allowed that. These are likely
|
||||
# not mistakes.
|
||||
Lint/BooleanSymbol:
|
||||
Enabled: false
|
||||
|
||||
# Using block.map(&:upcase) is not always the neatest way to show something. For
|
||||
# example if you have a block that just calls one thing, you don't want it
|
||||
# collapsed.
|
||||
#
|
||||
# action do |user|
|
||||
# user.delete
|
||||
# end
|
||||
#
|
||||
# This should be action(&:delete) because it is not clear what is actually
|
||||
# happening without the context of knowing what the inner variable should be
|
||||
# called.
|
||||
Style/SymbolProc:
|
||||
Enabled: false
|
||||
|
||||
# Allow a maxmium of 5 arguments and don't include keyword arguments
|
||||
Metrics/ParameterLists:
|
||||
Max: 5
|
||||
CountKeywordArgs: false
|
||||
|
||||
# This cop checks for chaining of a block after another block that spans multiple lines.
|
||||
Style/MultilineBlockChain:
|
||||
Exclude:
|
||||
- "spec/**/*.rb"
|
||||
|
||||
Metrics/AbcSize:
|
||||
Enabled: false
|
||||
|
||||
Style/FrozenStringLiteralComment:
|
||||
Enabled: true
|
||||
SafeAutoCorrect: true
|
||||
|
||||
Naming/PredicateName:
|
||||
Enabled: false
|
||||
|
||||
Layout/LineLength:
|
||||
# We want to reduce this back down to 120 but there are a fair number of offences
|
||||
# of this which need addressing individually and carefully.
|
||||
Max: 200
|
||||
|
||||
Metrics/PerceivedComplexity:
|
||||
# As above, we want to enable this again in the future, but for now we'll just
|
||||
# disable it entirely.
|
||||
Enabled: false
|
||||
|
||||
Metrics/CyclomaticComplexity:
|
||||
# As above.
|
||||
Enabled: false
|
||||
|
||||
Metrics/MethodLength:
|
||||
# As above.
|
||||
Enabled: false
|
||||
|
||||
Metrics/BlockNesting:
|
||||
# As above.
|
||||
Enabled: false
|
||||
|
||||
Style/StringConcatenation:
|
||||
Enabled: false
|
||||
|
||||
Metrics/BlockLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/ClassLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/CyclomaticComplexity:
|
||||
Enabled: false
|
||||
|
||||
Metrics/MethodLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/ModuleLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/PerceivedComplexity:
|
||||
Lint/UnusedMethodArgument:
|
||||
Enabled: false
|
||||
|
||||
Lint/MissingSuper:
|
||||
Style/SpecialGlobalVars:
|
||||
Enabled: false
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source "https://rubygems.org"
|
||||
gem "authie"
|
||||
gem "autoprefixer-rails"
|
||||
@@ -10,12 +12,12 @@ gem "clockwork"
|
||||
gem "dotenv-rails"
|
||||
gem "dynamic_form"
|
||||
gem "encrypto_signo"
|
||||
gem "execjs", "~> 2.7", "< 2.8"
|
||||
gem "foreman"
|
||||
gem "gelf"
|
||||
gem "haml"
|
||||
gem "hashie"
|
||||
gem "highline", require: false
|
||||
gem "execjs", '~> 2.7', "< 2.8"
|
||||
gem "jwt"
|
||||
gem "kaminari"
|
||||
gem "mail"
|
||||
@@ -28,8 +30,8 @@ gem "puma"
|
||||
gem "rails", "= 6.1.7.6"
|
||||
gem "resolv", "~> 0.2.1"
|
||||
gem "secure_headers"
|
||||
gem 'sentry-rails'
|
||||
gem 'sentry-ruby'
|
||||
gem "sentry-rails"
|
||||
gem "sentry-ruby"
|
||||
gem "turbolinks", "~> 5"
|
||||
|
||||
group :development, :assets do
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
authenticator :server do
|
||||
friendly_name "Server Authenticator"
|
||||
header "X-Server-API-Key", "The API token for a server that you wish to authenticate with.", example: "f29a45f0d4e1744ebaee"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
controller :messages do
|
||||
friendly_name "Messages API"
|
||||
description "This API allows you to access message details"
|
||||
@@ -12,7 +14,7 @@ controller :messages do
|
||||
action do
|
||||
begin
|
||||
message = identity.server.message(params.id)
|
||||
rescue Postal::MessageDB::Message::NotFound => e
|
||||
rescue Postal::MessageDB::Message::NotFound
|
||||
error "MessageNotFound", id: params.id
|
||||
end
|
||||
structure :message, message, return: true
|
||||
@@ -28,7 +30,7 @@ controller :messages do
|
||||
action do
|
||||
begin
|
||||
message = identity.server.message(params.id)
|
||||
rescue Postal::MessageDB::Message::NotFound => e
|
||||
rescue Postal::MessageDB::Message::NotFound
|
||||
error "MessageNotFound", id: params.id
|
||||
end
|
||||
message.deliveries.map do |d|
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
controller :send do
|
||||
friendly_name "Send API"
|
||||
description "This API allows you to send messages"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
structure :delivery do
|
||||
basic :id
|
||||
basic :status
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
structure :message do
|
||||
basic :id
|
||||
basic :token
|
||||
@@ -5,9 +7,9 @@ structure :message do
|
||||
expansion(:status) do
|
||||
{
|
||||
status: o.status,
|
||||
last_delivery_attempt: o.last_delivery_attempt ? o.last_delivery_attempt.to_f : nil,
|
||||
last_delivery_attempt: o.last_delivery_attempt&.to_f,
|
||||
held: o.held,
|
||||
hold_expiry: o.hold_expiry ? o.hold_expiry.to_f : nil
|
||||
hold_expiry: o.hold_expiry&.to_f
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AddressEndpointsController < ApplicationController
|
||||
|
||||
include WithinOrganization
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "authie/session"
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
@@ -46,11 +48,7 @@ class ApplicationController < ActionController::Base
|
||||
helper_method :page_title
|
||||
|
||||
def redirect_to_with_return_to(url, *args)
|
||||
if params[:return_to].blank? || !params[:return_to].starts_with?("/")
|
||||
redirect_to url_with_return_to(url), *args
|
||||
else
|
||||
redirect_to url_with_return_to(url), *args
|
||||
end
|
||||
redirect_to url_with_return_to(url), *args
|
||||
end
|
||||
|
||||
def set_timezone
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module WithinOrganization
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CredentialsController < ApplicationController
|
||||
|
||||
include WithinOrganization
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class DomainsController < ApplicationController
|
||||
|
||||
include WithinOrganization
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class HelpController < ApplicationController
|
||||
|
||||
include WithinOrganization
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class HTTPEndpointsController < ApplicationController
|
||||
|
||||
include WithinOrganization
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class IPAddressesController < ApplicationController
|
||||
|
||||
before_action :admin_required
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class IPPoolRulesController < ApplicationController
|
||||
|
||||
include WithinOrganization
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class IPPoolsController < ApplicationController
|
||||
|
||||
before_action :admin_required
|
||||
@@ -31,7 +33,7 @@ class IPPoolsController < ApplicationController
|
||||
def destroy
|
||||
@ip_pool.destroy
|
||||
redirect_to_with_json :ip_pools, notice: "IP pool has been removed successfully."
|
||||
rescue ActiveRecord::DeleteRestrictionError => e
|
||||
rescue ActiveRecord::DeleteRestrictionError
|
||||
redirect_to_with_json [:edit, @ip_pool], alert: "IP pool cannot be removed because it still has associated addresses or servers."
|
||||
end
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class MessagesController < ApplicationController
|
||||
|
||||
include WithinOrganization
|
||||
@@ -186,7 +188,7 @@ class MessagesController < ApplicationController
|
||||
if qs[:before]
|
||||
begin
|
||||
options[:where][:timestamp][:less_than] = get_time_from_string(qs[:before]).to_f
|
||||
rescue TimeUndetermined => e
|
||||
rescue TimeUndetermined
|
||||
flash.now[:alert] = "Couldn't determine time for before from '#{qs[:before]}'"
|
||||
end
|
||||
end
|
||||
@@ -194,7 +196,7 @@ class MessagesController < ApplicationController
|
||||
if qs[:after]
|
||||
begin
|
||||
options[:where][:timestamp][:greater_than] = get_time_from_string(qs[:after]).to_f
|
||||
rescue TimeUndetermined => e
|
||||
rescue TimeUndetermined
|
||||
flash.now[:alert] = "Couldn't determine time for after from '#{qs[:after]}'"
|
||||
end
|
||||
end
|
||||
@@ -220,6 +222,7 @@ class MessagesController < ApplicationController
|
||||
time = Chronic.parse(string, context: :past)
|
||||
end
|
||||
rescue StandardError
|
||||
time = nil
|
||||
end
|
||||
|
||||
raise TimeUndetermined, "Couldn't determine a suitable time from '#{string}'" if time.nil?
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class OrganizationIPPoolsController < ApplicationController
|
||||
|
||||
include WithinOrganization
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class OrganizationsController < ApplicationController
|
||||
|
||||
before_action :admin_required, only: [:new, :create, :delete, :destroy]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class RoutesController < ApplicationController
|
||||
|
||||
include WithinOrganization
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ServersController < ApplicationController
|
||||
|
||||
include WithinOrganization
|
||||
@@ -41,7 +43,12 @@ class ServersController < ApplicationController
|
||||
|
||||
def update
|
||||
extra_params = [:spam_threshold, :spam_failure_threshold, :postmaster_address]
|
||||
extra_params += [:send_limit, :allow_sender, :privacy_mode, :log_smtp_data, :outbound_spam_threshold, :message_retention_days, :raw_message_retention_days, :raw_message_retention_size] if current_user.admin?
|
||||
|
||||
if current_user.admin?
|
||||
extra_params += [:send_limit, :allow_sender, :privacy_mode, :log_smtp_data, :outbound_spam_threshold,
|
||||
:message_retention_days, :raw_message_retention_days, :raw_message_retention_size]
|
||||
end
|
||||
|
||||
if @server.update(safe_params(*extra_params))
|
||||
redirect_to_with_json organization_server_path(organization, @server), notice: "Server settings have been updated"
|
||||
else
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SessionsController < ApplicationController
|
||||
|
||||
layout "sub"
|
||||
@@ -8,7 +10,7 @@ class SessionsController < ApplicationController
|
||||
login(User.authenticate(params[:email_address], params[:password]))
|
||||
flash[:remember_login] = true
|
||||
redirect_to_with_return_to root_path
|
||||
rescue Postal::Errors::AuthenticationError => e
|
||||
rescue Postal::Errors::AuthenticationError
|
||||
flash.now[:alert] = "The credentials you've provided are incorrect. Please check and try again."
|
||||
render "new"
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SMTPEndpointsController < ApplicationController
|
||||
|
||||
include WithinOrganization
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TrackDomainsController < ApplicationController
|
||||
|
||||
include WithinOrganization
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class UserController < ApplicationController
|
||||
|
||||
skip_before_action :login_required, only: [:new, :create, :join]
|
||||
@@ -69,7 +71,9 @@ class UserController < ApplicationController
|
||||
|
||||
if @user.save
|
||||
if email_changed
|
||||
redirect_to_with_json verify_path(return_to: settings_path), notice: "Your settings have been updated successfully. As you've changed, your e-mail address you'll need to verify it before you can continue."
|
||||
redirect_to_with_json verify_path(return_to: settings_path),
|
||||
notice: "Your settings have been updated successfully. As you've changed, your e-mail " \
|
||||
"address you'll need to verify it before you can continue."
|
||||
else
|
||||
redirect_to_with_json settings_path, notice: "Your settings have been updated successfully."
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class UsersController < ApplicationController
|
||||
|
||||
before_action :admin_required
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class WebhooksController < ApplicationController
|
||||
|
||||
include WithinOrganization
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ApplicationHelper
|
||||
|
||||
def format_delivery_details(server, text)
|
||||
@@ -28,7 +30,7 @@ module ApplicationHelper
|
||||
server_domains = server.domains.verified.order(:name)
|
||||
unless server_domains.empty?
|
||||
s << "<optgroup label='Server Domains'>"
|
||||
for domain in server_domains
|
||||
server_domains.each do |domain|
|
||||
selected = domain == selected_domain ? "selected='selected'" : ""
|
||||
s << "<option value='#{domain.id}' #{selected}>#{domain.name}</option>"
|
||||
end
|
||||
@@ -38,7 +40,7 @@ module ApplicationHelper
|
||||
organization_domains = server.organization.domains.verified.order(:name)
|
||||
unless organization_domains.empty?
|
||||
s << "<optgroup label='Organization Domains'>"
|
||||
for domain in organization_domains
|
||||
organization_domains.each do |domain|
|
||||
selected = domain == selected_domain ? "selected='selected'" : ""
|
||||
s << "<option value='#{domain.id}' #{selected}>#{domain.name}</option>"
|
||||
end
|
||||
@@ -54,7 +56,7 @@ module ApplicationHelper
|
||||
http_endpoints = server.http_endpoints.order(:name).to_a
|
||||
if http_endpoints.present?
|
||||
s << "<optgroup label='HTTP Endpoints'>"
|
||||
for endpoint in http_endpoints
|
||||
http_endpoints.each do |endpoint|
|
||||
value = "#{endpoint.class}##{endpoint.uuid}"
|
||||
selected = value == selected_value ? "selected='selected'" : ""
|
||||
s << "<option value='#{value}' #{selected}>#{endpoint.description}</option>"
|
||||
@@ -65,7 +67,7 @@ module ApplicationHelper
|
||||
smtp_endpoints = server.smtp_endpoints.order(:name).to_a
|
||||
if smtp_endpoints.present?
|
||||
s << "<optgroup label='SMTP Endpoints'>"
|
||||
for endpoint in smtp_endpoints
|
||||
smtp_endpoints.each do |endpoint|
|
||||
value = "#{endpoint.class}##{endpoint.uuid}"
|
||||
selected = value == selected_value ? "selected='selected'" : ""
|
||||
s << "<option value='#{value}' #{selected}>#{endpoint.description}</option>"
|
||||
@@ -76,7 +78,7 @@ module ApplicationHelper
|
||||
address_endpoints = server.address_endpoints.order(:address).to_a
|
||||
if address_endpoints.present?
|
||||
s << "<optgroup label='Address Endpoints'>"
|
||||
for endpoint in address_endpoints
|
||||
address_endpoints.each do |endpoint|
|
||||
value = "#{endpoint.class}##{endpoint.uuid}"
|
||||
selected = value == selected_value ? "selected='selected'" : ""
|
||||
s << "<option value='#{value}' #{selected}>#{endpoint.address}</option>"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActionDeletionJob < Postal::Job
|
||||
|
||||
def perform
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActionDeletionsJob < Postal::Job
|
||||
|
||||
def perform
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CheckAllDNSJob < Postal::Job
|
||||
|
||||
def perform
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "authie/session"
|
||||
|
||||
class CleanupAuthieSessionsJob < Postal::Job
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ExpireHeldMessagesJob < Postal::Job
|
||||
|
||||
def perform
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ProcessMessageRetentionJob < Postal::Job
|
||||
|
||||
def perform
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class PruneSuppressionListsJob < Postal::Job
|
||||
|
||||
def perform
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class PruneWebhookRequestsJob < Postal::Job
|
||||
|
||||
def perform
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class RequeueWebhooksJob < Postal::Job
|
||||
|
||||
def perform
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SendNotificationsJob < Postal::Job
|
||||
|
||||
def perform
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SendWebhookJob < Postal::Job
|
||||
|
||||
def perform
|
||||
if server = Server.find(params["server_id"])
|
||||
new_items = {}
|
||||
if params["payload"]
|
||||
for key, value in params["payload"]
|
||||
next unless key.to_s =~ /\A_(\w+)/
|
||||
params["payload"]&.each do |key, value|
|
||||
next unless key.to_s =~ /\A_(\w+)/
|
||||
|
||||
begin
|
||||
new_items[::Regexp.last_match(1)] = server.message_db.message(value.to_i).webhook_hash
|
||||
rescue Postal::MessageDB::Message::NotFound
|
||||
end
|
||||
begin
|
||||
new_items[::Regexp.last_match(1)] = server.message_db.message(value.to_i).webhook_hash
|
||||
rescue Postal::MessageDB::Message::NotFound
|
||||
# No message found, don't do any replacement
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SleepJob < Postal::Job
|
||||
|
||||
def perform
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TidyRawMessagesJob < Postal::Job
|
||||
|
||||
def perform
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class UnqueueMessageJob < Postal::Job
|
||||
|
||||
# rubocop:disable Layout/LineLength
|
||||
def perform
|
||||
if original_message = QueuedMessage.find_by_id(params["id"])
|
||||
if original_message.acquire_lock
|
||||
@@ -91,11 +94,11 @@ class UnqueueMessageJob < Postal::Job
|
||||
log "#{log_prefix} Message is a bounce"
|
||||
original_messages = queued_message.message.original_messages
|
||||
unless original_messages.empty?
|
||||
for original_message in queued_message.message.original_messages
|
||||
queued_message.message.update(bounce_for_id: original_message.id, domain_id: original_message.domain_id)
|
||||
queued_message.message.create_delivery("Processed", details: "This has been detected as a bounce message for <msg:#{original_message.id}>.")
|
||||
original_message.bounce!(queued_message.message)
|
||||
log "#{log_prefix} Bounce linked with message #{original_message.id}"
|
||||
queued_message.message.original_messages.each do |orig_msg|
|
||||
queued_message.message.update(bounce_for_id: orig_msg.id, domain_id: orig_msg.domain_id)
|
||||
queued_message.message.create_delivery("Processed", details: "This has been detected as a bounce message for <msg:#{orig_msg.id}>.")
|
||||
orig_msg.bounce!(queued_message.message)
|
||||
log "#{log_prefix} Bounce linked with message #{orig_msg.id}"
|
||||
end
|
||||
queued_message.destroy
|
||||
next
|
||||
@@ -421,15 +424,17 @@ class UnqueueMessageJob < Postal::Job
|
||||
end
|
||||
rescue StandardError => e
|
||||
log "#{log_prefix} Internal error: #{e.class}: #{e.message}"
|
||||
e.backtrace.each { |e| log("#{log_prefix} #{e}") }
|
||||
e.backtrace.each { |line| log("#{log_prefix} #{line}") }
|
||||
queued_message.retry_later
|
||||
log "#{log_prefix} Queued message was unlocked"
|
||||
if defined?(Sentry)
|
||||
Sentry.capture_exception(e, extra: { job_id: self.id, server_id: queued_message.server_id, message_id: queued_message.message_id })
|
||||
end
|
||||
if queued_message.message
|
||||
queued_message.message.create_delivery("Error", details: "An internal error occurred while sending this message. This message will be retried automatically. If this persists, contact support for assistance.", output: "#{e.class}: #{e.message}", log_id: "J-#{self.id}")
|
||||
end
|
||||
queued_message.message&.create_delivery("Error",
|
||||
details: "An internal error occurred while sending " \
|
||||
"this message. This message will be retried " \
|
||||
"automatically.",
|
||||
output: "#{e.class}: #{e.message}", log_id: "J-#{self.id}")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -446,9 +451,11 @@ class UnqueueMessageJob < Postal::Job
|
||||
nil
|
||||
end
|
||||
end
|
||||
# rubocop:enable Layout/LineLength
|
||||
|
||||
private
|
||||
|
||||
# rubocop:disable Naming/MemoizedInstanceVariableName
|
||||
def cached_sender(klass, *args)
|
||||
@sender ||= begin
|
||||
sender = klass.new(*args)
|
||||
@@ -456,5 +463,6 @@ class UnqueueMessageJob < Postal::Job
|
||||
sender
|
||||
end
|
||||
end
|
||||
# rubocop:enable Naming/MemoizedInstanceVariableName
|
||||
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class WebhookDeliveryJob < Postal::Job
|
||||
|
||||
def perform
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AppMailer < ApplicationMailer
|
||||
|
||||
def verify_email_address(user)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ApplicationMailer < ActionMailer::Base
|
||||
|
||||
default from: "#{Postal.smtp_from_name} <#{Postal.smtp_from_address}>"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: additional_route_endpoints
|
||||
@@ -35,9 +37,7 @@ class AdditionalRouteEndpoint < ApplicationRecord
|
||||
end
|
||||
|
||||
def _endpoint=(value)
|
||||
if value.blank?
|
||||
self.endpoint = nil
|
||||
elsif value =~ /\#/
|
||||
if value && value =~ /\#/
|
||||
class_name, id = value.split("#", 2)
|
||||
unless Route::ENDPOINT_TYPES.include?(class_name)
|
||||
raise Postal::Error, "Invalid endpoint class name '#{class_name}'"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: address_endpoints
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ApplicationRecord < ActiveRecord::Base
|
||||
|
||||
self.abstract_class = true
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module HasAuthentication
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
@@ -42,18 +44,4 @@ module HasAuthentication
|
||||
|
||||
end
|
||||
|
||||
class Postal::Errors::AuthenticationError < Postal::Error
|
||||
|
||||
attr_reader :error
|
||||
|
||||
def initialize(error)
|
||||
@error = error
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Authentication Failed: #{@error}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# -*- SkipSchemaAnnotations
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "resolv"
|
||||
|
||||
module HasDNSChecks
|
||||
|
||||
def dns_ok?
|
||||
spf_status == "OK" && dkim_status == "OK" && ["OK", "Missing"].include?(mx_status) && ["OK", "Missing"].include?(return_path_status)
|
||||
spf_status == "OK" && dkim_status == "OK" && %w[OK Missing].include?(mx_status) && %w[OK Missing].include?(return_path_status)
|
||||
end
|
||||
|
||||
def dns_checked?
|
||||
@@ -42,12 +44,12 @@ module HasDNSChecks
|
||||
|
||||
def check_spf_record
|
||||
result = resolver.getresources(name, Resolv::DNS::Resource::IN::TXT)
|
||||
spf_records = result.map(&:data).select { |d| d =~ /\Av=spf1/ }
|
||||
spf_records = result.map(&:data).grep(/\Av=spf1/)
|
||||
if spf_records.empty?
|
||||
self.spf_status = "Missing"
|
||||
self.spf_error = "No SPF record exists for this domain"
|
||||
else
|
||||
suitable_spf_records = spf_records.select { |d| d =~ /include:\s*#{Regexp.escape(Postal.config.dns.spf_include)}/ }
|
||||
suitable_spf_records = spf_records.grep(/include:\s*#{Regexp.escape(Postal.config.dns.spf_include)}/)
|
||||
if suitable_spf_records.empty?
|
||||
self.spf_status = "Invalid"
|
||||
self.spf_error = "An SPF record exists but it doesn't include #{Postal.config.dns.spf_include}"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module HasMessage
|
||||
|
||||
def self.included(base)
|
||||
@@ -18,7 +20,7 @@ module HasMessage
|
||||
def include_message
|
||||
queued_messages = all.to_a
|
||||
server_ids = queued_messages.map(&:server_id).uniq
|
||||
if server_ids.size == 0
|
||||
if server_ids.empty?
|
||||
return []
|
||||
elsif server_ids.size > 1
|
||||
raise Postal::Error, "'include_message' can only be used on collections of messages from the same server"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module HasSoftDestroy
|
||||
|
||||
def self.included(base)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module HasUUID
|
||||
|
||||
def self.included(base)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: credentials
|
||||
@@ -21,7 +23,7 @@ class Credential < ApplicationRecord
|
||||
|
||||
belongs_to :server
|
||||
|
||||
TYPES = ["SMTP", "API", "SMTP-IP"]
|
||||
TYPES = %w[SMTP API SMTP-IP].freeze
|
||||
|
||||
validates :key, presence: true, uniqueness: { case_sensitive: false }
|
||||
validates :type, inclusion: { in: TYPES }
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: domains
|
||||
@@ -42,15 +44,14 @@ class Domain < ApplicationRecord
|
||||
|
||||
include HasDNSChecks
|
||||
|
||||
VERIFICATION_EMAIL_ALIASES = ["webmaster", "postmaster", "admin", "administrator", "hostmaster"]
|
||||
VERIFICATION_EMAIL_ALIASES = %w[webmaster postmaster admin administrator hostmaster].freeze
|
||||
VERIFICATION_METHODS = %w[DNS Email].freeze
|
||||
|
||||
belongs_to :server, optional: true
|
||||
belongs_to :owner, optional: true, polymorphic: true
|
||||
has_many :routes, dependent: :destroy
|
||||
has_many :track_domains, dependent: :destroy
|
||||
|
||||
VERIFICATION_METHODS = ["DNS", "Email"]
|
||||
|
||||
validates :name, presence: true, format: { with: /\A[a-z0-9\-.]*\z/ }, uniqueness: { case_sensitive: false, scope: [:owner_type, :owner_id], message: "is already added" }
|
||||
validates :verification_method, inclusion: { in: VERIFICATION_METHODS }
|
||||
|
||||
@@ -83,8 +84,8 @@ class Domain < ApplicationRecord
|
||||
|
||||
def parent_domains
|
||||
parts = name.split(".")
|
||||
parts[0, parts.size - 1].each_with_index.map do |p, i|
|
||||
parts[i..-1].join(".")
|
||||
parts[0, parts.size - 1].each_with_index.map do |_, i|
|
||||
parts[i..].join(".")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: http_endpoints
|
||||
@@ -29,8 +31,8 @@ class HTTPEndpoint < ApplicationRecord
|
||||
has_many :routes, as: :endpoint
|
||||
has_many :additional_route_endpoints, dependent: :destroy, as: :endpoint
|
||||
|
||||
ENCODINGS = ["BodyAsJSON", "FormData"]
|
||||
FORMATS = ["Hash", "RawMessage"]
|
||||
ENCODINGS = %w[BodyAsJSON FormData].freeze
|
||||
FORMATS = %w[Hash RawMessage].freeze
|
||||
|
||||
before_destroy :update_routes
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class IncomingMessagePrototype
|
||||
|
||||
attr_accessor :to
|
||||
@@ -22,13 +24,14 @@ class IncomingMessagePrototype
|
||||
end
|
||||
|
||||
def route
|
||||
@routes ||= if @to.present?
|
||||
uname, domain = @to.split("@", 2)
|
||||
uname, tag = uname.split("+", 2)
|
||||
@server.routes.includes(:domain).where(domains: { name: domain }, name: uname).first
|
||||
end
|
||||
@route ||= if @to.present?
|
||||
uname, domain = @to.split("@", 2)
|
||||
uname, _tag = uname.split("+", 2)
|
||||
@server.routes.includes(:domain).where(domains: { name: domain }, name: uname).first
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:disable Lint/DuplicateMethods
|
||||
def attachments
|
||||
(@attachments || []).map do |attachment|
|
||||
{
|
||||
@@ -38,6 +41,7 @@ class IncomingMessagePrototype
|
||||
}
|
||||
end
|
||||
end
|
||||
# rubocop:enable Lint/DuplicateMethods
|
||||
|
||||
def create_messages
|
||||
if valid?
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: ip_addresses
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: ip_pools
|
||||
|
||||
+17
-11
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: ip_pool_rules
|
||||
@@ -66,18 +68,22 @@ class IPPoolRule < ApplicationRecord
|
||||
errors.add :ip_pool_id, "must belong to the organization"
|
||||
end
|
||||
|
||||
def self.address_matches?(condition, address)
|
||||
address = Postal::Helpers.strip_name_from_address(address)
|
||||
if condition =~ /@/
|
||||
parts = address.split("@")
|
||||
domain = parts.pop
|
||||
uname = parts.join("@")
|
||||
uname, = uname.split("+", 2)
|
||||
condition == "#{uname}@#{domain}"
|
||||
else
|
||||
# Match as a domain
|
||||
condition == address.split("@").last
|
||||
class << self
|
||||
|
||||
def address_matches?(condition, address)
|
||||
address = Postal::Helpers.strip_name_from_address(address)
|
||||
if condition =~ /@/
|
||||
parts = address.split("@")
|
||||
domain = parts.pop
|
||||
uname = parts.join("@")
|
||||
uname, = uname.split("+", 2)
|
||||
condition == "#{uname}@#{domain}"
|
||||
else
|
||||
# Match as a domain
|
||||
condition == address.split("@").last
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: organizations
|
||||
@@ -23,7 +25,7 @@
|
||||
|
||||
class Organization < ApplicationRecord
|
||||
|
||||
RESERVED_PERMALINKS = ["new", "edit", "remove", "delete", "destroy", "admin", "mail", "org", "server"]
|
||||
RESERVED_PERMALINKS = %w[new edit remove delete destroy admin mail org server].freeze
|
||||
|
||||
INITIAL_QUOTA = 10
|
||||
INITIAL_SUPER_QUOTA = 10_000
|
||||
@@ -48,7 +50,7 @@ class Organization < ApplicationRecord
|
||||
has_many :ip_pool_rules, dependent: :destroy, as: :owner
|
||||
|
||||
after_create do
|
||||
if pool = IPPool.default
|
||||
if IPPool.default
|
||||
ip_pools << IPPool.default
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: organization_ip_pools
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: organization_users
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "resolv"
|
||||
|
||||
class OutgoingMessagePrototype
|
||||
@@ -47,13 +49,11 @@ class OutgoingMessagePrototype
|
||||
end
|
||||
|
||||
def find_domain
|
||||
@domain ||= begin
|
||||
domain = @server.authenticated_domain_for_address(@from)
|
||||
if @server.allow_sender? && domain.nil?
|
||||
domain = @server.authenticated_domain_for_address(@sender)
|
||||
end
|
||||
domain || :none
|
||||
domain = @server.authenticated_domain_for_address(@from)
|
||||
if @server.allow_sender? && domain.nil?
|
||||
domain = @server.authenticated_domain_for_address(@sender)
|
||||
end
|
||||
domain || :none
|
||||
end
|
||||
|
||||
def to_addresses
|
||||
@@ -93,6 +93,7 @@ class OutgoingMessagePrototype
|
||||
@errors || {}
|
||||
end
|
||||
|
||||
# rubocop:disable Lint/DuplicateMethods
|
||||
def attachments
|
||||
(@attachments || []).map do |attachment|
|
||||
{
|
||||
@@ -102,6 +103,7 @@ class OutgoingMessagePrototype
|
||||
}
|
||||
end
|
||||
end
|
||||
# rubocop:enable Lint/DuplicateMethods
|
||||
|
||||
def validate
|
||||
@errors = []
|
||||
@@ -135,7 +137,7 @@ class OutgoingMessagePrototype
|
||||
end
|
||||
|
||||
if attachments.present?
|
||||
attachments.each_with_index do |attachment, index|
|
||||
attachments.each do |attachment|
|
||||
if attachment[:name].blank?
|
||||
@errors << "AttachmentMissingName" unless @errors.include?("AttachmentMissingName")
|
||||
elsif attachment[:data].blank?
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: queued_messages
|
||||
|
||||
+16
-11
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: routes
|
||||
@@ -22,7 +24,9 @@
|
||||
|
||||
class Route < ApplicationRecord
|
||||
|
||||
MODES = ["Endpoint", "Accept", "Hold", "Bounce", "Reject"]
|
||||
MODES = %w[Endpoint Accept Hold Bounce Reject].freeze
|
||||
SPAM_MODES = %w[Mark Quarantine Fail].freeze
|
||||
ENDPOINT_TYPES = %w[SMTPEndpoint HTTPEndpoint AddressEndpoint].freeze
|
||||
|
||||
include HasUUID
|
||||
|
||||
@@ -31,9 +35,6 @@ class Route < ApplicationRecord
|
||||
belongs_to :endpoint, polymorphic: true, optional: true
|
||||
has_many :additional_route_endpoints, dependent: :destroy
|
||||
|
||||
SPAM_MODES = ["Mark", "Quarantine", "Fail"]
|
||||
ENDPOINT_TYPES = ["SMTPEndpoint", "HTTPEndpoint", "AddressEndpoint"]
|
||||
|
||||
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" } }
|
||||
@@ -115,7 +116,7 @@ class Route < ApplicationRecord
|
||||
if route.save
|
||||
seen << route.id
|
||||
else
|
||||
route.errors.each do |field, message|
|
||||
route.errors.each do |_, message|
|
||||
errors.add :base, message
|
||||
end
|
||||
raise ActiveRecord::RecordInvalid
|
||||
@@ -200,7 +201,7 @@ class Route < ApplicationRecord
|
||||
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 = Route.where(name: "__returnpath__").where.not(id: id).exists?
|
||||
elsif Route.where(name: "__returnpath__").where.not(id: id).exists?
|
||||
errors.add :base, "A return path route already exists for this server"
|
||||
end
|
||||
end
|
||||
@@ -218,12 +219,16 @@ class Route < ApplicationRecord
|
||||
errors.add :base, "Additional routes are not permitted unless the primary route is an actual endpoint"
|
||||
end
|
||||
|
||||
def self.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
|
||||
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
|
||||
route
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
+25
-12
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: servers
|
||||
@@ -41,7 +43,8 @@
|
||||
|
||||
class Server < ApplicationRecord
|
||||
|
||||
RESERVED_PERMALINKS = ["new", "all", "search", "stats", "edit", "manage", "delete", "destroy", "remove"]
|
||||
RESERVED_PERMALINKS = %w[new all search stats edit manage delete destroy remove].freeze
|
||||
MODES = %w[Live Development].freeze
|
||||
|
||||
include HasUUID
|
||||
include HasSoftDestroy
|
||||
@@ -62,8 +65,6 @@ class Server < ApplicationRecord
|
||||
has_many :track_domains, dependent: :destroy
|
||||
has_many :ip_pool_rules, dependent: :destroy, as: :owner
|
||||
|
||||
MODES = ["Live", "Development"]
|
||||
|
||||
random_string :token, type: :chars, length: 6, unique: true, upper_letters_only: true
|
||||
default_value :permalink, -> { name ? name.parameterize : nil }
|
||||
default_value :raw_message_retention_days, -> { 30 }
|
||||
@@ -155,11 +156,11 @@ class Server < ApplicationRecord
|
||||
time = Time.now.utc
|
||||
total_outgoing = 0.0
|
||||
total_bounces = 0.0
|
||||
message_db.statistics.get(:daily, [:outgoing, :bounces], time, 30).each do |date, stat|
|
||||
message_db.statistics.get(:daily, [:outgoing, :bounces], time, 30).each do |_, stat|
|
||||
total_outgoing += stat[:outgoing]
|
||||
total_bounces += stat[:bounces]
|
||||
end
|
||||
total_outgoing == 0 ? 0 : (total_bounces / total_outgoing) * 100
|
||||
total_outgoing.zero? ? 0 : (total_bounces / total_outgoing) * 100
|
||||
end
|
||||
end
|
||||
|
||||
@@ -218,6 +219,10 @@ class Server < ApplicationRecord
|
||||
}
|
||||
end
|
||||
|
||||
# Return the domain which can be used to authenticate emails sent from the given e-mail address.
|
||||
#
|
||||
# @param address [String] an e-mail address
|
||||
# @return [Domain, nil] the domain to use for authentication
|
||||
def authenticated_domain_for_address(address)
|
||||
return nil if address.blank?
|
||||
|
||||
@@ -226,16 +231,24 @@ class Server < ApplicationRecord
|
||||
return nil unless uname
|
||||
return nil unless domain_name
|
||||
|
||||
uname, = uname.split("+", 2)
|
||||
# Find a verified domain which directly matches the domain name for the given address.
|
||||
domain = Domain.verified
|
||||
.order(owner_type: :desc)
|
||||
.where("(owner_type = 'Organization' AND owner_id = ?) OR " \
|
||||
"(owner_type = 'Server' AND owner_id = ?)", organization_id, id)
|
||||
.where(name: domain_name)
|
||||
.first
|
||||
|
||||
# Check the server's domain
|
||||
if domain = Domain.verified.order(owner_type: :desc).where("(owner_type = 'Organization' AND owner_id = ?) OR (owner_type = 'Server' AND owner_id = ?)", organization_id, id).where(name: domain_name).first
|
||||
return domain
|
||||
end
|
||||
# If there is a matching domain, return it
|
||||
return domain if domain
|
||||
|
||||
return unless any_domain = domains.verified.where(use_for_any: true).order(:name).first
|
||||
# Otherwise, we need to look to see if there is a domain configured which can be used as the authenticated
|
||||
# domain for any domain. This will look for domains directly within the server and return that.
|
||||
any_domain = domains.verified.where(use_for_any: true).order(:name).first
|
||||
return any_domain if any_domain
|
||||
|
||||
any_domain
|
||||
# Return nil if we can't find anything suitable
|
||||
nil
|
||||
end
|
||||
|
||||
def find_authenticated_domain_from_headers(headers)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: smtp_endpoints
|
||||
@@ -24,7 +26,7 @@ class SMTPEndpoint < ApplicationRecord
|
||||
has_many :routes, as: :endpoint
|
||||
has_many :additional_route_endpoints, dependent: :destroy, as: :endpoint
|
||||
|
||||
SSL_MODES = ["None", "Auto", "STARTTLS", "TLS"]
|
||||
SSL_MODES = %w[None Auto STARTTLS TLS].freeze
|
||||
|
||||
before_destroy :update_routes
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: statistics
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: track_domains
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: users
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: user_invites
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: webhooks
|
||||
|
||||
+12
-10
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: webhook_events
|
||||
@@ -14,16 +16,16 @@
|
||||
|
||||
class WebhookEvent < ApplicationRecord
|
||||
|
||||
EVENTS = [
|
||||
"MessageSent",
|
||||
"MessageDelayed",
|
||||
"MessageDeliveryFailed",
|
||||
"MessageHeld",
|
||||
"MessageBounced",
|
||||
"MessageLinkClicked",
|
||||
"MessageLoaded",
|
||||
"DomainDNSError"
|
||||
]
|
||||
EVENTS = %w[
|
||||
MessageSent
|
||||
MessageDelayed
|
||||
MessageDeliveryFailed
|
||||
MessageHeld
|
||||
MessageBounced
|
||||
MessageLinkClicked
|
||||
MessageLoaded
|
||||
DomainDNSError
|
||||
].freeze
|
||||
|
||||
belongs_to :webhook
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: webhook_requests
|
||||
@@ -19,7 +21,7 @@ class WebhookRequest < ApplicationRecord
|
||||
|
||||
include HasUUID
|
||||
|
||||
RETRIES = { 1 => 2.minutes, 2 => 3.minutes, 3 => 6.minutes, 4 => 10.minutes, 5 => 15.minutes }
|
||||
RETRIES = { 1 => 2.minutes, 2 => 3.minutes, 3 => 6.minutes, 4 => 10.minutes, 5 => 15.minutes }.freeze
|
||||
|
||||
belongs_to :server
|
||||
belongs_to :webhook, optional: true
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
APP_PATH = File.expand_path("../config/application", __dir__)
|
||||
require_relative "../config/boot"
|
||||
require "rails/commands"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../config/boot"
|
||||
require "rake"
|
||||
Rake.application.run
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "pathname"
|
||||
require "fileutils"
|
||||
include FileUtils
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "pathname"
|
||||
require "fileutils"
|
||||
include FileUtils
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# This file is used by Rack-based servers to start the application.
|
||||
|
||||
require_relative "config/environment"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "boot"
|
||||
|
||||
require "rails"
|
||||
@@ -30,7 +32,7 @@ module Postal
|
||||
config.eager_load_paths << Rails.root.join("lib")
|
||||
|
||||
# Disable field_with_errors
|
||||
config.action_view.field_error_proc = proc { |t, i| t }
|
||||
config.action_view.field_error_proc = proc { |t, _| t }
|
||||
|
||||
# Load the tracking server middleware
|
||||
require "postal/tracking_middleware"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
$stdout.sync = true
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Clockwork
|
||||
|
||||
configure do |config|
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Load the Rails application.
|
||||
require_relative "application"
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
Rails.application.configure do
|
||||
# Settings specified here will take precedence over those in config/application.rb.
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
Rails.application.configure do
|
||||
# Settings specified here will take precedence over those in config/application.rb.
|
||||
|
||||
@@ -71,7 +73,7 @@ Rails.application.configure do
|
||||
# config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
|
||||
|
||||
if ENV["RAILS_LOG_TO_STDOUT"].present?
|
||||
logger = ActiveSupport::Logger.new(STDOUT)
|
||||
logger = ActiveSupport::Logger.new($stdout)
|
||||
logger.formatter = config.log_formatter
|
||||
config.logger = ActiveSupport::TaggedLogging.new(logger)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
Rails.application.configure do
|
||||
# Settings specified here will take precedence over those in config/application.rb.
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# ApplicationController.renderer.defaults.merge!(
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Version of your assets, change this if you want to expire all your assets.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Specify a serializer for the signed and encrypted cookie jars.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Configure sensitive parameters which will be filtered from the log file.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Add new inflection rules using the following format. Inflections
|
||||
@@ -12,7 +14,6 @@
|
||||
|
||||
# These inflection rules are supported but not enabled by default:
|
||||
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
||||
|
||||
inflect.acronym "DKIM"
|
||||
inflect.acronym "HTTP"
|
||||
inflect.acronym "SMTP"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "mail"
|
||||
module Mail
|
||||
|
||||
@@ -20,7 +22,7 @@ module Mail
|
||||
|
||||
## Extract plain text body of message
|
||||
def plain_body
|
||||
if multipart? and text_part
|
||||
if multipart? && text_part
|
||||
text_part.decoded
|
||||
elsif mime_type == "text/plain" || mime_type.nil?
|
||||
decoded
|
||||
@@ -29,7 +31,7 @@ module Mail
|
||||
|
||||
## Extract HTML text body of message
|
||||
def html_body
|
||||
if multipart? and html_part
|
||||
if multipart? && html_part
|
||||
html_part.decoded
|
||||
elsif mime_type == "text/html"
|
||||
decoded
|
||||
@@ -109,14 +111,16 @@ module Mail
|
||||
# Handle attached emails as attachments
|
||||
class AttachmentsList < Array
|
||||
|
||||
# rubocop:disable Lint/MissingSuper
|
||||
def initialize(parts_list)
|
||||
@parts_list = parts_list
|
||||
@content_disposition_type = "attachment"
|
||||
parts_list.map do |p|
|
||||
(p.parts.empty? and p.attachment?) ? p : p.attachments
|
||||
end.flatten.compact.each { |a| self << a }
|
||||
self
|
||||
parts = parts_list.map do |p|
|
||||
p.parts.empty? && p.attachment? ? p : p.attachments
|
||||
end.flatten.compact
|
||||
parts.each { |a| self << a }
|
||||
end
|
||||
# rubocop:enable Lint/MissingSuper
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Add new mime types for use in respond_to blocks:
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Be sure to restart your server when you modify this file.
|
||||
#
|
||||
# This file contains migration options to ease your Rails 5.0 upgrade.
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "postal"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ActionView
|
||||
module RecordIdentifier
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user