Allow ReadOnly to propogate up to the Ember app via Response Header

This commit is contained in:
Robin Ward 2015-04-24 14:32:18 -04:00
parent 5b3f99aa50
commit 3a6efa25f0
8 changed files with 58 additions and 16 deletions

View file

@ -1,13 +1,7 @@
/**
This mixin provides an 'ajax' method that can be used to perform ajax requests that
respect Discourse paths and the run loop.
@class Discourse.Ajax
@extends Ember.Mixin
@namespace Discourse
@module Discourse
**/
var _trackView = false;
Discourse.Ajax = Em.Mixin.create({
@ -56,8 +50,14 @@ Discourse.Ajax = Em.Mixin.create({
args.headers['Discourse-Track-View'] = true;
}
args.success = function(xhr) {
Ember.run(null, resolve, xhr);
args.success = function(data, textStatus, xhr) {
if (xhr.getResponseHeader('Discourse-Readonly')) {
Ember.run(function() {
Discourse.Site.currentProp('isReadOnly', true);
});
}
Ember.run(null, resolve, data);
};
args.error = function(xhr, textStatus) {

View file

@ -42,6 +42,7 @@ class ApplicationController < ActionController::Base
before_filter :preload_json
before_filter :check_xhr
before_filter :redirect_to_login_if_required
after_filter :add_readonly_header
layout :set_layout
@ -53,6 +54,10 @@ class ApplicationController < ActionController::Base
@use_crawler_layout ||= (has_escaped_fragment? || CrawlerDetection.crawler?(request.user_agent))
end
def add_readonly_header
response.headers['Discourse-Readonly'] = 'true' if Discourse.readonly_mode?
end
def slow_platform?
request.user_agent =~ /Android/
end
@ -394,7 +399,7 @@ class ApplicationController < ActionController::Base
def block_if_readonly_mode
return if request.fullpath.start_with?(path "/admin/backups")
raise Discourse::ReadOnly.new if !request.get? && Discourse.readonly_mode?
raise Discourse::ReadOnly.new if !(request.get? || request.head?) && Discourse.readonly_mode?
end
def build_not_found_page(status=404, layout=false)

View file

@ -613,7 +613,7 @@ en:
logout: "You were logged out."
refresh: "Refresh"
read_only_mode:
enabled: "An administrator enabled read-only mode. You can continue to browse the site but interactions may not work."
enabled: "Read-only mode is enabled. You can continue to browse the site but interactions may not work."
login_disabled: "Login is disabled while the site is in read only mode."
too_few_topics_notice: "Create at least 5 public topics and %{posts} public posts to get discussion started. New users cannot earn trust levels unless there's content for them to read. This message appears only to staff."

View file

@ -191,7 +191,7 @@ module Discourse
end
def self.readonly_mode?
!!$redis.get(readonly_mode_key)
DiscourseRedis.recently_readonly? || !!$redis.get(readonly_mode_key)
end
def self.request_refresh!

View file

@ -4,6 +4,19 @@
require_dependency 'cache'
class DiscourseRedis
def self.recently_readonly?
return false unless @last_read_only
@last_read_only > 15.seconds.ago
end
def self.received_readonly!
@last_read_only = Time.now
end
def self.clear_readonly!
@last_read_only = nil
end
def self.raw_connection(config = nil)
config ||= self.config
redis_opts = {host: config['host'], port: config['port'], db: config['db']}
@ -38,7 +51,10 @@ class DiscourseRedis
yield
rescue Redis::CommandError => ex
if ex.message =~ /READONLY/
STDERR.puts "WARN: Redis is in a readonly state. Performed a noop"
unless DiscourseRedis.recently_readonly?
STDERR.puts "WARN: Redis is in a readonly state. Performed a noop"
end
DiscourseRedis.received_readonly!
else
raise ex
end

View file

@ -107,16 +107,23 @@ describe Discourse do
context "#readonly_mode?" do
after do
DiscourseRedis.clear_readonly!
end
it "is false by default" do
expect(Discourse.readonly_mode?).to eq(false)
end
it "returns true when the key is present in redis" do
$redis.expects(:get).with(Discourse.readonly_mode_key).returns("1")
expect(Discourse.readonly_mode?).to eq(true)
end
it "returns false when the key is not present in redis" do
$redis.expects(:get).with(Discourse.readonly_mode_key).returns(nil)
expect(Discourse.readonly_mode?).to eq(false)
it "returns true when DiscourseRedis is recently read only" do
DiscourseRedis.received_readonly!
expect(Discourse.readonly_mode?).to eq(true)
end
end
context "#handle_exception" do

View file

@ -107,6 +107,18 @@ describe TopicsController do
end
end
describe "read only header" do
it "returns no read only header by default" do
get :show, {topic_id: topic.id}
expect(response.headers['Discourse-Readonly']).to eq(nil)
end
it "returns a readonly header if the site is read only" do
DiscourseRedis.received_readonly!
get :show, {topic_id: topic.id}
expect(response.headers['Discourse-Readonly']).to eq('true')
end
end
end
describe 'api' do

View file

@ -113,6 +113,8 @@ Spork.prefork do
# very expensive IO operations
SiteSetting.automatically_download_gravatars = false
DiscourseRedis.clear_readonly!
I18n.locale = :en
end