مراية لـ
https://github.com/postalserver/postal.git
تم المزامنة 2026-01-17 05:33:35 +00:00
refactor: remove the fast server
هذا الالتزام موجود في:
@@ -9,7 +9,6 @@ module Postal
|
||||
autoload :Countries
|
||||
autoload :DKIMHeader
|
||||
autoload :Error
|
||||
autoload :FastServer
|
||||
autoload :Helpers
|
||||
autoload :HTTP
|
||||
autoload :HTTPSender
|
||||
@@ -29,6 +28,7 @@ module Postal
|
||||
autoload :SMTPSender
|
||||
autoload :SMTPServer
|
||||
autoload :SpamCheck
|
||||
autoload :TrackingMiddleware
|
||||
autoload :UserCreator
|
||||
autoload :Version
|
||||
autoload :Worker
|
||||
@@ -37,7 +37,6 @@ module Postal
|
||||
def self.eager_load!
|
||||
super
|
||||
Postal::MessageDB.eager_load!
|
||||
Postal::FastServer.eager_load!
|
||||
Postal::SMTPServer.eager_load!
|
||||
end
|
||||
|
||||
|
||||
@@ -151,35 +151,6 @@ module Postal
|
||||
end
|
||||
end
|
||||
|
||||
def self.fast_server_default_private_key_path
|
||||
config.fast_server.default_private_key_path || config_root.join('fast_server.key')
|
||||
end
|
||||
|
||||
def self.fast_server_default_private_key
|
||||
@fast_server_default_private_key ||= OpenSSL::PKey::RSA.new(File.read(fast_server_default_private_key_path))
|
||||
end
|
||||
|
||||
def self.fast_server_default_certificate_path
|
||||
config.fast_server.default_tls_certificate_path || config_root.join('fast_server.cert')
|
||||
end
|
||||
|
||||
def self.fast_server_default_certificate_data
|
||||
@fast_server_default_certificate_data ||= File.read(fast_server_default_certificate_path)
|
||||
end
|
||||
|
||||
def self.fast_server_default_certificates
|
||||
@fast_server_default_certificates ||= begin
|
||||
certs = self.fast_server_default_certificate_data.scan(/-----BEGIN CERTIFICATE-----.+?-----END CERTIFICATE-----/m)
|
||||
certs.map do |c|
|
||||
OpenSSL::X509::Certificate.new(c)
|
||||
end.freeze
|
||||
end
|
||||
end
|
||||
|
||||
def self.lets_encrypt_private_key_path
|
||||
@lets_encrypt_private_key_path ||= Postal.config_root.join('lets_encrypt.pem')
|
||||
end
|
||||
|
||||
def self.signing_key_path
|
||||
config_root.join('signing.key')
|
||||
end
|
||||
@@ -203,19 +174,11 @@ module Postal
|
||||
raise ConfigError, "No config found at #{self.config_file_path}"
|
||||
end
|
||||
|
||||
unless File.exists?(self.lets_encrypt_private_key_path)
|
||||
raise ConfigError, "No Let's Encrypt private key found at #{self.lets_encrypt_private_key_path}"
|
||||
end
|
||||
|
||||
unless File.exists?(self.signing_key_path)
|
||||
raise ConfigError, "No signing key found at #{self.signing_key_path}"
|
||||
end
|
||||
end
|
||||
|
||||
def self.tracking_available?
|
||||
self.config.fast_server.enabled?
|
||||
end
|
||||
|
||||
def self.ip_pools?
|
||||
self.config.general.use_ip_pools?
|
||||
end
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
module Postal
|
||||
module FastServer
|
||||
extend ActiveSupport::Autoload
|
||||
eager_autoload do
|
||||
autoload :Client
|
||||
autoload :HTTPHeader
|
||||
autoload :HTTPHeaderSet
|
||||
autoload :Interface
|
||||
autoload :Server
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,172 +0,0 @@
|
||||
require 'stringio'
|
||||
|
||||
module Postal
|
||||
module FastServer
|
||||
class Client
|
||||
class ClientWentAway < StandardError; end
|
||||
class BadRequest < StandardError; end
|
||||
|
||||
def initialize(socket, options)
|
||||
@raw_socket = socket
|
||||
@options = options
|
||||
end
|
||||
|
||||
def run
|
||||
Timeout.timeout(15) do
|
||||
if Postal.config.fast_server.proxy_protocol
|
||||
|
||||
# gets without readahead
|
||||
line = ""
|
||||
char = nil
|
||||
while(char != "\n")
|
||||
char = @raw_socket.read(1)
|
||||
line << char
|
||||
end
|
||||
line.chomp!
|
||||
|
||||
if m = line.match(/\APROXY (.+) (.+) (.+) (.+) (.+)\z/)
|
||||
@remote_ip = m[2]
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if self.ssl?
|
||||
@socket = OpenSSL::SSL::SSLSocket.new(@raw_socket, self.class.ssl_context)
|
||||
@socket.accept
|
||||
else
|
||||
@socket = @raw_socket
|
||||
end
|
||||
|
||||
Timeout::timeout(20) do
|
||||
# Read the request line
|
||||
request = @socket.gets.to_s.chomp
|
||||
# Split the request into its 3 parts
|
||||
method, path, protocol = request.split(' ', 3)
|
||||
|
||||
raise BadRequest unless method && path && protocol
|
||||
|
||||
# Create an empty header set
|
||||
header_set = HTTPHeaderSet.new
|
||||
# Read each header and populate the header set
|
||||
loop do
|
||||
header = @socket.gets
|
||||
if header.nil?
|
||||
raise ClientWentAway
|
||||
elsif header.chomp == ""
|
||||
break
|
||||
else
|
||||
header_set << HTTPHeader.from_string(header.chomp)
|
||||
end
|
||||
end
|
||||
|
||||
# At this point, one might want to read the request body, but I don't think we need it.
|
||||
|
||||
# Build rack request
|
||||
server_name, server_port = header_set['Host'].try(:value).to_s.split(":", 2)
|
||||
request = {
|
||||
"REQUEST_METHOD" => method,
|
||||
"SCRIPT_NAME" => "",
|
||||
"PATH_INFO" => path.split('?', 2)[0],
|
||||
"QUERY_STRING" => path.split('?', 2)[1],
|
||||
"SERVER_NAME" => server_name || "",
|
||||
"SERVER_PORT" => server_name || "",
|
||||
"rack.version" => [1, 3],
|
||||
"rack.url_scheme" => ssl? ? "https" : "http",
|
||||
"rack.input" => StringIO.new(""),
|
||||
"rack.errors" => STDERR,
|
||||
"rack.multithread" => true,
|
||||
"rack.multiprocess" => true,
|
||||
"rack.run_once" => false,
|
||||
"rack.hijack" => false,
|
||||
"rack.hijack_io" => false,
|
||||
"REMOTE_ADDR" => remote_ip,
|
||||
}
|
||||
|
||||
# Add request headers to rack hash
|
||||
header_set.headers.each do |header|
|
||||
request["HTTP_" + header.key.gsub('-', '_').upcase] = header.value
|
||||
end
|
||||
|
||||
# Call the rack app and process the result
|
||||
code, headers, body = Interface.new.call(request)
|
||||
response = "HTTP/1.1 #{code} #{Rack::Utils::HTTP_STATUS_CODES[code]}\r\n"
|
||||
headers.each do |k,v|
|
||||
response << "#{k}:#{v}\r\n"
|
||||
end
|
||||
response << "\r\n"
|
||||
body.each do |data|
|
||||
response << data
|
||||
end
|
||||
@socket.write(response)
|
||||
end
|
||||
end
|
||||
rescue ClientWentAway, Timeout::Error, Errno::ECONNRESET
|
||||
# We don't really care if a client has disapeared, close the sockets and carry on.
|
||||
rescue OpenSSL::SSL::SSLError
|
||||
# Don't worry about SSL negotiation failures, disconnect and carry on
|
||||
rescue BadRequest
|
||||
# We couldn't read a proper HTTP request, disconnect the client
|
||||
rescue => e
|
||||
if defined?(Raven)
|
||||
Raven.capture_exception(e)
|
||||
end
|
||||
ensure
|
||||
@socket.close rescue nil
|
||||
@raw_socket.close rescue nil
|
||||
end
|
||||
|
||||
def ssl?
|
||||
!!@options[:ssl]
|
||||
end
|
||||
|
||||
def remote_ip
|
||||
@remote_ip || @raw_socket.peeraddr[3].sub('::ffff:', '')
|
||||
end
|
||||
|
||||
def self.ssl_context(domain_name = nil)
|
||||
@ssl_certificates ||= {}
|
||||
unless @ssl_certificates_refreshed && @ssl_certificates_refreshed > Time.now.utc.beginning_of_day
|
||||
@ssl_certificates_refreshed = Time.now.utc
|
||||
@ssl_certificates = {}
|
||||
end
|
||||
@ssl_certificates[domain_name] ||= OpenSSL::SSL::SSLContext.new.tap do |ssl_context|
|
||||
if domain_name
|
||||
if domain = TrackCertificate.active.where(:domain => domain_name).first
|
||||
ssl_context.cert = domain.certificate_object
|
||||
ssl_context.extra_chain_cert = domain.intermediaries_array
|
||||
ssl_context.key = domain.key_object
|
||||
end
|
||||
end
|
||||
|
||||
if ssl_context.cert.nil?
|
||||
ssl_context.cert = Postal.fast_server_default_certificates[0]
|
||||
ssl_context.extra_chain_cert = Postal.fast_server_default_certificates[1..-1]
|
||||
ssl_context.key = Postal.fast_server_default_private_key
|
||||
end
|
||||
|
||||
ssl_context.ssl_version = "SSLv23"
|
||||
ssl_context.ciphers = 'EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4 !DH'
|
||||
ssl_context.options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] |
|
||||
OpenSSL::SSL::OP_NO_SSLv2 |
|
||||
OpenSSL::SSL::OP_NO_SSLv3 |
|
||||
OpenSSL::SSL::OP_NO_COMPRESSION |
|
||||
OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE
|
||||
|
||||
if ssl_context.respond_to?('tmp_ecdh_callback=')
|
||||
ssl_context.tmp_ecdh_callback = Proc.new do |*a|
|
||||
OpenSSL::PKey::EC.new("prime256v1")
|
||||
end
|
||||
end
|
||||
|
||||
unless domain_name
|
||||
ssl_context.servername_cb = Proc.new do |ctx, hostname|
|
||||
self.ssl_context(hostname)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,21 +0,0 @@
|
||||
module Postal
|
||||
module FastServer
|
||||
class HTTPHeader
|
||||
attr_accessor :key, :value
|
||||
def self.from_string(string)
|
||||
k, v = string.to_s.split(/\:\s*/, 2)
|
||||
self.new(k.to_s, v.to_s)
|
||||
end
|
||||
|
||||
def initialize(k, v)
|
||||
@key = k
|
||||
@value = v
|
||||
end
|
||||
|
||||
def to_s
|
||||
@key + ": " + @value
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -1,37 +0,0 @@
|
||||
module Postal
|
||||
module FastServer
|
||||
class HTTPHeaderSet
|
||||
attr_accessor :headers
|
||||
def initialize
|
||||
@headers = []
|
||||
end
|
||||
|
||||
def self.from_string_array(array)
|
||||
header_set = self.new
|
||||
header_set.headers = array.map{|h|HTTPHeader.from_string(h)}
|
||||
header_set
|
||||
end
|
||||
|
||||
def select(key)
|
||||
@headers.select{|h|h.key.downcase == key.downcase}
|
||||
end
|
||||
|
||||
def [](key)
|
||||
@headers.find{|h|h.key.downcase == key.downcase}
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
self.delete(key)
|
||||
@headers << HTTPHeader.new(key, value)
|
||||
end
|
||||
|
||||
def delete(key)
|
||||
@headers.delete_if{|h|h.key.downcase == key.downcase}
|
||||
end
|
||||
|
||||
def <<(header)
|
||||
@headers << header
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,96 +0,0 @@
|
||||
module Postal
|
||||
module FastServer
|
||||
class Interface
|
||||
|
||||
# TODO: Make this multithreaded? Thread-safe?
|
||||
|
||||
TRACKING_PIXEL = File.read(Rails.root.join('app', 'assets', 'images', 'tracking_pixel.png'))
|
||||
|
||||
def get_message_db_from_server_token(token)
|
||||
if server = ::Server.find_by_token(token)
|
||||
server.message_db
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def call(env)
|
||||
request = Rack::Request.new(env)
|
||||
|
||||
if request.path =~ /\A\/(\.well-known\/.*)/
|
||||
if certificate = ::TrackCertificate.find_by_verification_path($1)
|
||||
return [200, {'Content-Length' => certificate.verification_string.bytesize.to_s}, [certificate.verification_string]]
|
||||
else
|
||||
return [404, {}, ["Verification not found"]]
|
||||
end
|
||||
|
||||
elsif request.path =~ /\A\/img\/([a-z0-9\-]+)\/([a-z0-9\-]+)/i
|
||||
server_token = $1
|
||||
message_token = $2
|
||||
|
||||
if message_db = get_message_db_from_server_token(server_token)
|
||||
begin
|
||||
message = message_db.message(:token => message_token)
|
||||
message.create_load(request)
|
||||
rescue Postal::MessageDB::Message::NotFound
|
||||
# This message has been removed, we'll just continue to serve the image
|
||||
rescue => e
|
||||
# Somethign else went wrong. We don't want to stop the image loading though because
|
||||
# this is our problem. Log this exception though.
|
||||
if defined?(Raven)
|
||||
Raven.capture_exception(e)
|
||||
end
|
||||
end
|
||||
source_image = request.params['src']
|
||||
if source_image.nil?
|
||||
headers = {}
|
||||
headers['Content-Type'] = "image/png"
|
||||
headers['Content-Length'] = TRACKING_PIXEL.bytesize.to_s
|
||||
return [200, headers, [TRACKING_PIXEL]]
|
||||
elsif source_image =~ /\Ahttps?\:\/\//
|
||||
response = Postal::HTTP.get(source_image, :timeout => 3)
|
||||
if response[:code] == 200
|
||||
headers = {}
|
||||
headers['Content-Type'] = response[:headers]['content-type']&.first
|
||||
headers['Last-Modified'] = response[:headers]['last-modified']&.first
|
||||
headers['Cache-Control'] = response[:headers]['cache-control']&.first
|
||||
headers['Etag'] = response[:headers]['etag']&.first
|
||||
headers['Content-Length'] = response[:body].bytesize.to_s
|
||||
return [200, headers, [response[:body]]]
|
||||
else
|
||||
return [404, {}, ['Not found']]
|
||||
end
|
||||
else
|
||||
return [400, {}, ['Invalid/missing source image']]
|
||||
end
|
||||
else
|
||||
return [404, {}, ['Invalid Server Token']]
|
||||
end
|
||||
end
|
||||
|
||||
if request.path =~ /\A\/([a-z0-9\-]+)\/([a-z0-9\-]+)/i
|
||||
server_token = $1
|
||||
link_token = $2
|
||||
if message_db = get_message_db_from_server_token(server_token)
|
||||
if link = message_db.select(:links, :where => {:token => link_token}, :limit => 1).first
|
||||
time = Time.now.to_f
|
||||
if link['message_id']
|
||||
message_db.update(:messages, {:clicked => time}, :where => {:id => link['message_id']})
|
||||
message_db.insert(:clicks, {:message_id => link['message_id'], :link_id => link['id'], :ip_address => request.ip, :user_agent => request.user_agent, :timestamp => time})
|
||||
SendWebhookJob.queue(:main, :server_id => message_db.server_id, :event => 'MessageLinkClicked', :payload => {:_message => link['message_id'], :url => link['url'], :token => link['token'], :ip_address => request.ip, :user_agent => request.user_agent})
|
||||
end
|
||||
return [307, {'Location' => link['url']}, ["Redirected to: #{link['url']}"]]
|
||||
else
|
||||
return [404, {}, ['Link not found']]
|
||||
end
|
||||
else
|
||||
return [404, {}, ['Invalid Server Token']]
|
||||
end
|
||||
end
|
||||
|
||||
[200, {}, ["Hello."]]
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,49 +0,0 @@
|
||||
require 'socket'
|
||||
require 'openssl'
|
||||
|
||||
module Postal
|
||||
module FastServer
|
||||
class Server
|
||||
|
||||
def run
|
||||
if Postal.config.fast_server.bind_address.blank?
|
||||
Postal.logger_for(:fast_server).info "Cannot start fast server because no bind address has been specified"
|
||||
exit 1
|
||||
end
|
||||
|
||||
Thread.abort_on_exception = true
|
||||
TrackCertificate
|
||||
|
||||
bind_addresses = Postal.config.fast_server.bind_address
|
||||
bind_addresses = [bind_addresses] unless bind_addresses.is_a?(Array)
|
||||
|
||||
server_sockets = bind_addresses.each_with_object({}) do |bind_addr, sockets|
|
||||
sockets[TCPServer.new(bind_addr, Postal.config.fast_server.port)] = {:ssl => false}
|
||||
sockets[TCPServer.new(bind_addr, Postal.config.fast_server.ssl_port)] = {:ssl => true}
|
||||
Postal.logger_for(:fast_server).info("Fast server started listening on HTTP (#{bind_addr}:#{Postal.config.fast_server.port})")
|
||||
Postal.logger_for(:fast_server).info("Fast server started listening on HTTPS port (#{bind_addr}:#{Postal.config.fast_server.ssl_port})")
|
||||
end
|
||||
|
||||
loop do
|
||||
client = nil
|
||||
ios = select(server_sockets.keys, nil, nil, 1)
|
||||
if ios && server_io = ios[0][0]
|
||||
begin
|
||||
client_io = server_io.accept_nonblock
|
||||
client = Client.new(client_io, server_sockets[server_io])
|
||||
Thread.new(client) { |t_client| t_client.run }
|
||||
rescue IO::WaitReadable, Errno::EINTR
|
||||
# Never mind, guess the client went away
|
||||
rescue => e
|
||||
if defined?(Raven)
|
||||
Raven.capture_exception(e)
|
||||
end
|
||||
client_io.close rescue nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,29 +0,0 @@
|
||||
require 'acme-client'
|
||||
|
||||
module Postal
|
||||
module LetsEncrypt
|
||||
|
||||
def self.client
|
||||
@client ||= Acme::Client.new(:private_key => private_key, :directory => directory)
|
||||
end
|
||||
|
||||
def self.private_key
|
||||
@private_key ||= OpenSSL::PKey::RSA.new(File.open(Postal.lets_encrypt_private_key_path))
|
||||
end
|
||||
|
||||
def self.directory
|
||||
@directory ||= Rails.env.development? ? "https://acme-staging-v02.api.letsencrypt.org/directory" : "https://acme-v02.api.letsencrypt.org/directory"
|
||||
end
|
||||
|
||||
def self.register_private_key(email_address)
|
||||
registration = client.new_account(:contact => "mailto:#{email_address}", :terms_of_service_agreed => true)
|
||||
logger.info "Successfully registered private key with address #{email_address}"
|
||||
true
|
||||
end
|
||||
|
||||
def self.logger
|
||||
Postal.logger_for(:lets_encrypt)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -80,11 +80,11 @@ module Postal
|
||||
end
|
||||
|
||||
def parse(part, type = nil)
|
||||
if Postal.tracking_available? && @domain.track_clicks?
|
||||
if @domain.track_clicks?
|
||||
part = insert_links(part, type)
|
||||
end
|
||||
|
||||
if Postal.tracking_available? && @domain.track_loads? && type == :html
|
||||
if @domain.track_loads? && type == :html
|
||||
part = insert_tracking_image(part)
|
||||
end
|
||||
|
||||
|
||||
105
lib/postal/tracking_middleware.rb
Normal file
105
lib/postal/tracking_middleware.rb
Normal file
@@ -0,0 +1,105 @@
|
||||
module Postal
|
||||
class TrackingMiddleware
|
||||
|
||||
TRACKING_PIXEL = File.read(Rails.root.join('app', 'assets', 'images', 'tracking_pixel.png'))
|
||||
|
||||
def initialize(app = nil)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
unless env['HTTP_X_POSTAL_TRACK_HOST'].to_i == 1
|
||||
return @app.call(env)
|
||||
end
|
||||
|
||||
request = Rack::Request.new(env)
|
||||
|
||||
case request.path
|
||||
when /\A\/img\/([a-z0-9\-]+)\/([a-z0-9\-]+)/i
|
||||
server_token = $1
|
||||
message_token = $2
|
||||
dispatch_image_request(request, server_token, message_token)
|
||||
when /\A\/([a-z0-9\-]+)\/([a-z0-9\-]+)/i
|
||||
server_token = $1
|
||||
link_token = $2
|
||||
dispatch_redirect_request(request, server_token, link_token)
|
||||
else
|
||||
[200, {}, ["Hello."]]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def dispatch_image_request(request, server_token, message_token)
|
||||
message_db = get_message_db_from_server_token(server_token)
|
||||
if message_db.nil?
|
||||
return [404, {}, ['Invalid Server Token']]
|
||||
end
|
||||
|
||||
begin
|
||||
message = message_db.message(:token => message_token)
|
||||
message.create_load(request)
|
||||
rescue Postal::MessageDB::Message::NotFound
|
||||
# This message has been removed, we'll just continue to serve the image
|
||||
rescue => e
|
||||
# Somethign else went wrong. We don't want to stop the image loading though because
|
||||
# this is our problem. Log this exception though.
|
||||
Raven.capture_exception(e) if defined?(Raven)
|
||||
end
|
||||
|
||||
source_image = request.params['src']
|
||||
case source_image
|
||||
when nil
|
||||
headers = {}
|
||||
headers['Content-Type'] = "image/png"
|
||||
headers['Content-Length'] = TRACKING_PIXEL.bytesize.to_s
|
||||
return [200, headers, [TRACKING_PIXEL]]
|
||||
when /\Ahttps?\:\/\//
|
||||
response = Postal::HTTP.get(source_image, :timeout => 3)
|
||||
if response[:code] == 200
|
||||
headers = {}
|
||||
headers['Content-Type'] = response[:headers]['content-type']&.first
|
||||
headers['Last-Modified'] = response[:headers]['last-modified']&.first
|
||||
headers['Cache-Control'] = response[:headers]['cache-control']&.first
|
||||
headers['Etag'] = response[:headers]['etag']&.first
|
||||
headers['Content-Length'] = response[:body].bytesize.to_s
|
||||
return [200, headers, [response[:body]]]
|
||||
else
|
||||
return [404, {}, ['Not found']]
|
||||
end
|
||||
else
|
||||
return [400, {}, ['Invalid/missing source image']]
|
||||
end
|
||||
end
|
||||
|
||||
def dispatch_redirect_request(request, server_token, link_token)
|
||||
message_db = get_message_db_from_server_token(server_token)
|
||||
if message_db.nil?
|
||||
return [404, {}, ['Invalid Server Token']]
|
||||
end
|
||||
|
||||
link = message_db.select(:links, :where => {:token => link_token}, :limit => 1).first
|
||||
if link.nil?
|
||||
return [404, {}, ['Link not found']]
|
||||
end
|
||||
|
||||
time = Time.now.to_f
|
||||
if link['message_id']
|
||||
message_db.update(:messages, {:clicked => time}, :where => {:id => link['message_id']})
|
||||
message_db.insert(:clicks, {:message_id => link['message_id'], :link_id => link['id'], :ip_address => request.ip, :user_agent => request.user_agent, :timestamp => time})
|
||||
SendWebhookJob.queue(:main, :server_id => message_db.server_id, :event => 'MessageLinkClicked', :payload => {:_message => link['message_id'], :url => link['url'], :token => link['token'], :ip_address => request.ip, :user_agent => request.user_agent})
|
||||
end
|
||||
|
||||
return [307, {'Location' => link['url']}, ["Redirected to: #{link['url']}"]]
|
||||
end
|
||||
|
||||
def get_message_db_from_server_token(token)
|
||||
if server = ::Server.find_by_token(token)
|
||||
server.message_db
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -28,11 +28,6 @@ namespace :postal do
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Start the fast server'#
|
||||
task :fast_server => :environment do
|
||||
Postal::FastServer::Server.new.run
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Rake::Task['db:migrate'].enhance do
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم