Restructure omni services and add Chatwoot research snapshot
This commit is contained in:
1
research/chatwoot/config/initializers/00_init.rb
Normal file
1
research/chatwoot/config/initializers/00_init.rb
Normal file
@@ -0,0 +1 @@
|
||||
APPS_CONFIG = YAML.load_file(Rails.root.join('config/integration/apps.yml'))
|
||||
@@ -0,0 +1,87 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# original Authors: Gitlab
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/initializers/0_inject_enterprise_edition_module.rb
|
||||
#
|
||||
|
||||
### Ref: https://medium.com/@leo_hetsch/ruby-modules-include-vs-prepend-vs-extend-f09837a5b073
|
||||
# Ancestors chain : it holds a list of constant names which are its ancestors
|
||||
# example, by calling ancestors on the String class,
|
||||
# String.ancestors => [String, Comparable, Object, PP::ObjectMixin, Kernel, BasicObject]
|
||||
#
|
||||
# Include: Ruby will insert the module into the ancestors chain of the class, just after its superclass
|
||||
# ancestor chain : [OriginalClass, IncludedModule, ...]
|
||||
#
|
||||
# Extend: class will actually import the module methods as class methods
|
||||
#
|
||||
# Prepend: Ruby will look into the module methods before looking into the class.
|
||||
# ancestor chain : [PrependedModule, OriginalClass, ...]
|
||||
########
|
||||
|
||||
require 'active_support/inflector'
|
||||
|
||||
module InjectEnterpriseEditionModule
|
||||
def prepend_mod_with(constant_name, namespace: Object, with_descendants: false)
|
||||
each_extension_for(constant_name, namespace) do |constant|
|
||||
prepend_module(constant, with_descendants)
|
||||
end
|
||||
end
|
||||
|
||||
def extend_mod_with(constant_name, namespace: Object)
|
||||
# rubocop:disable Performance/MethodObjectAsBlock
|
||||
each_extension_for(
|
||||
constant_name,
|
||||
namespace,
|
||||
&method(:extend)
|
||||
)
|
||||
# rubocop:enable Performance/MethodObjectAsBlock
|
||||
end
|
||||
|
||||
def include_mod_with(constant_name, namespace: Object)
|
||||
# rubocop:disable Performance/MethodObjectAsBlock
|
||||
each_extension_for(
|
||||
constant_name,
|
||||
namespace,
|
||||
&method(:include)
|
||||
)
|
||||
# rubocop:enable Performance/MethodObjectAsBlock
|
||||
end
|
||||
|
||||
def prepend_mod(with_descendants: false)
|
||||
prepend_mod_with(name, with_descendants: with_descendants)
|
||||
end
|
||||
|
||||
def extend_mod
|
||||
extend_mod_with(name)
|
||||
end
|
||||
|
||||
def include_mod
|
||||
include_mod_with(name)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def prepend_module(mod, with_descendants)
|
||||
prepend(mod)
|
||||
|
||||
descendants.each { |descendant| descendant.prepend(mod) } if with_descendants
|
||||
end
|
||||
|
||||
def each_extension_for(constant_name, namespace)
|
||||
ChatwootApp.extensions.each do |extension_name|
|
||||
extension_namespace =
|
||||
const_get_maybe_false(namespace, extension_name.camelize)
|
||||
|
||||
extension_module =
|
||||
const_get_maybe_false(extension_namespace, constant_name)
|
||||
|
||||
yield(extension_module) if extension_module
|
||||
end
|
||||
end
|
||||
|
||||
def const_get_maybe_false(mod, name)
|
||||
mod&.const_defined?(name, false) && mod&.const_get(name, false)
|
||||
end
|
||||
end
|
||||
|
||||
Module.prepend(InjectEnterpriseEditionModule)
|
||||
20
research/chatwoot/config/initializers/01_redis.rb
Normal file
20
research/chatwoot/config/initializers/01_redis.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
# TODO: Phase out the custom ConnectionPool wrappers ($alfred / $velma),
|
||||
# switch to plain Redis clients here and let Rails 7.1+ handle pooling
|
||||
# via `pool:` in RedisCacheStore (see rack_attack initializer).
|
||||
|
||||
# Alfred
|
||||
# Add here as you use it for more features
|
||||
# Used for Round Robin, Conversation Emails & Online Presence
|
||||
alfred_size = ENV.fetch('REDIS_ALFRED_SIZE', 5)
|
||||
$alfred = ConnectionPool.new(size: alfred_size, timeout: 1) do
|
||||
redis = Rails.env.test? ? MockRedis.new : Redis.new(Redis::Config.app)
|
||||
Redis::Namespace.new('alfred', redis: redis, warning: true)
|
||||
end
|
||||
|
||||
# Velma : Determined protector
|
||||
# used in rack attack
|
||||
velma_size = ENV.fetch('REDIS_VELMA_SIZE', 10)
|
||||
$velma = ConnectionPool.new(size: velma_size, timeout: 1) do
|
||||
config = Rails.env.test? ? MockRedis.new : Redis.new(Redis::Config.app)
|
||||
Redis::Namespace.new('velma', redis: config, warning: true)
|
||||
end
|
||||
15
research/chatwoot/config/initializers/actioncable.rb
Normal file
15
research/chatwoot/config/initializers/actioncable.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'action_cable/subscription_adapter/redis'
|
||||
|
||||
ActionCable::SubscriptionAdapter::Redis.redis_connector = lambda do |config|
|
||||
# For supporting GCP Memorystore where `client` command is disabled.
|
||||
# You can configure the following ENV variable to get your installation working.
|
||||
# ref:
|
||||
# https://github.com/mperham/sidekiq/issues/3518#issuecomment-595611673
|
||||
# https://github.com/redis/redis-rb/issues/767
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75173
|
||||
# https://github.com/rails/rails/blob/4a23cb3415eac03d76623112576559a722d1f23d/actioncable/lib/action_cable/subscription_adapter/base.rb#L30
|
||||
config[:id] = nil if ENV['REDIS_DISABLE_CLIENT_COMMAND'].present?
|
||||
Redis.new(config.except(:adapter, :channel_prefix))
|
||||
end
|
||||
@@ -0,0 +1 @@
|
||||
ActiveRecordQueryTrace.enabled = true if Rails.env.development?
|
||||
23
research/chatwoot/config/initializers/ai_agents.rb
Normal file
23
research/chatwoot/config/initializers/ai_agents.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'agents'
|
||||
|
||||
Rails.application.config.after_initialize do
|
||||
api_key = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_API_KEY')&.value
|
||||
model = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_MODEL')&.value.presence || LlmConstants::DEFAULT_MODEL
|
||||
api_endpoint = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value || LlmConstants::OPENAI_API_ENDPOINT
|
||||
|
||||
if api_key.present?
|
||||
Agents.configure do |config|
|
||||
config.openai_api_key = api_key
|
||||
if api_endpoint.present?
|
||||
api_base = "#{api_endpoint.chomp('/')}/v1"
|
||||
config.openai_api_base = api_base
|
||||
end
|
||||
config.default_model = model
|
||||
config.debug = false
|
||||
end
|
||||
end
|
||||
rescue StandardError => e
|
||||
Rails.logger.error "Failed to configure AI Agents SDK: #{e.message}"
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# ActiveSupport::Reloader.to_prepare do
|
||||
# ApplicationController.renderer.defaults.merge!(
|
||||
# http_host: 'example.org',
|
||||
# https: false
|
||||
# )
|
||||
# end
|
||||
20
research/chatwoot/config/initializers/assets.rb
Normal file
20
research/chatwoot/config/initializers/assets.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
# 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.
|
||||
Rails.application.config.assets.version = '1.0'
|
||||
|
||||
# Add additional assets to the asset load path.
|
||||
# Rails.application.config.assets.paths << Emoji.images_path
|
||||
# Add Yarn node_modules folder to the asset load path.
|
||||
Rails.application.config.assets.paths << Rails.root.join('node_modules')
|
||||
|
||||
# Precompile additional assets.
|
||||
# application.js, application.css, and all non-JS/CSS in the app/assets
|
||||
# folder are already added.
|
||||
# Rails.application.config.assets.precompile += %w( admin.js admin.css )
|
||||
Rails.application.config.assets.precompile += %w[dashboardChart.js]
|
||||
|
||||
# to take care of fonts in assets pre-compiling
|
||||
# Ref: https://stackoverflow.com/questions/56960709/rails-font-cors-policy
|
||||
# https://github.com/rails/sprockets/issues/632#issuecomment-551324428
|
||||
Rails.application.config.assets.precompile << ['*.svg', '*.eot', '*.woff', '*.ttf']
|
||||
5
research/chatwoot/config/initializers/audited.rb
Normal file
5
research/chatwoot/config/initializers/audited.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
# configuration related audited gem : https://github.com/collectiveidea/audited
|
||||
|
||||
Audited.config do |config|
|
||||
config.audit_class = 'Enterprise::AuditLog'
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
# 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.
|
||||
# Rails.backtrace_cleaner.add_silencer { |line| /my_noisy_library/.match?(line) }
|
||||
|
||||
# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
|
||||
# Rails.backtrace_cleaner.remove_silencers!
|
||||
@@ -0,0 +1,36 @@
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Define an application-wide content security policy
|
||||
# For further information see the following documentation
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
||||
|
||||
# Rails.application.config.content_security_policy do |policy|
|
||||
# policy.default_src :self, :https
|
||||
# policy.font_src :self, :https, :data
|
||||
# policy.img_src :self, :https, :data
|
||||
# policy.object_src :none
|
||||
# policy.script_src :self, :https
|
||||
# Allow @vite/client to hot reload javascript changes in development
|
||||
# policy.script_src *policy.script_src, :unsafe_eval, "http://#{ ViteRuby.config.host_with_port }" if Rails.env.development?
|
||||
# You may need to enable this in production as well depending on your setup.
|
||||
# policy.script_src *policy.script_src, :blob if Rails.env.test?
|
||||
# policy.style_src :self, :https
|
||||
# Allow @vite/client to hot reload style changes in development
|
||||
# policy.style_src *policy.style_src, :unsafe_inline if Rails.env.development?
|
||||
# Allow @vite/client to hot reload changes in development
|
||||
# policy.connect_src *policy.connect_src, "ws://#{ ViteRuby.config.host_with_port }" if Rails.env.development?
|
||||
|
||||
# # Specify URI for violation reports
|
||||
# # policy.report_uri "/csp-violation-report-endpoint"
|
||||
# end
|
||||
|
||||
# If you are using UJS then enable automatic nonce generation
|
||||
# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
|
||||
|
||||
# Set the nonce only to specific directives
|
||||
# Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
|
||||
|
||||
# Report CSP violations to a specified URI
|
||||
# For further information see the following documentation:
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
|
||||
# Rails.application.config.content_security_policy_report_only = true
|
||||
@@ -0,0 +1,5 @@
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Specify a serializer for the signed and encrypted cookie jars.
|
||||
# Valid options are :json, :marshal, and :hybrid.
|
||||
Rails.application.config.action_dispatch.cookies_serializer = :json
|
||||
35
research/chatwoot/config/initializers/cors.rb
Normal file
35
research/chatwoot/config/initializers/cors.rb
Normal file
@@ -0,0 +1,35 @@
|
||||
# config/initializers/cors.rb
|
||||
# ref: https://github.com/cyu/rack-cors
|
||||
|
||||
# font cors issue with CDN
|
||||
# Ref: https://stackoverflow.com/questions/56960709/rails-font-cors-policy
|
||||
Rails.application.config.middleware.insert_before 0, Rack::Cors do
|
||||
allow do
|
||||
origins '*'
|
||||
resource '/packs/*', headers: :any, methods: [:get, :options]
|
||||
resource '/audio/*', headers: :any, methods: [:get, :options]
|
||||
# Make the public endpoints accessible to the frontend
|
||||
resource '/public/api/*', headers: :any, methods: :any
|
||||
|
||||
if ActiveModel::Type::Boolean.new.cast(ENV.fetch('CW_API_ONLY_SERVER', false)) || Rails.env.development?
|
||||
resource '*', headers: :any, methods: :any, expose: %w[access-token client uid expiry]
|
||||
end
|
||||
|
||||
if ActiveModel::Type::Boolean.new.cast(ENV.fetch('ENABLE_API_CORS', false))
|
||||
resource '/api/*', headers: :any, methods: :any, expose: %w[access-token client uid expiry]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
################################################
|
||||
######### Action Cable Related Config ##########
|
||||
################################################
|
||||
|
||||
# Mount Action Cable outside main process or domain
|
||||
# Rails.application.config.action_cable.mount_path = nil
|
||||
# Rails.application.config.action_cable.url = 'wss://example.com/cable'
|
||||
# Rails.application.config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
|
||||
|
||||
# To Enable connecting to the API channel public APIs
|
||||
# ref : https://medium.com/@emikaijuin/connecting-to-action-cable-without-rails-d39a8aaa52d5
|
||||
Rails.application.config.action_cable.disable_request_forgery_protection = true
|
||||
@@ -0,0 +1,2 @@
|
||||
Rack::Utils::HTTP_STATUS_CODES[901] = 'Trial Expired'
|
||||
Rack::Utils::HTTP_STATUS_CODES[902] = 'Account Suspended'
|
||||
6
research/chatwoot/config/initializers/datadog.rb
Normal file
6
research/chatwoot/config/initializers/datadog.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
if ENV['DD_TRACE_AGENT_URL'].present?
|
||||
Datadog.configure do |c|
|
||||
# Instrumentation
|
||||
c.tracing.instrument :rails
|
||||
end
|
||||
end
|
||||
274
research/chatwoot/config/initializers/devise.rb
Normal file
274
research/chatwoot/config/initializers/devise.rb
Normal file
@@ -0,0 +1,274 @@
|
||||
# Use this hook to configure devise mailer, warden hooks and so forth.
|
||||
# Many of these configuration options can be set straight in your model.
|
||||
Devise.setup do |config|
|
||||
# The secret key used by Devise. Devise uses this key to generate
|
||||
# random tokens. Changing this key will render invalid all existing
|
||||
# confirmation, reset password and unlock tokens in the database.
|
||||
# Devise will use the `secret_key_base` as its `secret_key`
|
||||
# by default. You can change it below and use your own secret key.
|
||||
# config.secret_key = 'dff4665a082305d28b485d1d763d0d3e52e2577220eaa551836862a3dbca1aade309fe7ceed35180ac494cbc27bd2f5f84d45e1'
|
||||
|
||||
# ==> Mailer Configuration
|
||||
# Configure the e-mail address which will be shown in Devise::Mailer,
|
||||
# note that it will be overwritten if you use your own mailer class
|
||||
# with default "from" parameter.
|
||||
config.mailer_sender = ENV.fetch('MAILER_SENDER_EMAIL', 'Chatwoot <accounts@chatwoot.com>')
|
||||
|
||||
# Configure the class responsible to send e-mails.
|
||||
# config.mailer = 'Devise::Mailer'
|
||||
|
||||
# Configure the parent class responsible to send e-mails.
|
||||
config.parent_mailer = 'ApplicationMailer'
|
||||
|
||||
# ==> ORM configuration
|
||||
# Load and configure the ORM. Supports :active_record (default) and
|
||||
# :mongoid (bson_ext recommended) by default. Other ORMs may be
|
||||
# available as additional gems.
|
||||
require 'devise/orm/active_record'
|
||||
|
||||
# ==> Configuration for any authentication mechanism
|
||||
# Configure which keys are used when authenticating a user. The default is
|
||||
# just :email. You can configure it to use [:username, :subdomain], so for
|
||||
# authenticating a user, both parameters are required. Remember that those
|
||||
# parameters are used only when authenticating and not when retrieving from
|
||||
# session. If you need permissions, you should implement that in a before filter.
|
||||
# You can also supply a hash where the value is a boolean determining whether
|
||||
# or not authentication should be aborted when the value is not present.
|
||||
# config.authentication_keys = [:email]
|
||||
|
||||
# Configure parameters from the request object used for authentication. Each entry
|
||||
# given should be a request method and it will automatically be passed to the
|
||||
# find_for_authentication method and considered in your model lookup. For instance,
|
||||
# if you set :request_keys to [:subdomain], :subdomain will be used on authentication.
|
||||
# The same considerations mentioned for authentication_keys also apply to request_keys.
|
||||
# config.request_keys = []
|
||||
|
||||
# Configure which authentication keys should be case-insensitive.
|
||||
# These keys will be downcased upon creating or modifying a user and when used
|
||||
# to authenticate or find a user. Default is :email.
|
||||
config.case_insensitive_keys = [:email]
|
||||
|
||||
# Configure which authentication keys should have whitespace stripped.
|
||||
# These keys will have whitespace before and after removed upon creating or
|
||||
# modifying a user and when used to authenticate or find a user. Default is :email.
|
||||
config.strip_whitespace_keys = [:email]
|
||||
|
||||
# Tell if authentication through request.params is enabled. True by default.
|
||||
# It can be set to an array that will enable params authentication only for the
|
||||
# given strategies, for example, `config.params_authenticatable = [:database]` will
|
||||
# enable it only for database (email + password) authentication.
|
||||
# config.params_authenticatable = true
|
||||
|
||||
# Tell if authentication through HTTP Auth is enabled. False by default.
|
||||
# It can be set to an array that will enable http authentication only for the
|
||||
# given strategies, for example, `config.http_authenticatable = [:database]` will
|
||||
# enable it only for database authentication. The supported strategies are:
|
||||
# :database = Support basic authentication with authentication key + password
|
||||
# config.http_authenticatable = false
|
||||
|
||||
# If 401 status code should be returned for AJAX requests. True by default.
|
||||
# config.http_authenticatable_on_xhr = true
|
||||
|
||||
# The realm used in Http Basic Authentication. 'Application' by default.
|
||||
# config.http_authentication_realm = 'Application'
|
||||
|
||||
# It will change confirmation, password recovery and other workflows
|
||||
# to behave the same regardless if the e-mail provided was right or wrong.
|
||||
# Does not affect registerable.
|
||||
# config.paranoid = true
|
||||
|
||||
# By default Devise will store the user in session. You can skip storage for
|
||||
# particular strategies by setting this option.
|
||||
# Notice that if you are skipping storage for all authentication paths, you
|
||||
# may want to disable generating routes to Devise's sessions controller by
|
||||
# passing skip: :sessions to `devise_for` in your config/routes.rb
|
||||
config.skip_session_storage = [:http_auth]
|
||||
|
||||
# By default, Devise cleans up the CSRF token on authentication to
|
||||
# avoid CSRF token fixation attacks. This means that, when using AJAX
|
||||
# requests for sign in and sign up, you need to get a new CSRF token
|
||||
# from the server. You can disable this option at your own risk.
|
||||
# config.clean_up_csrf_token_on_authentication = true
|
||||
|
||||
# When false, Devise will not attempt to reload routes on eager load.
|
||||
# This can reduce the time taken to boot the app but if your application
|
||||
# requires the Devise mappings to be loaded during boot time the application
|
||||
# won't boot properly.
|
||||
# config.reload_routes = true
|
||||
|
||||
# ==> Configuration for :database_authenticatable
|
||||
# For bcrypt, this is the cost for hashing the password and defaults to 11. If
|
||||
# using other algorithms, it sets how many times you want the password to be hashed.
|
||||
#
|
||||
# Limiting the stretches to just one in testing will increase the performance of
|
||||
# your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use
|
||||
# a value less than 10 in other environments. Note that, for bcrypt (the default
|
||||
# algorithm), the cost increases exponentially with the number of stretches (e.g.
|
||||
# a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation).
|
||||
config.stretches = Rails.env.test? ? 1 : 11
|
||||
|
||||
# Set up a pepper to generate the hashed password.
|
||||
# config.pepper = '476c6cafcbeb13c8862c8e11eebf80deb085775f2471c15d7e8c8bfe258874701f2f619f5feefdcf593575b2997847de25a6dc57a9838145136de1155e91dce7'
|
||||
|
||||
# Send a notification email when the user's password is changed
|
||||
# config.send_password_change_notification = false
|
||||
|
||||
# ==> Configuration for :confirmable
|
||||
# A period that the user is allowed to access the website even without
|
||||
# confirming their account. For instance, if set to 2.days, the user will be
|
||||
# able to access the website for two days without confirming their account,
|
||||
# access will be blocked just in the third day. Default is 0.days, meaning
|
||||
# the user cannot access the website without confirming their account.
|
||||
# config.allow_unconfirmed_access_for = 2.days
|
||||
|
||||
# A period that the user is allowed to confirm their account before their
|
||||
# token becomes invalid. For example, if set to 3.days, the user can confirm
|
||||
# their account within 3 days after the mail was sent, but on the fourth day
|
||||
# their account can't be confirmed with the token any more.
|
||||
# Default is nil, meaning there is no restriction on how long a user can take
|
||||
# before confirming their account.
|
||||
# config.confirm_within = 3.days
|
||||
|
||||
# If true, requires any email changes to be confirmed (exactly the same way as
|
||||
# initial account confirmation) to be applied. Requires additional unconfirmed_email
|
||||
# db field (see migrations). Until confirmed, new email is stored in
|
||||
# unconfirmed_email column, and copied to email column on successful confirmation.
|
||||
config.reconfirmable = true
|
||||
|
||||
# Defines which key will be used when confirming an account
|
||||
# config.confirmation_keys = [:email]
|
||||
|
||||
# ==> Configuration for :rememberable
|
||||
# The time the user will be remembered without asking for credentials again.
|
||||
# config.remember_for = 2.weeks
|
||||
|
||||
# Invalidates all the remember me tokens when the user signs out.
|
||||
config.expire_all_remember_me_on_sign_out = true
|
||||
|
||||
# If true, extends the user's remember period when remembered via cookie.
|
||||
# config.extend_remember_period = false
|
||||
|
||||
# Options to be passed to the created cookie. For instance, you can set
|
||||
# secure: true in order to force SSL only cookies.
|
||||
# config.rememberable_options = {}
|
||||
|
||||
# ==> Configuration for :validatable
|
||||
# Range for password length.
|
||||
config.password_length = 6..128
|
||||
|
||||
# Email regex used to validate email formats. It simply asserts that
|
||||
# one (and only one) @ exists in the given string. This is mainly
|
||||
# to give user feedback and not to assert the e-mail validity.
|
||||
config.email_regexp = /\A[^@\s]+@[^@\s]+\z/
|
||||
|
||||
# ==> Configuration for :timeoutable
|
||||
# The time you want to timeout the user session without activity. After this
|
||||
# time the user will be asked for credentials again. Default is 30 minutes.
|
||||
# config.timeout_in = 30.minutes
|
||||
|
||||
# ==> Configuration for :lockable
|
||||
# Defines which strategy will be used to lock an account.
|
||||
# :failed_attempts = Locks an account after a number of failed attempts to sign in.
|
||||
# :none = No lock strategy. You should handle locking by yourself.
|
||||
# config.lock_strategy = :failed_attempts
|
||||
|
||||
# Defines which key will be used when locking and unlocking an account
|
||||
# config.unlock_keys = [:email]
|
||||
|
||||
# Defines which strategy will be used to unlock an account.
|
||||
# :email = Sends an unlock link to the user email
|
||||
# :time = Re-enables login after a certain amount of time (see :unlock_in below)
|
||||
# :both = Enables both strategies
|
||||
# :none = No unlock strategy. You should handle unlocking by yourself.
|
||||
# config.unlock_strategy = :both
|
||||
|
||||
# Number of authentication tries before locking an account if lock_strategy
|
||||
# is failed attempts.
|
||||
# config.maximum_attempts = 20
|
||||
|
||||
# Time interval to unlock the account if :time is enabled as unlock_strategy.
|
||||
# config.unlock_in = 1.hour
|
||||
|
||||
# Warn on the last attempt before the account is locked.
|
||||
# config.last_attempt_warning = true
|
||||
|
||||
# ==> Configuration for :recoverable
|
||||
#
|
||||
# Defines which key will be used when recovering the password for an account
|
||||
# config.reset_password_keys = [:email]
|
||||
|
||||
# Time interval you can reset your password with a reset password key.
|
||||
# Don't put a too small interval or your users won't have the time to
|
||||
# change their passwords.
|
||||
config.reset_password_within = 6.hours
|
||||
|
||||
# When set to false, does not sign a user in automatically after their password is
|
||||
# reset. Defaults to true, so a user is signed in automatically after a reset.
|
||||
# config.sign_in_after_reset_password = true
|
||||
|
||||
# ==> Configuration for :encryptable
|
||||
# Allow you to use another hashing or encryption algorithm besides bcrypt (default).
|
||||
# You can use :sha1, :sha512 or algorithms from others authentication tools as
|
||||
# :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20
|
||||
# for default behavior) and :restful_authentication_sha1 (then you should set
|
||||
# stretches to 10, and copy REST_AUTH_SITE_KEY to pepper).
|
||||
#
|
||||
# Require the `devise-encryptable` gem when using anything other than bcrypt
|
||||
# config.encryptor = :sha512
|
||||
|
||||
# ==> Scopes configuration
|
||||
# Turn scoped views on. Before rendering "sessions/new", it will first check for
|
||||
# "users/sessions/new". It's turned off by default because it's slower if you
|
||||
# are using only default views.
|
||||
config.scoped_views = true
|
||||
|
||||
# Configure the default scope given to Warden. By default it's the first
|
||||
# devise role declared in your routes (usually :user).
|
||||
config.default_scope = :user
|
||||
|
||||
# Set this configuration to false if you want /users/sign_out to sign out
|
||||
# only the current scope. By default, Devise signs out all scopes.
|
||||
config.sign_out_all_scopes = true
|
||||
|
||||
# ==> Navigation configuration
|
||||
# Lists the formats that should be treated as navigational. Formats like
|
||||
# :html, should redirect to the sign in page when the user does not have
|
||||
# access, but formats like :xml or :json, should return 401.
|
||||
#
|
||||
# If you have any extra navigational formats, like :iphone or :mobile, you
|
||||
# should add them to the navigational formats lists.
|
||||
#
|
||||
# The "*/*" below is required to match Internet Explorer requests.
|
||||
# config.navigational_formats = ['*/*', :html]
|
||||
|
||||
# The default HTTP method used to sign out a resource. Default is :delete.
|
||||
config.sign_out_via = :delete
|
||||
|
||||
# ==> OmniAuth
|
||||
# Add a new OmniAuth provider. Check the wiki for more information on setting
|
||||
# up on your models and hooks.
|
||||
# config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
|
||||
|
||||
# ==> Warden configuration
|
||||
# If you want to use other strategies, that are not supported by Devise, or
|
||||
# change the failure app, you can configure them inside the config.warden block.
|
||||
#
|
||||
# config.warden do |manager|
|
||||
# manager.intercept_401 = false
|
||||
# manager.default_strategies(scope: :user).unshift :some_external_strategy
|
||||
# end
|
||||
|
||||
# ==> Mountable engine configurations
|
||||
# When using Devise inside an engine, let's call it `MyEngine`, and this engine
|
||||
# is mountable, there are some extra configurations to be taken into account.
|
||||
# The following options are available, assuming the engine is mounted as:
|
||||
#
|
||||
# mount MyEngine, at: '/my_engine'
|
||||
#
|
||||
# The router that invoked `devise_for`, in the example above, would be:
|
||||
# config.router_name = :my_engine
|
||||
#
|
||||
# When using OmniAuth, Devise cannot automatically set OmniAuth path,
|
||||
# so you need to do it manually. For the users scope, it would be:
|
||||
# config.omniauth_path_prefix = '/my_engine/users/auth'
|
||||
end
|
||||
52
research/chatwoot/config/initializers/devise_token_auth.rb
Normal file
52
research/chatwoot/config/initializers/devise_token_auth.rb
Normal file
@@ -0,0 +1,52 @@
|
||||
DeviseTokenAuth.setup do |config|
|
||||
# By default the authorization headers will change after each request. The
|
||||
# client is responsible for keeping track of the changing tokens. Change
|
||||
# this to false to prevent the Authorization header from changing after
|
||||
# each request.
|
||||
config.change_headers_on_each_request = false
|
||||
|
||||
# By default, users will need to re-authenticate after 2 weeks. This setting
|
||||
# determines how long tokens will remain valid after they are issued.
|
||||
config.token_lifespan = 2.months
|
||||
|
||||
# By default, old tokens are not invalidated when password is changed.
|
||||
# Enable this option if you want to make passwords updates to logout other devices.
|
||||
config.remove_tokens_after_password_reset = true
|
||||
|
||||
# Sets the max number of concurrent devices per user, which is 10 by default.
|
||||
# After this limit is reached, the oldest tokens will be removed.
|
||||
config.max_number_of_devices = 25
|
||||
|
||||
# Sometimes it's necessary to make several requests to the API at the same
|
||||
# time. In this case, each request in the batch will need to share the same
|
||||
# auth token. This setting determines how far apart the requests can be while
|
||||
# still using the same auth token.
|
||||
# config.batch_request_buffer_throttle = 5.seconds
|
||||
|
||||
# This route will be the prefix for all oauth2 redirect callbacks. For
|
||||
# example, using the default '/omniauth', the github oauth2 provider will
|
||||
# redirect successful authentications to '/omniauth/github/callback'
|
||||
# config.omniauth_prefix = "/omniauth"
|
||||
|
||||
# By default sending current password is not needed for the password update.
|
||||
# Uncomment to enforce current_password param to be checked before all
|
||||
# attribute updates. Set it to :password if you want it to be checked only if
|
||||
# password is updated.
|
||||
# config.check_current_password_before_update = :attributes
|
||||
|
||||
# By default we will use callbacks for single omniauth.
|
||||
# It depends on fields like email, provider and uid.
|
||||
# config.default_callbacks = true
|
||||
|
||||
# Makes it possible to change the headers names
|
||||
# config.headers_names = {:'access-token' => 'access-token',
|
||||
# :'client' => 'client',
|
||||
# :'expiry' => 'expiry',
|
||||
# :'uid' => 'uid',
|
||||
# :'token-type' => 'token-type' }
|
||||
|
||||
# By default, only Bearer Token authentication is implemented out of the box.
|
||||
# If, however, you wish to integrate with legacy Devise authentication, you can
|
||||
# do so by enabling this flag. NOTE: This feature is highly experimental!
|
||||
# config.enable_standard_devise_support = false
|
||||
end
|
||||
6
research/chatwoot/config/initializers/event_handlers.rb
Normal file
6
research/chatwoot/config/initializers/event_handlers.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
Rails.application.configure do
|
||||
config.to_prepare do
|
||||
Rails.configuration.dispatcher = Dispatcher.instance
|
||||
Rails.configuration.dispatcher.load_listeners
|
||||
end
|
||||
end
|
||||
46
research/chatwoot/config/initializers/facebook_messenger.rb
Normal file
46
research/chatwoot/config/initializers/facebook_messenger.rb
Normal file
@@ -0,0 +1,46 @@
|
||||
# ref: https://github.com/jgorset/facebook-messenger#make-a-configuration-provider
|
||||
class ChatwootFbProvider < Facebook::Messenger::Configuration::Providers::Base
|
||||
def valid_verify_token?(_verify_token)
|
||||
GlobalConfigService.load('FB_VERIFY_TOKEN', '')
|
||||
end
|
||||
|
||||
def app_secret_for(_page_id)
|
||||
GlobalConfigService.load('FB_APP_SECRET', '')
|
||||
end
|
||||
|
||||
def access_token_for(page_id)
|
||||
Channel::FacebookPage.where(page_id: page_id).last.page_access_token
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def bot
|
||||
Chatwoot::Bot
|
||||
end
|
||||
end
|
||||
|
||||
Rails.application.reloader.to_prepare do
|
||||
Facebook::Messenger.configure do |config|
|
||||
config.provider = ChatwootFbProvider.new
|
||||
end
|
||||
|
||||
Facebook::Messenger::Bot.on :message do |message|
|
||||
Webhooks::FacebookEventsJob.perform_later(message.to_json)
|
||||
end
|
||||
|
||||
Facebook::Messenger::Bot.on :delivery do |delivery|
|
||||
Rails.logger.info "Recieved delivery status #{delivery.to_json}"
|
||||
Webhooks::FacebookDeliveryJob.perform_later(delivery.to_json)
|
||||
end
|
||||
|
||||
Facebook::Messenger::Bot.on :read do |read|
|
||||
Rails.logger.info "Recieved read status #{read.to_json}"
|
||||
Webhooks::FacebookDeliveryJob.perform_later(read.to_json)
|
||||
end
|
||||
|
||||
Facebook::Messenger::Bot.on :message_echo do |message|
|
||||
# Add delay to prevent race condition where echo arrives before send message API completes
|
||||
# This avoids duplicate messages when echo comes early during API processing
|
||||
Webhooks::FacebookEventsJob.set(wait: 2.seconds).perform_later(message.to_json)
|
||||
end
|
||||
end
|
||||
11
research/chatwoot/config/initializers/feature_policy.rb
Normal file
11
research/chatwoot/config/initializers/feature_policy.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# Define an application-wide HTTP feature policy. For further
|
||||
# information see https://developers.google.com/web/updates/2018/06/feature-policy
|
||||
#
|
||||
# Rails.application.config.feature_policy do |f|
|
||||
# f.camera :none
|
||||
# f.gyroscope :none
|
||||
# f.microphone :none
|
||||
# f.usb :none
|
||||
# f.fullscreen :self
|
||||
# f.payment :self, "https://secure.example.com"
|
||||
# end
|
||||
@@ -0,0 +1,13 @@
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Configure sensitive parameters which will be filtered from the log file.
|
||||
Rails.application.config.filter_parameters += [
|
||||
:password, :secret, :_key, :auth, :crypt, :salt, :certificate, :otp, :access, :private, :protected, :ssn,
|
||||
:otp_secret, :otp_code, :backup_code, :mfa_token, :otp_backup_codes
|
||||
]
|
||||
|
||||
# Regex to filter all occurrences of 'token' in keys except for 'website_token'
|
||||
filter_regex = /\A(?!.*\bwebsite_token\b).*token/i
|
||||
|
||||
# Apply the regex for filtering
|
||||
Rails.application.config.filter_parameters += [filter_regex]
|
||||
30
research/chatwoot/config/initializers/geocoder.rb
Normal file
30
research/chatwoot/config/initializers/geocoder.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
# Geocoding options
|
||||
# timeout: 3, # geocoding service timeout (secs)
|
||||
# lookup: :nominatim, # name of geocoding service (symbol)
|
||||
# ip_lookup: :ipinfo_io, # name of IP address geocoding service (symbol)
|
||||
# language: :en, # ISO-639 language code
|
||||
# use_https: false, # use HTTPS for lookup requests? (if supported)
|
||||
# http_proxy: nil, # HTTP proxy server (user:pass@host:port)
|
||||
# https_proxy: nil, # HTTPS proxy server (user:pass@host:port)
|
||||
# api_key: nil, # API key for geocoding service
|
||||
# cache: nil, # cache object (must respond to #[], #[]=, and #del)
|
||||
# cache_prefix: 'geocoder:', # prefix (string) to use for all cache keys
|
||||
|
||||
# Exceptions that should not be rescued by default
|
||||
# (if you want to implement custom error handling);
|
||||
# supports SocketError and Timeout::Error
|
||||
# always_raise: [],
|
||||
|
||||
# Calculation options
|
||||
# units: :mi, # :km for kilometers or :mi for miles
|
||||
# distances: :linear # :spherical or :linear
|
||||
|
||||
module GeocoderConfiguration
|
||||
LOOK_UP_DB = Rails.root.join('vendor/db/GeoLiteCity.mmdb')
|
||||
end
|
||||
|
||||
Geocoder.configure(ip_lookup: :geoip2, geoip2: { file: GeocoderConfiguration::LOOK_UP_DB }) if ENV['IP_LOOKUP_API_KEY'].present?
|
||||
|
||||
Rails.application.config.after_initialize do
|
||||
Geocoder::SetupService.new.perform
|
||||
end
|
||||
16
research/chatwoot/config/initializers/git_sha.rb
Normal file
16
research/chatwoot/config/initializers/git_sha.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
# Define a method to fetch the git commit hash
|
||||
def fetch_git_sha
|
||||
sha = `git rev-parse HEAD` if File.directory?('.git')
|
||||
if sha.present?
|
||||
sha.strip
|
||||
elsif File.exist?('.git_sha')
|
||||
File.read('.git_sha').strip
|
||||
# This is for Heroku. Ensure heroku labs:enable runtime-dyno-metadata is turned on.
|
||||
elsif ENV.fetch('HEROKU_SLUG_COMMIT', nil).present?
|
||||
ENV.fetch('HEROKU_SLUG_COMMIT', nil)
|
||||
else
|
||||
'unknown'
|
||||
end
|
||||
end
|
||||
|
||||
GIT_HASH = fetch_git_sha
|
||||
16
research/chatwoot/config/initializers/inflections.rb
Normal file
16
research/chatwoot/config/initializers/inflections.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Add new inflection rules using the following format. Inflections
|
||||
# are locale specific, and you may define rules for as many different
|
||||
# locales as you wish. All of these examples are active by default:
|
||||
# ActiveSupport::Inflector.inflections(:en) do |inflect|
|
||||
# inflect.plural /^(ox)$/i, '\1en'
|
||||
# inflect.singular /^(ox)en/i, '\1'
|
||||
# inflect.irregular 'person', 'people'
|
||||
# inflect.uncountable %w( fish sheep )
|
||||
# end
|
||||
|
||||
# These inflection rules are supported but not enabled by default:
|
||||
# ActiveSupport::Inflector.inflections(:en) do |inflect|
|
||||
# inflect.acronym 'RESTful'
|
||||
# end
|
||||
48
research/chatwoot/config/initializers/languages.rb
Normal file
48
research/chatwoot/config/initializers/languages.rb
Normal file
@@ -0,0 +1,48 @@
|
||||
# Based on ISO_639-3 Codes. ref: https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
|
||||
# This Hash is used in account model, so do not change the index for existing languages
|
||||
|
||||
LANGUAGES_CONFIG = {
|
||||
0 => { name: 'English (en)', iso_639_3_code: 'eng', iso_639_1_code: 'en', enabled: true },
|
||||
1 => { name: 'العربية (ar)', iso_639_3_code: 'ara', iso_639_1_code: 'ar', enabled: true },
|
||||
2 => { name: 'Nederlands (nl) ', iso_639_3_code: 'nld', iso_639_1_code: 'nl', enabled: true },
|
||||
3 => { name: 'Français (fr)', iso_639_3_code: 'fra', iso_639_1_code: 'fr', enabled: true },
|
||||
4 => { name: 'Deutsch (de)', iso_639_3_code: 'deu', iso_639_1_code: 'de', enabled: true },
|
||||
5 => { name: 'हिन्दी (hi)', iso_639_3_code: 'hin', iso_639_1_code: 'hi', enabled: false },
|
||||
6 => { name: 'Italiano (it)', iso_639_3_code: 'ita', iso_639_1_code: 'it', enabled: true },
|
||||
7 => { name: '日本語 (ja)', iso_639_3_code: 'jpn', iso_639_1_code: 'ja', enabled: true },
|
||||
8 => { name: '한국어 (ko)', iso_639_3_code: 'kor', iso_639_1_code: 'ko', enabled: true },
|
||||
9 => { name: 'Português (pt)', iso_639_3_code: 'por', iso_639_1_code: 'pt', enabled: true },
|
||||
10 => { name: 'русский (ru)', iso_639_3_code: 'rus', iso_639_1_code: 'ru', enabled: true },
|
||||
11 => { name: '中文 (zh)', iso_639_3_code: 'zho', iso_639_1_code: 'zh', enabled: false },
|
||||
12 => { name: 'Español (es)', iso_639_3_code: 'spa', iso_639_1_code: 'es', enabled: true },
|
||||
13 => { name: 'മലയാളം (ml)', iso_639_3_code: 'mal', iso_639_1_code: 'ml', enabled: true },
|
||||
14 => { name: 'Català (ca)', iso_639_3_code: 'cat', iso_639_1_code: 'ca', enabled: true },
|
||||
15 => { name: 'ελληνικά (el)', iso_639_3_code: 'ell', iso_639_1_code: 'el', enabled: true },
|
||||
16 => { name: 'Português Brasileiro (pt-BR)', iso_639_3_code: '', iso_639_1_code: 'pt_BR', enabled: true },
|
||||
17 => { name: 'Română (ro)', iso_639_3_code: 'ron', iso_639_1_code: 'ro', enabled: true },
|
||||
18 => { name: 'தமிழ் (ta)', iso_639_3_code: 'tam', iso_639_1_code: 'ta', enabled: true },
|
||||
19 => { name: 'فارسی (fa)', iso_639_3_code: 'fas', iso_639_1_code: 'fa', enabled: true },
|
||||
20 => { name: '中文 (台湾) (zh-TW)', iso_639_3_code: 'zho', iso_639_1_code: 'zh_TW', enabled: true },
|
||||
21 => { name: 'Tiếng Việt (vi)', iso_639_3_code: 'vie', iso_639_1_code: 'vi', enabled: true },
|
||||
22 => { name: 'dansk (da)', iso_639_3_code: 'dan', iso_639_1_code: 'da', enabled: true },
|
||||
23 => { name: 'Türkçe (tr)', iso_639_3_code: 'tur', iso_639_1_code: 'tr', enabled: true },
|
||||
24 => { name: 'čeština (cs)', iso_639_3_code: 'ces', iso_639_1_code: 'cs', enabled: true },
|
||||
25 => { name: 'suomi, suomen kieli (fi)', iso_639_3_code: 'fin', iso_639_1_code: 'fi', enabled: true },
|
||||
26 => { name: 'Bahasa Indonesia (id)', iso_639_3_code: 'ind', iso_639_1_code: 'id', enabled: true },
|
||||
27 => { name: 'Svenska (sv)', iso_639_3_code: 'swe', iso_639_1_code: 'sv', enabled: true },
|
||||
28 => { name: 'magyar nyelv (hu)', iso_639_3_code: 'hun', iso_639_1_code: 'hu', enabled: true },
|
||||
29 => { name: 'norsk (no)', iso_639_3_code: 'nor', iso_639_1_code: 'no', enabled: true },
|
||||
30 => { name: '中文 (zh-CN)', iso_639_3_code: 'zho', iso_639_1_code: 'zh_CN', enabled: true },
|
||||
31 => { name: 'język polski (pl)', iso_639_3_code: 'pol', iso_639_1_code: 'pl', enabled: true },
|
||||
32 => { name: 'slovenčina (sk)', iso_639_3_code: 'slk', iso_639_1_code: 'sk', enabled: true },
|
||||
33 => { name: 'украї́нська мо́ва (uk)', iso_639_3_code: 'ukr', iso_639_1_code: 'uk', enabled: true },
|
||||
34 => { name: 'ภาษาไทย (th)', iso_639_3_code: 'tha', iso_639_1_code: 'th', enabled: true },
|
||||
35 => { name: 'latviešu valoda (lv)', iso_639_3_code: 'lav', iso_639_1_code: 'lv', enabled: true },
|
||||
36 => { name: 'íslenska (is)', iso_639_3_code: 'isl', iso_639_1_code: 'is', enabled: true },
|
||||
37 => { name: 'עִברִית (he)', iso_639_3_code: 'heb', iso_639_1_code: 'he', enabled: true },
|
||||
38 => { name: 'lietuvių (lt)', iso_639_3_code: 'lit', iso_639_1_code: 'lt', enabled: true },
|
||||
39 => { name: 'Српски (sr)', iso_639_3_code: 'srp', iso_639_1_code: 'sr', enabled: true },
|
||||
40 => { name: 'български (bg)', iso_639_3_code: 'bul', iso_639_1_code: 'bg', enabled: true }
|
||||
}.filter { |_key, val| val[:enabled] }.freeze
|
||||
|
||||
Rails.configuration.i18n.available_locales = LANGUAGES_CONFIG.map { |_index, lang| lang[:iso_639_1_code].to_sym }
|
||||
3
research/chatwoot/config/initializers/liquid_handler.rb
Normal file
3
research/chatwoot/config/initializers/liquid_handler.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
require Rails.root.join('lib/action_view/template/handlers/liquid')
|
||||
|
||||
ActionView::Template.register_template_handler :liquid, ActionView::Template::Handlers::Liquid
|
||||
30
research/chatwoot/config/initializers/lograge.rb
Normal file
30
research/chatwoot/config/initializers/lograge.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
if ActiveModel::Type::Boolean.new.cast(ENV.fetch('LOGRAGE_ENABLED', false)).present?
|
||||
require 'lograge'
|
||||
|
||||
Rails.application.configure do
|
||||
config.lograge.enabled = true
|
||||
config.lograge.formatter = Lograge::Formatters::Json.new
|
||||
config.lograge.custom_payload do |controller|
|
||||
# We only need user_id for API requests
|
||||
# might error out for other controller - ref: https://github.com/chatwoot/chatwoot/issues/6922
|
||||
user_id = controller&.try(:current_user)&.id if controller.is_a?(Api::BaseController) && controller&.try(:current_user).is_a?(User)
|
||||
{
|
||||
host: controller.request.host,
|
||||
remote_ip: controller.request.remote_ip,
|
||||
user_id: user_id
|
||||
}
|
||||
end
|
||||
|
||||
config.lograge.custom_options = lambda do |event|
|
||||
param_exceptions = %w[controller action format id]
|
||||
{
|
||||
params: event.payload[:params]&.except(*param_exceptions)
|
||||
}
|
||||
end
|
||||
|
||||
config.lograge.ignore_custom = lambda do |event|
|
||||
# ignore update_presence events in log
|
||||
return true if event.payload[:channel_class] == 'RoomChannel'
|
||||
end
|
||||
end
|
||||
end
|
||||
54
research/chatwoot/config/initializers/mailer.rb
Normal file
54
research/chatwoot/config/initializers/mailer.rb
Normal file
@@ -0,0 +1,54 @@
|
||||
Rails.application.configure do
|
||||
#########################################
|
||||
# Configuration Related to Action Mailer
|
||||
#########################################
|
||||
|
||||
# We need the application frontend url to be used in our emails
|
||||
config.action_mailer.default_url_options = { host: ENV['FRONTEND_URL'] } if ENV['FRONTEND_URL'].present?
|
||||
# We load certain mailer templates from our database. This ensures changes to it is reflected immediately
|
||||
config.action_mailer.perform_caching = false
|
||||
config.action_mailer.perform_deliveries = true
|
||||
config.action_mailer.raise_delivery_errors = true
|
||||
|
||||
# Config related to smtp
|
||||
smtp_settings = {
|
||||
address: ENV.fetch('SMTP_ADDRESS', 'localhost'),
|
||||
port: ENV.fetch('SMTP_PORT', 587)
|
||||
}
|
||||
|
||||
smtp_settings[:authentication] = ENV.fetch('SMTP_AUTHENTICATION', 'login').to_sym if ENV['SMTP_AUTHENTICATION'].present?
|
||||
smtp_settings[:domain] = ENV['SMTP_DOMAIN'] if ENV['SMTP_DOMAIN'].present?
|
||||
smtp_settings[:user_name] = ENV.fetch('SMTP_USERNAME', nil)
|
||||
smtp_settings[:password] = ENV.fetch('SMTP_PASSWORD', nil)
|
||||
smtp_settings[:enable_starttls_auto] = ActiveModel::Type::Boolean.new.cast(ENV.fetch('SMTP_ENABLE_STARTTLS_AUTO', true))
|
||||
smtp_settings[:openssl_verify_mode] = ENV['SMTP_OPENSSL_VERIFY_MODE'] if ENV['SMTP_OPENSSL_VERIFY_MODE'].present?
|
||||
smtp_settings[:ssl] = ActiveModel::Type::Boolean.new.cast(ENV.fetch('SMTP_SSL', true)) if ENV['SMTP_SSL']
|
||||
smtp_settings[:tls] = ActiveModel::Type::Boolean.new.cast(ENV.fetch('SMTP_TLS', true)) if ENV['SMTP_TLS']
|
||||
smtp_settings[:open_timeout] = ENV['SMTP_OPEN_TIMEOUT'].to_i if ENV['SMTP_OPEN_TIMEOUT'].present?
|
||||
smtp_settings[:read_timeout] = ENV['SMTP_READ_TIMEOUT'].to_i if ENV['SMTP_READ_TIMEOUT'].present?
|
||||
|
||||
config.action_mailer.delivery_method = :smtp unless Rails.env.test?
|
||||
config.action_mailer.smtp_settings = smtp_settings
|
||||
|
||||
# Use sendmail if using postfix for email
|
||||
config.action_mailer.delivery_method = :sendmail if ENV['SMTP_ADDRESS'].blank?
|
||||
|
||||
# You can use letter opener for your local development by setting the environment variable
|
||||
config.action_mailer.delivery_method = :letter_opener if Rails.env.development? && ENV['LETTER_OPENER']
|
||||
|
||||
#########################################
|
||||
# Configuration Related to Action MailBox
|
||||
#########################################
|
||||
|
||||
# Set this to appropriate ingress service for which the options are :
|
||||
# :relay for Exim, Postfix, Qmail
|
||||
# :mailgun for Mailgun
|
||||
# :mandrill for Mandrill
|
||||
# :postmark for Postmark
|
||||
# :sendgrid for Sendgrid
|
||||
# :ses for Amazon SES
|
||||
config.action_mailbox.ingress = ENV.fetch('RAILS_INBOUND_EMAIL_SERVICE', 'relay').to_sym
|
||||
|
||||
# Amazon SES ActionMailbox configuration
|
||||
config.action_mailbox.ses.subscribed_topic = ENV['ACTION_MAILBOX_SES_SNS_TOPIC'] if ENV['ACTION_MAILBOX_SES_SNS_TOPIC'].present?
|
||||
end
|
||||
4
research/chatwoot/config/initializers/mime_types.rb
Normal file
4
research/chatwoot/config/initializers/mime_types.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Add new mime types for use in respond_to blocks:
|
||||
# Mime::Type.register "text/richtext", :rtf
|
||||
21
research/chatwoot/config/initializers/monkey_patches/chat.rb
Normal file
21
research/chatwoot/config/initializers/monkey_patches/chat.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
module Slack
|
||||
module Web
|
||||
module Api
|
||||
module Endpoints
|
||||
module Chat
|
||||
# TODO: Remove this monkey patch when PR for this issue https://github.com/slack-ruby/slack-ruby-client/issues/388 is merged
|
||||
def chat_unfurl(options = {})
|
||||
if (options[:channel].nil? || options[:ts].nil?) && (options[:unfurl_id].nil? || options[:source].nil?)
|
||||
raise ArgumentError, 'Either a combination of :channel and :ts or :unfurl_id and :source is required'
|
||||
end
|
||||
|
||||
raise ArgumentError, 'Required arguments :unfurls missing' if options[:unfurls].nil?
|
||||
|
||||
options = options.merge(channel: conversations_id(options)['channel']['id']) if options[:channel]
|
||||
post('chat.unfurl', options)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
# When working with experimental extensions, which doesn't have support on all providers
|
||||
# This monkey patch will help us to ignore the extensions when dumping the schema
|
||||
# Additionally we will also ignore the tables associated with those features and exentions
|
||||
|
||||
# Once the feature stabilizes, we can remove the tables/extension from the ignore list
|
||||
# Ensure you write appropriate migrations when you do that.
|
||||
|
||||
module ActiveRecord
|
||||
module ConnectionAdapters
|
||||
module PostgreSQL
|
||||
class SchemaDumper < ConnectionAdapters::SchemaDumper
|
||||
cattr_accessor :ignore_extentions, default: []
|
||||
|
||||
private
|
||||
|
||||
def extensions(stream)
|
||||
extensions = @connection.extensions
|
||||
return unless extensions.any?
|
||||
|
||||
stream.puts ' # These extensions should be enabled to support this database'
|
||||
extensions.sort.each do |extension|
|
||||
stream.puts " enable_extension #{extension.inspect}" unless ignore_extentions.include?(extension)
|
||||
end
|
||||
stream.puts
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
9
research/chatwoot/config/initializers/omniauth.rb
Normal file
9
research/chatwoot/config/initializers/omniauth.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
# OmniAuth configuration
|
||||
# Sets the full host URL for callbacks and proper redirect handling
|
||||
OmniAuth.config.full_host = ENV.fetch('FRONTEND_URL', 'http://localhost:3000')
|
||||
|
||||
Rails.application.config.middleware.use OmniAuth::Builder do
|
||||
provider :google_oauth2, ENV.fetch('GOOGLE_OAUTH_CLIENT_ID', nil), ENV.fetch('GOOGLE_OAUTH_CLIENT_SECRET', nil), {
|
||||
provider_ignores_state: true
|
||||
}
|
||||
end
|
||||
11
research/chatwoot/config/initializers/permissions_policy.rb
Normal file
11
research/chatwoot/config/initializers/permissions_policy.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# Define an application-wide HTTP permissions policy. For further
|
||||
# information see https://developers.google.com/web/updates/2018/06/feature-policy
|
||||
#
|
||||
# Rails.application.config.permissions_policy do |f|
|
||||
# f.camera :none
|
||||
# f.gyroscope :none
|
||||
# f.microphone :none
|
||||
# f.usb :none
|
||||
# f.fullscreen :self
|
||||
# f.payment :self, "https://secure.example.com"
|
||||
# end
|
||||
267
research/chatwoot/config/initializers/rack_attack.rb
Normal file
267
research/chatwoot/config/initializers/rack_attack.rb
Normal file
@@ -0,0 +1,267 @@
|
||||
class Rack::Attack
|
||||
### Configure Cache ###
|
||||
|
||||
# If you don't want to use Rails.cache (Rack::Attack's default), then
|
||||
# configure it here.
|
||||
#
|
||||
# Note: The store is only used for throttling (not blocklisting and
|
||||
# safelisting). It must implement .increment and .write like
|
||||
# ActiveSupport::Cache::Store
|
||||
|
||||
# Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
|
||||
|
||||
# https://github.com/rack/rack-attack/issues/102
|
||||
# Rails 7.1 automatically adds its own ConnectionPool around RedisCacheStore.
|
||||
# Because `$velma` is *already* a ConnectionPool, double-wrapping causes
|
||||
# Redis calls like `get` to hit the outer wrapper and explode.
|
||||
# `pool: false` tells Rails to skip its internal pool and use ours directly.
|
||||
# TODO: We can use build in connection pool in future upgrade
|
||||
Rack::Attack.cache.store = ActiveSupport::Cache::RedisCacheStore.new(redis: $velma, pool: false)
|
||||
|
||||
class Request < ::Rack::Request
|
||||
# You many need to specify a method to fetch the correct remote IP address
|
||||
# if the web server is behind a load balancer.
|
||||
def remote_ip
|
||||
@remote_ip ||= (env['action_dispatch.remote_ip'] || ip).to_s
|
||||
end
|
||||
|
||||
def allowed_ip?
|
||||
default_allowed_ips = ['127.0.0.1', '::1']
|
||||
env_allowed_ips = ENV.fetch('RACK_ATTACK_ALLOWED_IPS', '').split(',').map(&:strip)
|
||||
(default_allowed_ips + env_allowed_ips).include?(remote_ip)
|
||||
end
|
||||
|
||||
# Rails would allow requests to paths with extentions, so lets compare against the path with extention stripped
|
||||
# example /auth & /auth.json would both work
|
||||
def path_without_extentions
|
||||
path[/^[^.]+/]
|
||||
end
|
||||
end
|
||||
|
||||
### Safelist IPs from Environment Variable ###
|
||||
#
|
||||
# This block ensures requests from any IP present in RACK_ATTACK_ALLOWED_IPS
|
||||
# will bypass Rack::Attack’s throttling rules.
|
||||
#
|
||||
# Example: RACK_ATTACK_ALLOWED_IPS="127.0.0.1,::1,192.168.0.10"
|
||||
|
||||
Rack::Attack.safelist('trusted IPs', &:allowed_ip?)
|
||||
|
||||
# Safelist health check endpoint so it never touches Redis for throttle tracking.
|
||||
# This keeps /health fully dependency-free for ALB liveness checks.
|
||||
Rack::Attack.safelist('health check') do |req|
|
||||
req.path == '/health'
|
||||
end
|
||||
|
||||
### Throttle Spammy Clients ###
|
||||
|
||||
# If any single client IP is making tons of requests, then they're
|
||||
# probably malicious or a poorly-configured scraper. Either way, they
|
||||
# don't deserve to hog all of the app server's CPU. Cut them off!
|
||||
#
|
||||
# Note: If you're serving assets through rack, those requests may be
|
||||
# counted by rack-attack and this throttle may be activated too
|
||||
# quickly. If so, enable the condition to exclude them from tracking.
|
||||
|
||||
# Throttle all requests by IP (60rpm)
|
||||
#
|
||||
# Key: "rack::attack:#{Time.now.to_i/:period}:req/ip:#{req.ip}"
|
||||
|
||||
throttle('req/ip', limit: ENV.fetch('RACK_ATTACK_LIMIT', '3000').to_i, period: 1.minute, &:ip)
|
||||
|
||||
###-----------------------------------------------###
|
||||
###-----Authentication Related Throttling---------###
|
||||
###-----------------------------------------------###
|
||||
|
||||
### Prevent Brute-Force Super Admin Login Attacks ###
|
||||
throttle('super_admin_login/ip', limit: 5, period: 5.minutes) do |req|
|
||||
req.ip if req.path_without_extentions == '/super_admin/sign_in' && req.post?
|
||||
end
|
||||
|
||||
throttle('super_admin_login/email', limit: 5, period: 15.minutes) do |req|
|
||||
if req.path_without_extentions == '/super_admin/sign_in' && req.post?
|
||||
# NOTE: This line used to throw ArgumentError /rails/action_mailbox/sendgrid/inbound_emails : invalid byte sequence in UTF-8
|
||||
# Hence placed in the if block
|
||||
# ref: https://github.com/rack/rack-attack/issues/399
|
||||
email = req.params['email'].presence || ActionDispatch::Request.new(req.env).params['email'].presence
|
||||
email.to_s.downcase.gsub(/\s+/, '')
|
||||
end
|
||||
end
|
||||
|
||||
# ### Prevent Brute-Force Login Attacks ###
|
||||
# Exclude MFA verification attempts from regular login throttling
|
||||
throttle('login/ip', limit: 5, period: 5.minutes) do |req|
|
||||
if req.path_without_extentions == '/auth/sign_in' && req.post? && req.params['mfa_token'].blank?
|
||||
# Skip if this is an MFA verification request
|
||||
req.ip
|
||||
end
|
||||
end
|
||||
|
||||
throttle('login/email', limit: 10, period: 15.minutes) do |req|
|
||||
# Skip if this is an MFA verification request
|
||||
if req.path_without_extentions == '/auth/sign_in' && req.post? && req.params['mfa_token'].blank?
|
||||
# ref: https://github.com/rack/rack-attack/issues/399
|
||||
# NOTE: This line used to throw ArgumentError /rails/action_mailbox/sendgrid/inbound_emails : invalid byte sequence in UTF-8
|
||||
# Hence placed in the if block
|
||||
email = req.params['email'].presence || ActionDispatch::Request.new(req.env).params['email'].presence
|
||||
email.to_s.downcase.gsub(/\s+/, '')
|
||||
end
|
||||
end
|
||||
|
||||
## Reset password throttling
|
||||
throttle('reset_password/ip', limit: 5, period: 30.minutes) do |req|
|
||||
req.ip if req.path_without_extentions == '/auth/password' && req.post?
|
||||
end
|
||||
|
||||
throttle('reset_password/email', limit: 5, period: 1.hour) do |req|
|
||||
if req.path_without_extentions == '/auth/password' && req.post?
|
||||
email = req.params['email'].presence || ActionDispatch::Request.new(req.env).params['email'].presence
|
||||
email.to_s.downcase.gsub(/\s+/, '')
|
||||
end
|
||||
end
|
||||
|
||||
## Resend confirmation throttling
|
||||
throttle('resend_confirmation/ip', limit: 5, period: 30.minutes) do |req|
|
||||
req.ip if req.path_without_extentions == '/api/v1/profile/resend_confirmation' && req.post?
|
||||
end
|
||||
|
||||
## MFA throttling - prevent brute force attacks
|
||||
throttle('mfa_verification/ip', limit: 5, period: 1.minute) do |req|
|
||||
if req.path_without_extentions == '/api/v1/profile/mfa'
|
||||
req.ip if req.delete? # Throttle disable attempts
|
||||
elsif req.path_without_extentions.match?(%r{/api/v1/profile/mfa/(verify|backup_codes)})
|
||||
req.ip if req.post? # Throttle verify and backup_codes attempts
|
||||
end
|
||||
end
|
||||
|
||||
# Separate rate limiting for MFA verification attempts
|
||||
throttle('mfa_login/ip', limit: 10, period: 1.minute) do |req|
|
||||
req.ip if req.path_without_extentions == '/auth/sign_in' && req.post? && req.params['mfa_token'].present?
|
||||
end
|
||||
|
||||
throttle('mfa_login/token', limit: 10, period: 1.minute) do |req|
|
||||
if req.path_without_extentions == '/auth/sign_in' && req.post?
|
||||
# Track by MFA token to prevent brute force on a specific token
|
||||
mfa_token = req.params['mfa_token'].presence
|
||||
(mfa_token.presence)
|
||||
end
|
||||
end
|
||||
|
||||
## Prevent Brute-Force Signup Attacks ###
|
||||
throttle('accounts/ip', limit: 5, period: 30.minutes) do |req|
|
||||
req.ip if req.path_without_extentions == '/api/v1/accounts' && req.post?
|
||||
end
|
||||
|
||||
##-----------------------------------------------##
|
||||
|
||||
###-----------------------------------------------###
|
||||
###-----------Widget API Throttling---------------###
|
||||
###-----------------------------------------------###
|
||||
|
||||
# Rack attack on widget APIs can be disabled by setting ENABLE_RACK_ATTACK_WIDGET_API to false
|
||||
# For clients using the widgets in specific conditions like inside and iframe
|
||||
# TODO: Deprecate this feature in future after finding a better solution
|
||||
if ActiveModel::Type::Boolean.new.cast(ENV.fetch('ENABLE_RACK_ATTACK_WIDGET_API', true))
|
||||
## Prevent Conversation Bombing on Widget APIs ###
|
||||
throttle('api/v1/widget/conversations', limit: 6, period: 12.hours) do |req|
|
||||
req.ip if req.path_without_extentions == '/api/v1/widget/conversations' && req.post?
|
||||
end
|
||||
|
||||
## Prevent Contact update Bombing in Widget API ###
|
||||
throttle('api/v1/widget/contacts', limit: 60, period: 1.hour) do |req|
|
||||
req.ip if req.path_without_extentions == '/api/v1/widget/contacts' && (req.patch? || req.put?)
|
||||
end
|
||||
|
||||
## Prevent Conversation Bombing through multiple sessions
|
||||
throttle('widget?website_token={website_token}&cw_conversation={x-auth-token}', limit: 5, period: 1.hour) do |req|
|
||||
req.ip if req.path_without_extentions == '/widget' && ActionDispatch::Request.new(req.env).params['cw_conversation'].blank?
|
||||
end
|
||||
end
|
||||
|
||||
##-----------------------------------------------##
|
||||
|
||||
###-----------------------------------------------###
|
||||
###----------Application API Throttling-----------###
|
||||
###-----------------------------------------------###
|
||||
|
||||
## Prevent Abuse of Converstion Transcript APIs ###
|
||||
throttle('/api/v1/accounts/:account_id/conversations/:conversation_id/transcript', limit: 30, period: 1.hour) do |req|
|
||||
match_data = %r{/api/v1/accounts/(?<account_id>\d+)/conversations/(?<conversation_id>\d+)/transcript}.match(req.path)
|
||||
match_data[:account_id] if match_data.present?
|
||||
end
|
||||
|
||||
## Prevent Abuse of attachment upload APIs ##
|
||||
throttle('/api/v1/accounts/:account_id/upload', limit: 60, period: 1.hour) do |req|
|
||||
match_data = %r{/api/v1/accounts/(?<account_id>\d+)/upload}.match(req.path)
|
||||
match_data[:account_id] if match_data.present?
|
||||
end
|
||||
|
||||
## Prevent abuse of contact search api
|
||||
throttle('/api/v1/accounts/:account_id/contacts/search', limit: ENV.fetch('RATE_LIMIT_CONTACT_SEARCH', '100').to_i, period: 1.minute) do |req|
|
||||
match_data = %r{/api/v1/accounts/(?<account_id>\d+)/contacts/search}.match(req.path)
|
||||
match_data[:account_id] if match_data.present?
|
||||
end
|
||||
|
||||
# Throttle by individual user (based on uid)
|
||||
throttle('/api/v2/accounts/:account_id/reports/user', limit: ENV.fetch('RATE_LIMIT_REPORTS_API_USER_LEVEL', '100').to_i, period: 1.minute) do |req|
|
||||
match_data = %r{/api/v2/accounts/(?<account_id>\d+)/reports}.match(req.path)
|
||||
# Extract user identification (uid for web, api_access_token for API requests)
|
||||
user_uid = req.get_header('HTTP_UID')
|
||||
api_access_token = req.get_header('HTTP_API_ACCESS_TOKEN') || req.get_header('api_access_token')
|
||||
|
||||
# Use uid if present, otherwise fallback to api_access_token for tracking
|
||||
user_identifier = user_uid.presence || api_access_token.presence
|
||||
|
||||
"#{user_identifier}:#{match_data[:account_id]}" if match_data.present? && user_identifier.present?
|
||||
end
|
||||
|
||||
## Prevent abuse of reports api at account level
|
||||
throttle('/api/v2/accounts/:account_id/reports', limit: ENV.fetch('RATE_LIMIT_REPORTS_API_ACCOUNT_LEVEL', '1000').to_i, period: 1.minute) do |req|
|
||||
match_data = %r{/api/v2/accounts/(?<account_id>\d+)/reports}.match(req.path)
|
||||
match_data[:account_id] if match_data.present?
|
||||
end
|
||||
|
||||
## Prevent increased use of conversations meta API per user
|
||||
throttle('/api/v1/accounts/:account_id/conversations/meta/user',
|
||||
limit: ENV.fetch('RATE_LIMIT_CONVERSATIONS_META', '30').to_i, period: 1.minute) do |req|
|
||||
match_data = %r{/api/v1/accounts/(?<account_id>\d+)/conversations/meta}.match(req.path)
|
||||
next unless match_data.present? && req.get?
|
||||
|
||||
user_uid = req.get_header('HTTP_UID')
|
||||
api_access_token = req.get_header('HTTP_API_ACCESS_TOKEN') || req.get_header('api_access_token')
|
||||
user_identifier = user_uid.presence || api_access_token.presence
|
||||
|
||||
"#{user_identifier}:#{match_data[:account_id]}" if user_identifier.present?
|
||||
end
|
||||
|
||||
## ----------------------------------------------- ##
|
||||
end
|
||||
|
||||
# Log blocked events
|
||||
ActiveSupport::Notifications.subscribe('throttle.rack_attack') do |_name, _start, _finish, _request_id, payload|
|
||||
req = payload[:request]
|
||||
|
||||
user_uid = req.get_header('HTTP_UID')
|
||||
api_access_token = req.get_header('HTTP_API_ACCESS_TOKEN') || req.get_header('api_access_token')
|
||||
|
||||
# Mask the token if present
|
||||
masked_api_token = api_access_token.present? ? "#{api_access_token[0..4]}...[REDACTED]" : nil
|
||||
|
||||
# Use uid if present, otherwise fallback to masked api_access_token for tracking
|
||||
user_identifier = user_uid.presence || masked_api_token.presence || 'unknown_user'
|
||||
|
||||
# Extract account ID if present
|
||||
account_match = %r{/accounts/(?<account_id>\d+)}.match(req.path)
|
||||
account_id = account_match ? account_match[:account_id] : 'unknown_account'
|
||||
|
||||
Rails.logger.warn(
|
||||
"[Rack::Attack][Blocked] remote_ip: \"#{req.remote_ip}\", " \
|
||||
"path: \"#{req.path}\", " \
|
||||
"user_identifier: \"#{user_identifier}\", " \
|
||||
"account_id: \"#{account_id}\", " \
|
||||
"method: \"#{req.request_method}\", " \
|
||||
"user_agent: \"#{req.user_agent}\""
|
||||
)
|
||||
end
|
||||
|
||||
Rack::Attack.enabled = Rails.env.production? ? ActiveModel::Type::Boolean.new.cast(ENV.fetch('ENABLE_RACK_ATTACK', true)) : false
|
||||
8
research/chatwoot/config/initializers/rack_profiler.rb
Normal file
8
research/chatwoot/config/initializers/rack_profiler.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
if Rails.env.development? && ENV['DISABLE_MINI_PROFILER'].blank?
|
||||
require 'rack-mini-profiler'
|
||||
|
||||
# initialization is skipped so trigger it
|
||||
Rack::MiniProfilerRails.initialize!(Rails.application)
|
||||
end
|
||||
6
research/chatwoot/config/initializers/rack_timeout.rb
Normal file
6
research/chatwoot/config/initializers/rack_timeout.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
require 'rack-timeout'
|
||||
|
||||
# Reduce noise by filtering state=ready and state=completed which are logged at INFO level
|
||||
Rails.application.config.after_initialize do
|
||||
Rack::Timeout::Logger.level = Logger::ERROR
|
||||
end
|
||||
14
research/chatwoot/config/initializers/searchkick.rb
Normal file
14
research/chatwoot/config/initializers/searchkick.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
Searchkick.queue_name = :async_database_migration if ENV.fetch('OPENSEARCH_URL', '').present?
|
||||
|
||||
access_key_id = ENV.fetch('OPENSEARCH_AWS_ACCESS_KEY_ID', '')
|
||||
secret_access_key = ENV.fetch('OPENSEARCH_AWS_SECRET_ACCESS_KEY', '')
|
||||
|
||||
if access_key_id.present? && secret_access_key.present?
|
||||
region = ENV.fetch('OPENSEARCH_AWS_REGION', 'us-east-1')
|
||||
|
||||
Searchkick.aws_credentials = {
|
||||
access_key_id: access_key_id,
|
||||
secret_access_key: secret_access_key,
|
||||
region: region
|
||||
}
|
||||
end
|
||||
44
research/chatwoot/config/initializers/secure_password.rb
Normal file
44
research/chatwoot/config/initializers/secure_password.rb
Normal file
@@ -0,0 +1,44 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
Devise.setup do |config|
|
||||
# ==> Configuration for the Devise Secure Password extension
|
||||
# Module: password_has_required_content
|
||||
#
|
||||
# Configure password content requirements including the number of uppercase,
|
||||
# lowercase, number, and special characters that are required. To configure the
|
||||
# minimum and maximum length refer to the Devise config.password_length
|
||||
# standard configuration parameter.
|
||||
|
||||
# The number of uppercase letters (latin A-Z) required in a password:
|
||||
config.password_required_uppercase_count = 1
|
||||
|
||||
# The number of lowercase letters (latin A-Z) required in a password:
|
||||
config.password_required_lowercase_count = 1
|
||||
|
||||
# The number of numbers (0-9) required in a password:
|
||||
config.password_required_number_count = 1
|
||||
|
||||
# The number of special characters (!@#$%^&*()_+-=[]{}|') required in a password:
|
||||
config.password_required_special_character_count = 1
|
||||
|
||||
# we are not using the configurations below
|
||||
# ==> Configuration for the Devise Secure Password extension
|
||||
# Module: password_disallows_frequent_reuse
|
||||
#
|
||||
# The number of previously used passwords that can not be reused:
|
||||
# config.password_previously_used_count = 8
|
||||
|
||||
# ==> Configuration for the Devise Secure Password extension
|
||||
# Module: password_disallows_frequent_changes
|
||||
# *Requires* password_disallows_frequent_reuse
|
||||
#
|
||||
# The minimum time that must pass between password changes:
|
||||
# config.password_minimum_age = 1.days
|
||||
|
||||
# ==> Configuration for the Devise Secure Password extension
|
||||
# Module: password_requires_regular_updates
|
||||
# *Requires* password_disallows_frequent_reuse
|
||||
#
|
||||
# The maximum allowed age of a password:
|
||||
# config.password_maximum_age = 180.days
|
||||
end
|
||||
15
research/chatwoot/config/initializers/sentry.rb
Normal file
15
research/chatwoot/config/initializers/sentry.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
if ENV['SENTRY_DSN'].present?
|
||||
Sentry.init do |config|
|
||||
config.dsn = ENV['SENTRY_DSN']
|
||||
config.enabled_environments = %w[staging production]
|
||||
|
||||
# To activate performance monitoring, set one of these options.
|
||||
# We recommend adjusting the value in production:
|
||||
config.traces_sample_rate = 0.1 if ENV['ENABLE_SENTRY_TRANSACTIONS']
|
||||
|
||||
config.excluded_exceptions += ['Rack::Timeout::RequestTimeoutException']
|
||||
|
||||
# to track post data in sentry
|
||||
config.send_default_pii = true unless ENV['DISABLE_SENTRY_PII']
|
||||
end
|
||||
end
|
||||
3
research/chatwoot/config/initializers/session_store.rb
Normal file
3
research/chatwoot/config/initializers/session_store.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
Rails.application.config.session_store :cookie_store, key: '_chatwoot_session', same_site: :lax
|
||||
38
research/chatwoot/config/initializers/sidekiq.rb
Normal file
38
research/chatwoot/config/initializers/sidekiq.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
require Rails.root.join('lib/redis/config')
|
||||
|
||||
schedule_file = 'config/schedule.yml'
|
||||
|
||||
Sidekiq.configure_client do |config|
|
||||
config.redis = Redis::Config.app
|
||||
end
|
||||
|
||||
# Logs whenever a job is pulled off Redis for execution.
|
||||
class ChatwootDequeuedLogger
|
||||
def call(_worker, job, queue)
|
||||
payload = job['args'].first
|
||||
Sidekiq.logger.info("Dequeued #{job['wrapped']} #{payload['job_id']} from #{queue}")
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
Sidekiq.configure_server do |config|
|
||||
config.redis = Redis::Config.app
|
||||
|
||||
if ActiveModel::Type::Boolean.new.cast(ENV.fetch('ENABLE_SIDEKIQ_DEQUEUE_LOGGER', false))
|
||||
config.server_middleware do |chain|
|
||||
chain.add ChatwootDequeuedLogger
|
||||
end
|
||||
end
|
||||
|
||||
# skip the default start stop logging
|
||||
if Rails.env.production?
|
||||
config.logger.formatter = Sidekiq::Logger::Formatters::JSON.new
|
||||
config[:skip_default_job_logging] = true
|
||||
config.logger.level = Logger.const_get(ENV.fetch('LOG_LEVEL', 'info').upcase.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
# https://github.com/ondrejbartas/sidekiq-cron
|
||||
Rails.application.reloader.to_prepare do
|
||||
Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file) if File.exist?(schedule_file) && Sidekiq.server?
|
||||
end
|
||||
3
research/chatwoot/config/initializers/stripe.rb
Normal file
3
research/chatwoot/config/initializers/stripe.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
require 'stripe'
|
||||
|
||||
Stripe.api_key = ENV.fetch('STRIPE_SECRET_KEY', nil)
|
||||
11
research/chatwoot/config/initializers/warden_hooks.rb
Normal file
11
research/chatwoot/config/initializers/warden_hooks.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
Warden::Manager.after_set_user do |user, auth, opts|
|
||||
scope = opts[:scope]
|
||||
auth.cookies.signed["#{scope}.id"] = user.id
|
||||
auth.cookies.signed["#{scope}.expires_at"] = 30.minutes.from_now
|
||||
end
|
||||
|
||||
Warden::Manager.before_logout do |_user, auth, opts|
|
||||
scope = opts[:scope]
|
||||
auth.cookies.signed["#{scope}.id"] = nil
|
||||
auth.cookies.signed["#{scope}.expires_at"] = nil
|
||||
end
|
||||
14
research/chatwoot/config/initializers/wrap_parameters.rb
Normal file
14
research/chatwoot/config/initializers/wrap_parameters.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# This file contains settings for ActionController::ParamsWrapper which
|
||||
# is enabled by default.
|
||||
|
||||
# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
|
||||
ActiveSupport.on_load(:action_controller) do
|
||||
wrap_parameters format: [:json]
|
||||
end
|
||||
|
||||
# To enable root element in JSON for ActiveRecord objects.
|
||||
# ActiveSupport.on_load(:active_record) do
|
||||
# self.include_root_in_json = true
|
||||
# end
|
||||
Reference in New Issue
Block a user