2013-05-30 18:41:29 -04:00
|
|
|
require 'cache'
|
2017-03-17 02:21:30 -04:00
|
|
|
require 'open3'
|
2017-12-21 16:29:11 -05:00
|
|
|
require_dependency 'route_format'
|
2013-08-23 02:21:52 -04:00
|
|
|
require_dependency 'plugin/instance'
|
2013-10-09 00:10:37 -04:00
|
|
|
require_dependency 'auth/default_current_user_provider'
|
2015-04-27 13:06:53 -04:00
|
|
|
require_dependency 'version'
|
2017-09-08 13:38:46 -04:00
|
|
|
require 'digest/sha1'
|
2013-05-30 18:41:29 -04:00
|
|
|
|
2015-07-14 14:52:35 -04:00
|
|
|
# Prevents errors with reloading dev with conditional includes
|
|
|
|
if Rails.env.development?
|
|
|
|
require_dependency 'file_store/s3_store'
|
|
|
|
require_dependency 'file_store/local_store'
|
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
module Discourse
|
|
|
|
|
2014-04-17 01:57:17 -04:00
|
|
|
require 'sidekiq/exception_handler'
|
2014-02-20 22:30:25 -05:00
|
|
|
class SidekiqExceptionHandler
|
|
|
|
extend Sidekiq::ExceptionHandler
|
|
|
|
end
|
|
|
|
|
2017-03-17 02:21:30 -04:00
|
|
|
class Utils
|
|
|
|
def self.execute_command(*command, failure_message: "")
|
|
|
|
stdout, stderr, status = Open3.capture3(*command)
|
|
|
|
|
|
|
|
if !status.success?
|
|
|
|
failure_message = "#{failure_message}\n" if !failure_message.blank?
|
|
|
|
raise "#{failure_message}#{stderr}"
|
|
|
|
end
|
|
|
|
|
|
|
|
stdout
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.pretty_logs(logs)
|
|
|
|
logs.join("\n".freeze)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-07-17 16:22:46 -04:00
|
|
|
# Log an exception.
|
|
|
|
#
|
2014-07-17 18:07:25 -04:00
|
|
|
# If your code is in a scheduled job, it is recommended to use the
|
|
|
|
# error_context() method in Jobs::Base to pass the job arguments and any
|
|
|
|
# other desired context.
|
2014-07-17 16:22:46 -04:00
|
|
|
# See app/jobs/base.rb for the error_context function.
|
2015-02-09 15:47:46 -05:00
|
|
|
def self.handle_job_exception(ex, context = {}, parent_logger = nil)
|
2014-02-20 22:30:25 -05:00
|
|
|
context ||= {}
|
|
|
|
parent_logger ||= SidekiqExceptionHandler
|
|
|
|
|
|
|
|
cm = RailsMultisite::ConnectionManagement
|
|
|
|
parent_logger.handle_exception(ex, {
|
|
|
|
current_db: cm.current_db,
|
|
|
|
current_hostname: cm.current_hostname
|
|
|
|
}.merge(context))
|
|
|
|
end
|
|
|
|
|
2013-06-18 20:31:19 -04:00
|
|
|
# Expected less matches than what we got in a find
|
2015-03-22 21:16:21 -04:00
|
|
|
class TooManyMatches < StandardError; end
|
2013-06-18 20:31:19 -04:00
|
|
|
|
2013-02-25 11:42:20 -05:00
|
|
|
# When they try to do something they should be logged in for
|
2015-03-22 21:16:21 -04:00
|
|
|
class NotLoggedIn < StandardError; end
|
2013-02-05 14:16:51 -05:00
|
|
|
|
|
|
|
# When the input is somehow bad
|
2015-03-22 21:16:21 -04:00
|
|
|
class InvalidParameters < StandardError; end
|
2013-02-05 14:16:51 -05:00
|
|
|
|
|
|
|
# When they don't have permission to do something
|
2015-09-18 03:14:10 -04:00
|
|
|
class InvalidAccess < StandardError
|
2017-09-23 10:39:58 -04:00
|
|
|
attr_reader :obj, :custom_message
|
|
|
|
def initialize(msg = nil, obj = nil, opts = nil)
|
2015-09-18 03:14:10 -04:00
|
|
|
super(msg)
|
2017-09-23 10:39:58 -04:00
|
|
|
|
|
|
|
opts ||= {}
|
|
|
|
@custom_message = opts[:custom_message] if opts[:custom_message]
|
2015-09-18 03:14:10 -04:00
|
|
|
@obj = obj
|
|
|
|
end
|
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
|
|
|
|
# When something they want is not found
|
2017-09-26 12:58:15 -04:00
|
|
|
class NotFound < StandardError; end
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2013-06-04 18:34:53 -04:00
|
|
|
# When a setting is missing
|
2015-03-22 21:16:21 -04:00
|
|
|
class SiteSettingMissing < StandardError; end
|
2013-06-04 18:34:53 -04:00
|
|
|
|
2013-11-05 13:04:47 -05:00
|
|
|
# When ImageMagick is missing
|
2015-03-22 21:16:21 -04:00
|
|
|
class ImageMagickMissing < StandardError; end
|
2013-11-05 13:04:47 -05:00
|
|
|
|
2014-02-12 23:37:28 -05:00
|
|
|
# When read-only mode is enabled
|
2015-03-22 21:16:21 -04:00
|
|
|
class ReadOnly < StandardError; end
|
2014-02-12 23:37:28 -05:00
|
|
|
|
2013-07-29 01:13:13 -04:00
|
|
|
# Cross site request forgery
|
2015-03-22 21:16:21 -04:00
|
|
|
class CSRF < StandardError; end
|
2013-07-29 01:13:13 -04:00
|
|
|
|
2017-08-06 21:43:09 -04:00
|
|
|
class Deprecation < StandardError; end
|
|
|
|
|
2013-12-23 18:50:36 -05:00
|
|
|
def self.filters
|
2015-07-27 02:46:50 -04:00
|
|
|
@filters ||= [:latest, :unread, :new, :read, :posted, :bookmarks]
|
2013-12-23 18:50:36 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.anonymous_filters
|
2015-07-27 02:46:50 -04:00
|
|
|
@anonymous_filters ||= [:latest, :top, :categories]
|
2013-12-23 18:50:36 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.top_menu_items
|
2014-01-14 12:48:57 -05:00
|
|
|
@top_menu_items ||= Discourse.filters + [:category, :categories, :top]
|
2013-12-23 18:50:36 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.anonymous_top_menu_items
|
2014-01-14 12:48:57 -05:00
|
|
|
@anonymous_top_menu_items ||= Discourse.anonymous_filters + [:category, :categories, :top]
|
2013-12-23 18:50:36 -05:00
|
|
|
end
|
|
|
|
|
2016-04-06 04:57:59 -04:00
|
|
|
PIXEL_RATIOS ||= [1, 1.5, 2, 3]
|
2015-05-29 03:57:54 -04:00
|
|
|
|
2015-05-25 11:59:00 -04:00
|
|
|
def self.avatar_sizes
|
2015-05-29 03:57:54 -04:00
|
|
|
# TODO: should cache these when we get a notification system for site settings
|
|
|
|
set = Set.new
|
|
|
|
|
|
|
|
SiteSetting.avatar_sizes.split("|").map(&:to_i).each do |size|
|
|
|
|
PIXEL_RATIOS.each do |pixel_ratio|
|
|
|
|
set << size * pixel_ratio
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-05-26 01:41:50 -04:00
|
|
|
set
|
2015-05-25 11:59:00 -04:00
|
|
|
end
|
|
|
|
|
2013-08-01 01:59:57 -04:00
|
|
|
def self.activate_plugins!
|
2015-04-27 13:06:53 -04:00
|
|
|
all_plugins = Plugin::Instance.find_all("#{Rails.root}/plugins")
|
|
|
|
|
2017-09-08 13:38:46 -04:00
|
|
|
if Rails.env.development?
|
|
|
|
plugin_hash = Digest::SHA1.hexdigest(all_plugins.map { |p| p.path }.sort.join('|'))
|
|
|
|
hash_file = "#{Rails.root}/tmp/plugin-hash"
|
|
|
|
old_hash = File.read(hash_file) rescue nil
|
|
|
|
|
|
|
|
if old_hash && old_hash != plugin_hash
|
|
|
|
puts "WARNING: It looks like your discourse plugins have recently changed."
|
|
|
|
puts "It is highly recommended to remove your `tmp` directory, otherwise"
|
|
|
|
puts "plugins might not work."
|
|
|
|
puts
|
|
|
|
else
|
|
|
|
File.write(hash_file, plugin_hash)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-04-27 13:06:53 -04:00
|
|
|
@plugins = []
|
|
|
|
all_plugins.each do |p|
|
|
|
|
v = p.metadata.required_version || Discourse::VERSION::STRING
|
|
|
|
if Discourse.has_needed_version?(Discourse::VERSION::STRING, v)
|
|
|
|
p.activate!
|
|
|
|
@plugins << p
|
|
|
|
else
|
|
|
|
STDERR.puts "Could not activate #{p.metadata.name}, discourse does not meet required version (#{v})"
|
|
|
|
end
|
|
|
|
end
|
2013-08-01 01:59:57 -04:00
|
|
|
end
|
|
|
|
|
2015-02-04 16:23:39 -05:00
|
|
|
def self.disabled_plugin_names
|
2016-06-30 10:55:01 -04:00
|
|
|
plugins.select { |p| !p.enabled? }.map(&:name)
|
2015-02-04 16:23:39 -05:00
|
|
|
end
|
|
|
|
|
2013-08-01 01:59:57 -04:00
|
|
|
def self.plugins
|
2015-02-10 11:18:16 -05:00
|
|
|
@plugins ||= []
|
2013-08-01 01:59:57 -04:00
|
|
|
end
|
|
|
|
|
2017-01-12 15:43:09 -05:00
|
|
|
def self.plugin_themes
|
|
|
|
@plugin_themes ||= plugins.map(&:themes).flatten
|
|
|
|
end
|
|
|
|
|
2016-11-14 19:42:55 -05:00
|
|
|
def self.official_plugins
|
2017-07-27 21:20:09 -04:00
|
|
|
plugins.find_all { |p| p.metadata.official? }
|
2016-11-14 19:42:55 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.unofficial_plugins
|
2017-07-27 21:20:09 -04:00
|
|
|
plugins.find_all { |p| !p.metadata.official? }
|
2016-11-14 19:42:55 -05:00
|
|
|
end
|
|
|
|
|
2014-01-14 20:07:42 -05:00
|
|
|
def self.assets_digest
|
|
|
|
@assets_digest ||= begin
|
|
|
|
digest = Digest::MD5.hexdigest(ActionView::Base.assets_manifest.assets.values.sort.join)
|
|
|
|
|
|
|
|
channel = "/global/asset-version"
|
2015-05-03 22:21:00 -04:00
|
|
|
message = MessageBus.last_message(channel)
|
2014-01-14 20:07:42 -05:00
|
|
|
|
|
|
|
unless message && message.data == digest
|
2015-05-03 22:21:00 -04:00
|
|
|
MessageBus.publish channel, digest
|
2014-01-14 20:07:42 -05:00
|
|
|
end
|
|
|
|
digest
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-08-25 21:04:16 -04:00
|
|
|
def self.authenticators
|
|
|
|
# TODO: perhaps we don't need auth providers and authenticators maybe one object is enough
|
|
|
|
|
|
|
|
# NOTE: this bypasses the site settings and gives a list of everything, we need to register every middleware
|
|
|
|
# for the cases of multisite
|
|
|
|
# In future we may change it so we don't include them all for cases where we are not a multisite, but we would
|
|
|
|
# require a restart after site settings change
|
|
|
|
Users::OmniauthCallbacksController::BUILTIN_AUTH + auth_providers.map(&:authenticator)
|
|
|
|
end
|
|
|
|
|
2013-08-01 01:59:57 -04:00
|
|
|
def self.auth_providers
|
2013-08-01 02:05:46 -04:00
|
|
|
providers = []
|
2015-02-10 11:18:16 -05:00
|
|
|
plugins.each do |p|
|
|
|
|
next unless p.auth_providers
|
|
|
|
p.auth_providers.each do |prov|
|
|
|
|
providers << prov
|
2013-08-01 01:59:57 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
providers
|
|
|
|
end
|
|
|
|
|
2013-05-30 18:41:29 -04:00
|
|
|
def self.cache
|
|
|
|
@cache ||= Cache.new
|
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
|
|
|
|
# Get the current base URL for the current site
|
|
|
|
def self.current_hostname
|
2016-06-30 10:55:01 -04:00
|
|
|
SiteSetting.force_hostname.presence || RailsMultisite::ConnectionManagement.current_hostname
|
2013-05-30 18:41:29 -04:00
|
|
|
end
|
|
|
|
|
2013-11-05 13:04:47 -05:00
|
|
|
def self.base_uri(default_value = "")
|
2016-06-30 10:55:01 -04:00
|
|
|
ActionController::Base.config.relative_url_root.presence || default_value
|
2013-03-14 08:01:52 -04:00
|
|
|
end
|
|
|
|
|
2016-07-28 13:54:17 -04:00
|
|
|
def self.base_protocol
|
|
|
|
SiteSetting.force_https? ? "https" : "http"
|
|
|
|
end
|
|
|
|
|
2013-05-30 18:41:29 -04:00
|
|
|
def self.base_url_no_prefix
|
2016-07-28 13:54:17 -04:00
|
|
|
default_port = SiteSetting.force_https? ? 443 : 80
|
|
|
|
url = "#{base_protocol}://#{current_hostname}"
|
2016-06-30 10:55:01 -04:00
|
|
|
url << ":#{SiteSetting.port}" if SiteSetting.port.to_i > 0 && SiteSetting.port.to_i != default_port
|
|
|
|
url
|
2013-04-05 06:38:20 -04:00
|
|
|
end
|
|
|
|
|
2013-05-30 18:41:29 -04:00
|
|
|
def self.base_url
|
2015-09-21 14:28:20 -04:00
|
|
|
base_url_no_prefix + base_uri
|
2013-05-30 18:41:29 -04:00
|
|
|
end
|
|
|
|
|
2017-07-19 15:08:54 -04:00
|
|
|
def self.route_for(uri)
|
|
|
|
|
|
|
|
uri = URI(uri) rescue nil unless (uri.is_a?(URI))
|
|
|
|
return unless uri
|
|
|
|
|
|
|
|
path = uri.path || ""
|
|
|
|
if (uri.host == Discourse.current_hostname &&
|
|
|
|
path.start_with?(Discourse.base_uri)) ||
|
|
|
|
!uri.host
|
|
|
|
|
|
|
|
path.slice!(Discourse.base_uri)
|
|
|
|
return Rails.application.routes.recognize_path(path)
|
|
|
|
end
|
|
|
|
|
2017-07-20 16:01:16 -04:00
|
|
|
nil
|
|
|
|
rescue ActionController::RoutingError
|
2017-07-19 15:08:54 -04:00
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
2017-01-11 05:03:36 -05:00
|
|
|
READONLY_MODE_KEY_TTL ||= 60
|
|
|
|
READONLY_MODE_KEY ||= 'readonly_mode'.freeze
|
|
|
|
PG_READONLY_MODE_KEY ||= 'readonly_mode:postgres'.freeze
|
2016-06-29 02:19:18 -04:00
|
|
|
USER_READONLY_MODE_KEY ||= 'readonly_mode:user'.freeze
|
|
|
|
|
2017-01-11 05:03:36 -05:00
|
|
|
READONLY_KEYS ||= [
|
2017-01-11 03:38:07 -05:00
|
|
|
READONLY_MODE_KEY,
|
|
|
|
PG_READONLY_MODE_KEY,
|
|
|
|
USER_READONLY_MODE_KEY
|
|
|
|
]
|
|
|
|
|
|
|
|
def self.enable_readonly_mode(key = READONLY_MODE_KEY)
|
|
|
|
if key == USER_READONLY_MODE_KEY
|
|
|
|
$redis.set(key, 1)
|
2016-06-29 02:19:18 -04:00
|
|
|
else
|
2017-01-11 03:38:07 -05:00
|
|
|
$redis.setex(key, READONLY_MODE_KEY_TTL, 1)
|
|
|
|
keep_readonly_mode(key)
|
2016-06-29 02:19:18 -04:00
|
|
|
end
|
2016-06-29 01:55:17 -04:00
|
|
|
|
2015-05-03 22:21:00 -04:00
|
|
|
MessageBus.publish(readonly_channel, true)
|
2013-02-05 14:16:51 -05:00
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2017-01-11 03:38:07 -05:00
|
|
|
def self.keep_readonly_mode(key)
|
2015-02-11 15:50:17 -05:00
|
|
|
# extend the expiry by 1 minute every 30 seconds
|
2016-11-10 10:44:51 -05:00
|
|
|
unless Rails.env.test?
|
|
|
|
Thread.new do
|
|
|
|
while readonly_mode?
|
2017-01-11 03:38:07 -05:00
|
|
|
$redis.expire(key, READONLY_MODE_KEY_TTL)
|
2016-11-10 10:44:51 -05:00
|
|
|
sleep 30.seconds
|
|
|
|
end
|
2015-02-11 15:50:17 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-01-11 03:38:07 -05:00
|
|
|
def self.disable_readonly_mode(key = READONLY_MODE_KEY)
|
2016-06-29 02:19:18 -04:00
|
|
|
$redis.del(key)
|
2015-05-03 22:21:00 -04:00
|
|
|
MessageBus.publish(readonly_channel, false)
|
2013-02-05 14:16:51 -05:00
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2014-02-12 23:37:28 -05:00
|
|
|
def self.readonly_mode?
|
2017-09-07 01:29:30 -04:00
|
|
|
recently_readonly? || $redis.mget(*READONLY_KEYS).compact.present?
|
2017-01-11 03:38:07 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.last_read_only
|
|
|
|
@last_read_only ||= {}
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.recently_readonly?
|
2017-01-11 05:03:36 -05:00
|
|
|
return false unless read_only = last_read_only[$redis.namespace]
|
2017-01-11 03:38:07 -05:00
|
|
|
read_only > 15.seconds.ago
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.received_readonly!
|
|
|
|
last_read_only[$redis.namespace] = Time.zone.now
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.clear_readonly!
|
|
|
|
last_read_only[$redis.namespace] = nil
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
2017-08-15 22:38:30 -04:00
|
|
|
def self.request_refresh!(user_ids: nil)
|
2014-02-21 00:52:11 -05:00
|
|
|
# Causes refresh on next click for all clients
|
|
|
|
#
|
2015-05-03 22:21:00 -04:00
|
|
|
# This is better than `MessageBus.publish "/file-change", ["refresh"]` because
|
2014-02-21 00:52:11 -05:00
|
|
|
# it spreads the refreshes out over a time period
|
2017-08-15 22:38:30 -04:00
|
|
|
if user_ids
|
2017-08-16 00:06:47 -04:00
|
|
|
MessageBus.publish("/refresh_client", 'clobber', user_ids: user_ids)
|
2017-08-15 22:38:30 -04:00
|
|
|
else
|
|
|
|
MessageBus.publish('/global/asset-version', 'clobber')
|
|
|
|
end
|
2014-02-21 00:52:11 -05:00
|
|
|
end
|
|
|
|
|
2017-10-03 23:22:23 -04:00
|
|
|
def self.ensure_version_file_loaded
|
|
|
|
unless @version_file_loaded
|
|
|
|
version_file = "#{Rails.root}/config/version.rb"
|
|
|
|
require version_file if File.exists?(version_file)
|
|
|
|
@version_file_loaded = true
|
|
|
|
end
|
|
|
|
end
|
2013-08-02 17:25:57 -04:00
|
|
|
|
2017-10-03 23:22:23 -04:00
|
|
|
def self.git_version
|
|
|
|
ensure_version_file_loaded
|
|
|
|
$git_version ||=
|
|
|
|
begin
|
|
|
|
git_cmd = 'git rev-parse HEAD'
|
|
|
|
self.try_git(git_cmd, Discourse::VERSION::STRING)
|
|
|
|
end
|
2013-02-18 01:39:54 -05:00
|
|
|
end
|
|
|
|
|
2014-09-09 17:04:10 -04:00
|
|
|
def self.git_branch
|
2017-10-03 23:22:23 -04:00
|
|
|
ensure_version_file_loaded
|
|
|
|
$git_branch ||=
|
|
|
|
begin
|
|
|
|
git_cmd = 'git rev-parse --abbrev-ref HEAD'
|
|
|
|
self.try_git(git_cmd, 'unknown')
|
|
|
|
end
|
2017-08-28 12:24:56 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.full_version
|
2017-10-03 23:22:23 -04:00
|
|
|
ensure_version_file_loaded
|
|
|
|
$full_version ||=
|
|
|
|
begin
|
|
|
|
git_cmd = 'git describe --dirty --match "v[0-9]*"'
|
|
|
|
self.try_git(git_cmd, 'unknown')
|
|
|
|
end
|
2017-08-28 12:24:56 -04:00
|
|
|
end
|
|
|
|
|
2017-10-03 23:22:23 -04:00
|
|
|
def self.try_git(git_cmd, default_value)
|
2017-08-28 12:24:56 -04:00
|
|
|
version_value = false
|
2014-09-09 17:04:10 -04:00
|
|
|
|
2017-10-03 23:22:23 -04:00
|
|
|
begin
|
|
|
|
version_value = `#{git_cmd}`.strip
|
|
|
|
rescue
|
|
|
|
version_value = default_value
|
2014-09-09 17:04:10 -04:00
|
|
|
end
|
2017-08-28 12:24:56 -04:00
|
|
|
|
|
|
|
if version_value.empty?
|
|
|
|
version_value = default_value
|
|
|
|
end
|
|
|
|
|
|
|
|
version_value
|
2014-09-09 17:04:10 -04:00
|
|
|
end
|
|
|
|
|
2013-09-06 03:28:37 -04:00
|
|
|
# Either returns the site_contact_username user or the first admin.
|
|
|
|
def self.site_contact_user
|
2014-05-06 09:41:59 -04:00
|
|
|
user = User.find_by(username_lower: SiteSetting.site_contact_username.downcase) if SiteSetting.site_contact_username.present?
|
2015-11-24 14:37:33 -05:00
|
|
|
user ||= (system_user || User.admins.real.order(:id).first)
|
2013-05-30 18:41:29 -04:00
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2015-05-06 19:00:13 -04:00
|
|
|
SYSTEM_USER_ID ||= -1
|
2014-06-24 20:45:20 -04:00
|
|
|
|
2013-09-06 03:28:37 -04:00
|
|
|
def self.system_user
|
2016-04-25 17:03:17 -04:00
|
|
|
@system_user ||= User.find_by(id: SYSTEM_USER_ID)
|
2013-09-06 03:28:37 -04:00
|
|
|
end
|
|
|
|
|
2013-07-31 17:26:34 -04:00
|
|
|
def self.store
|
2017-10-06 01:20:01 -04:00
|
|
|
if SiteSetting.Upload.enable_s3_uploads
|
2013-07-31 17:26:34 -04:00
|
|
|
@s3_store_loaded ||= require 'file_store/s3_store'
|
2013-11-05 13:04:47 -05:00
|
|
|
FileStore::S3Store.new
|
2013-07-31 17:26:34 -04:00
|
|
|
else
|
|
|
|
@local_store_loaded ||= require 'file_store/local_store'
|
2013-11-05 13:04:47 -05:00
|
|
|
FileStore::LocalStore.new
|
2013-07-31 17:26:34 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-10-09 00:10:37 -04:00
|
|
|
def self.current_user_provider
|
|
|
|
@current_user_provider || Auth::DefaultCurrentUserProvider
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.current_user_provider=(val)
|
|
|
|
@current_user_provider = val
|
|
|
|
end
|
|
|
|
|
2013-11-05 13:04:47 -05:00
|
|
|
def self.asset_host
|
|
|
|
Rails.configuration.action_controller.asset_host
|
|
|
|
end
|
|
|
|
|
2014-02-12 23:37:28 -05:00
|
|
|
def self.readonly_channel
|
2014-02-19 12:21:41 -05:00
|
|
|
"/site/read-only"
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
2014-02-12 23:37:28 -05:00
|
|
|
|
2014-03-27 22:48:14 -04:00
|
|
|
# all forking servers must call this
|
|
|
|
# after fork, otherwise Discourse will be
|
|
|
|
# in a bad state
|
|
|
|
def self.after_fork
|
2015-05-05 19:53:10 -04:00
|
|
|
# note: all this reconnecting may no longer be needed per https://github.com/redis/redis-rb/pull/414
|
2014-04-07 13:38:47 -04:00
|
|
|
current_db = RailsMultisite::ConnectionManagement.current_db
|
|
|
|
RailsMultisite::ConnectionManagement.establish_connection(db: current_db)
|
2015-05-03 22:21:00 -04:00
|
|
|
MessageBus.after_fork
|
2014-03-27 22:48:14 -04:00
|
|
|
SiteSetting.after_fork
|
|
|
|
$redis.client.reconnect
|
|
|
|
Rails.cache.reconnect
|
2014-05-07 18:05:28 -04:00
|
|
|
Logster.store.redis.reconnect
|
2014-04-22 21:01:17 -04:00
|
|
|
# shuts down all connections in the pool
|
2017-07-27 21:20:09 -04:00
|
|
|
Sidekiq.redis_pool.shutdown { |c| nil }
|
2014-04-22 21:01:17 -04:00
|
|
|
# re-establish
|
|
|
|
Sidekiq.redis = sidekiq_redis_config
|
2014-08-11 03:51:55 -04:00
|
|
|
start_connection_reaper
|
2016-07-16 01:11:34 -04:00
|
|
|
|
|
|
|
# in case v8 was initialized we want to make sure it is nil
|
|
|
|
PrettyText.reset_context
|
2016-11-01 22:34:20 -04:00
|
|
|
|
2016-11-02 01:59:58 -04:00
|
|
|
Tilt::ES6ModuleTranspilerTemplate.reset_context if defined? Tilt::ES6ModuleTranspilerTemplate
|
2016-11-01 22:34:20 -04:00
|
|
|
JsLocaleHelper.reset_context if defined? JsLocaleHelper
|
2014-05-07 18:05:28 -04:00
|
|
|
nil
|
2014-04-22 21:01:17 -04:00
|
|
|
end
|
|
|
|
|
2017-12-01 00:23:21 -05:00
|
|
|
# report a warning maintaining backtrack for logster
|
|
|
|
def self.warn_exception(e, message: "", env: nil)
|
|
|
|
if Rails.logger.respond_to? :add_with_opts
|
2018-01-04 17:54:28 -05:00
|
|
|
|
|
|
|
env ||= {}
|
|
|
|
env[:current_db] ||= RailsMultisite::ConnectionManagement.current_db
|
|
|
|
|
2017-12-01 00:23:21 -05:00
|
|
|
# logster
|
|
|
|
Rails.logger.add_with_opts(
|
|
|
|
::Logger::Severity::WARN,
|
|
|
|
"#{message} : #{e}",
|
|
|
|
"discourse-exception",
|
|
|
|
backtrace: e.backtrace.join("\n"),
|
|
|
|
env: env
|
|
|
|
)
|
|
|
|
else
|
|
|
|
# no logster ... fallback
|
|
|
|
Rails.logger.warn("#{message} #{e}")
|
|
|
|
end
|
|
|
|
rescue
|
|
|
|
STDERR.puts "Failed to report exception #{e} #{message}"
|
|
|
|
end
|
|
|
|
|
2015-02-16 17:58:23 -05:00
|
|
|
def self.start_connection_reaper
|
|
|
|
return if GlobalSetting.connection_reaper_age < 1 ||
|
|
|
|
GlobalSetting.connection_reaper_interval < 1
|
|
|
|
|
2014-08-11 03:51:55 -04:00
|
|
|
# this helps keep connection counts in check
|
|
|
|
Thread.new do
|
|
|
|
while true
|
2015-02-16 17:58:23 -05:00
|
|
|
begin
|
|
|
|
sleep GlobalSetting.connection_reaper_interval
|
2015-10-16 20:29:16 -04:00
|
|
|
reap_connections(GlobalSetting.connection_reaper_age, GlobalSetting.connection_reaper_max_age)
|
2015-02-16 17:58:23 -05:00
|
|
|
rescue => e
|
2017-12-01 00:23:21 -05:00
|
|
|
Discourse.warn_exception(e, message: "Error reaping connections")
|
2014-08-11 03:51:55 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-10-16 20:29:16 -04:00
|
|
|
def self.reap_connections(idle, max_age)
|
2015-02-16 17:58:23 -05:00
|
|
|
pools = []
|
2017-07-27 21:20:09 -04:00
|
|
|
ObjectSpace.each_object(ActiveRecord::ConnectionAdapters::ConnectionPool) { |pool| pools << pool }
|
2015-02-16 17:58:23 -05:00
|
|
|
|
|
|
|
pools.each do |pool|
|
2015-10-16 20:29:16 -04:00
|
|
|
pool.drain(idle.seconds, max_age.seconds)
|
2015-02-16 17:58:23 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-12-04 22:46:34 -05:00
|
|
|
SIDEKIQ_NAMESPACE ||= 'sidekiq'.freeze
|
|
|
|
|
2014-04-22 21:01:17 -04:00
|
|
|
def self.sidekiq_redis_config
|
2015-06-25 02:51:48 -04:00
|
|
|
conf = GlobalSetting.redis_config.dup
|
2016-12-04 22:46:34 -05:00
|
|
|
conf[:namespace] = SIDEKIQ_NAMESPACE
|
2015-06-25 02:51:48 -04:00
|
|
|
conf
|
2014-03-27 22:48:14 -04:00
|
|
|
end
|
|
|
|
|
2014-07-29 10:40:02 -04:00
|
|
|
def self.static_doc_topic_ids
|
|
|
|
[SiteSetting.tos_topic_id, SiteSetting.guidelines_topic_id, SiteSetting.privacy_topic_id]
|
|
|
|
end
|
|
|
|
|
2017-02-17 12:09:53 -05:00
|
|
|
cattr_accessor :last_ar_cache_reset
|
|
|
|
|
|
|
|
def self.reset_active_record_cache_if_needed(e)
|
|
|
|
last_cache_reset = Discourse.last_ar_cache_reset
|
2017-07-27 21:20:09 -04:00
|
|
|
if e && e.message =~ /UndefinedColumn/ && (last_cache_reset.nil? || last_cache_reset < 30.seconds.ago)
|
2017-02-17 12:09:53 -05:00
|
|
|
Rails.logger.warn "Clear Active Record cache cause schema appears to have changed!"
|
|
|
|
Discourse.last_ar_cache_reset = Time.zone.now
|
|
|
|
Discourse.reset_active_record_cache
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.reset_active_record_cache
|
|
|
|
ActiveRecord::Base.connection.query_cache.clear
|
2017-08-17 06:27:35 -04:00
|
|
|
(ActiveRecord::Base.connection.tables - %w[schema_migrations versions]).each do |table|
|
2017-02-17 12:09:53 -05:00
|
|
|
table.classify.constantize.reset_column_information rescue nil
|
|
|
|
end
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
2017-11-15 16:39:11 -05:00
|
|
|
def self.running_in_rack?
|
|
|
|
ENV["DISCOURSE_RUNNING_IN_RACK"] == "1"
|
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|