embargoed 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +19 -0
- data/README.md +27 -0
- data/lib/embargoed/configuration.rb +60 -0
- data/lib/embargoed/engine.rb +16 -0
- data/lib/embargoed/i18n/accept_language_parser.rb +109 -0
- data/lib/embargoed/i18n/internationalization.rb +141 -0
- data/lib/embargoed/ip_locator.rb +29 -0
- data/lib/embargoed/maintenance_file.rb +119 -0
- data/lib/embargoed/maintenance_page/base.rb +79 -0
- data/lib/embargoed/maintenance_page/erb.rb +22 -0
- data/lib/embargoed/maintenance_page/html.rb +21 -0
- data/lib/embargoed/maintenance_page/json.rb +26 -0
- data/lib/embargoed/maintenance_page.rb +24 -0
- data/lib/embargoed/ordered_options.rb +98 -0
- data/lib/embargoed/rake_tasks.rb +3 -0
- data/lib/embargoed/request.rb +39 -0
- data/lib/embargoed/version.rb +3 -0
- data/lib/embargoed.rb +16 -0
- data/lib/rack/embargoed.rb +21 -0
- data/lib/tasks/maintenance.rake +48 -0
- data/public/Geoacumen-Country.mmdb +0 -0
- data/public/embargoed-message.jpg +0 -0
- data/public/ip_geo_db_license.md +206 -0
- data/public/maintenance.html +62 -0
- data/public/maintenance.html.erb +69 -0
- data/public/maintenance.json +1 -0
- metadata +239 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c4f9b6c54838896e47c825725907170322148c1bf40351d0e4607b5375037e98
|
4
|
+
data.tar.gz: 63e544b02169e811de9547fd1bf95a4008df0faaaa5e5fdf51efbdfb253b5d18
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2a3a1062c73c4014abb13f1077587831b98a17e77675e806b567f165f6bf352538b929ab2a01c15a0958004f90c7716d7e74cf63b232da3e646aaa805cae7b6d
|
7
|
+
data.tar.gz: 4183a31830ffee4d535f8a35b49681318462d8afe416d330bf3a5d8cf01466cd60c6679c69a3689779584efe048335cf9e09fd32e1819a5a123e3e1e314b2e8d
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2011 by Biola University
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Embargoed
|
2
|
+
=======
|
3
|
+
Embargoed is [Rack](http://rack.rubyforge.org/) middleware with a [Ruby on Rails](http://rubyonrails.org) engine that blocks all requests from Russia and displays a pro-Ukraine message instead.
|
4
|
+
|
5
|
+
Embargoed is a fork from [Turnout](https://github.com/biola/turnout).
|
6
|
+
|
7
|
+

|
8
|
+
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this to your `Gemfile`:
|
13
|
+
|
14
|
+
gem 'embargoed'
|
15
|
+
|
16
|
+
then run
|
17
|
+
|
18
|
+
bundle install
|
19
|
+
|
20
|
+
_Note that you'll need to restart your Rails server before it works_
|
21
|
+
|
22
|
+
That's it! Just by having `embargoed` on your Gemfile your Rails app will display this message to all requests from a Russian IP.
|
23
|
+
|
24
|
+
|
25
|
+
## Collaborate
|
26
|
+
|
27
|
+
Please feel free to contact me [@rameerez](https://twitter.com/rameerez) or fork this to port it to other platforms, or make PRs to this repo to collaborate.
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require_relative './ordered_options'
|
2
|
+
module Embargoed
|
3
|
+
class Configuration
|
4
|
+
SETTINGS = [
|
5
|
+
:app_root,
|
6
|
+
:named_maintenance_file_paths,
|
7
|
+
:maintenance_pages_path,
|
8
|
+
:default_maintenance_page,
|
9
|
+
:default_reason,
|
10
|
+
:default_allowed_ips,
|
11
|
+
:skip_middleware,
|
12
|
+
:default_allowed_paths,
|
13
|
+
:default_response_code,
|
14
|
+
:default_retry_after,
|
15
|
+
:i18n
|
16
|
+
].freeze
|
17
|
+
|
18
|
+
SETTINGS.each do |setting|
|
19
|
+
attr_accessor setting
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@skip_middleware = false
|
24
|
+
@app_root = '.'
|
25
|
+
@named_maintenance_file_paths = {default: app_root.join('tmp', 'maintenance.yml').to_s}
|
26
|
+
@maintenance_pages_path = app_root.join('public').to_s
|
27
|
+
@default_maintenance_page = Embargoed::MaintenancePage::HTML
|
28
|
+
@default_reason = "The site is temporarily down for maintenance.\nPlease check back soon."
|
29
|
+
@default_allowed_paths = []
|
30
|
+
@default_allowed_ips = []
|
31
|
+
@default_response_code = 503
|
32
|
+
@default_retry_after = 7200 # 2 hours by default
|
33
|
+
@i18n = Embargoed::OrderedOptions.new
|
34
|
+
@i18n.railties_load_path = []
|
35
|
+
@i18n.load_path = []
|
36
|
+
@i18n.fallbacks = Embargoed::OrderedOptions.new
|
37
|
+
@i18n.enabled = false
|
38
|
+
@i18n.use_language_header = false
|
39
|
+
end
|
40
|
+
|
41
|
+
def app_root
|
42
|
+
Pathname.new(@app_root.to_s)
|
43
|
+
end
|
44
|
+
|
45
|
+
def named_maintenance_file_paths=(named_paths)
|
46
|
+
# Force keys to symbols
|
47
|
+
@named_maintenance_file_paths = Hash[named_paths.map { |k, v| [k.to_sym, v] }]
|
48
|
+
end
|
49
|
+
|
50
|
+
def update(settings_hash)
|
51
|
+
settings_hash.each do |setting, value|
|
52
|
+
unless SETTINGS.include? setting.to_sym
|
53
|
+
raise ArgumentError, "invalid setting: #{setting}"
|
54
|
+
end
|
55
|
+
|
56
|
+
self.public_send "#{setting}=", value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'embargoed'
|
2
|
+
require 'rack/embargoed'
|
3
|
+
require 'rails'
|
4
|
+
|
5
|
+
# For Rails 3
|
6
|
+
if defined? Rails::Engine
|
7
|
+
module Embargoed
|
8
|
+
class Engine < Rails::Engine
|
9
|
+
initializer 'embargoed.add_to_middleware_stack' do |app|
|
10
|
+
unless Embargoed.config.skip_middleware
|
11
|
+
app.config.middleware.use(Rack::Embargoed)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Embargoed
|
2
|
+
class AcceptLanguageParser
|
3
|
+
attr_accessor :header
|
4
|
+
|
5
|
+
def initialize(header)
|
6
|
+
@header = header
|
7
|
+
end
|
8
|
+
|
9
|
+
# Returns a sorted array based on user preference in HTTP_ACCEPT_LANGUAGE.
|
10
|
+
# Browsers send this HTTP header, so don't think this is holy.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
#
|
14
|
+
# request.user_preferred_languages
|
15
|
+
# # => [ 'nl-NL', 'nl-BE', 'nl', 'en-US', 'en' ]
|
16
|
+
#
|
17
|
+
def user_preferred_languages
|
18
|
+
return [] if header.to_s.strip.empty?
|
19
|
+
@user_preferred_languages ||= begin
|
20
|
+
header.to_s.gsub(/\s+/, '').split(',').map do |language|
|
21
|
+
locale, quality = language.split(';q=')
|
22
|
+
raise ArgumentError, 'Not correctly formatted' unless locale =~ /^[a-z\-0-9]+|\*$/i
|
23
|
+
|
24
|
+
locale = locale.downcase.gsub(/-[a-z0-9]+$/i, &:upcase) # Uppercase territory
|
25
|
+
locale = nil if locale == '*' # Ignore wildcards
|
26
|
+
|
27
|
+
quality = quality ? quality.to_f : 1.0
|
28
|
+
|
29
|
+
[locale, quality]
|
30
|
+
end.sort do |(_, left), (_, right)|
|
31
|
+
right <=> left
|
32
|
+
end.map(&:first).compact
|
33
|
+
rescue ArgumentError # Just rescue anything if the browser messed up badly.
|
34
|
+
[]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sets the user languages preference, overriding the browser
|
39
|
+
#
|
40
|
+
def user_preferred_languages=(languages)
|
41
|
+
@user_preferred_languages = languages
|
42
|
+
end
|
43
|
+
|
44
|
+
# Finds the locale specifically requested by the browser.
|
45
|
+
#
|
46
|
+
# Example:
|
47
|
+
#
|
48
|
+
# request.preferred_language_from I18n.available_locales
|
49
|
+
# # => 'nl'
|
50
|
+
#
|
51
|
+
def preferred_language_from(array)
|
52
|
+
(user_preferred_languages & array.map(&:to_s)).first
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the first of the user_preferred_languages that is compatible
|
56
|
+
# with the available locales. Ignores region.
|
57
|
+
#
|
58
|
+
# Example:
|
59
|
+
#
|
60
|
+
# request.compatible_language_from I18n.available_locales
|
61
|
+
#
|
62
|
+
def compatible_language_from(available_languages)
|
63
|
+
user_preferred_languages.map do |preferred| #en-US
|
64
|
+
preferred = preferred.downcase
|
65
|
+
preferred_language = preferred.split('-', 2).first
|
66
|
+
|
67
|
+
available_languages.find do |available| # en
|
68
|
+
available = available.to_s.downcase
|
69
|
+
preferred == available || preferred_language == available.split('-', 2).first
|
70
|
+
end
|
71
|
+
end.compact.first
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns a supplied list of available locals without any extra application info
|
75
|
+
# that may be attached to the locale for storage in the application.
|
76
|
+
#
|
77
|
+
# Example:
|
78
|
+
# [ja_JP-x1, en-US-x4, en_UK-x5, fr-FR-x3] => [ja-JP, en-US, en-UK, fr-FR]
|
79
|
+
#
|
80
|
+
def sanitize_available_locales(available_languages)
|
81
|
+
available_languages.map do |available|
|
82
|
+
available.to_s.split(/[_-]/).reject { |part| part.start_with?("x") }.join("-")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns the first of the user preferred languages that is
|
87
|
+
# also found in available languages. Finds best fit by matching on
|
88
|
+
# primary language first and secondarily on region. If no matching region is
|
89
|
+
# found, return the first language in the group matching that primary language.
|
90
|
+
#
|
91
|
+
# Example:
|
92
|
+
#
|
93
|
+
# request.language_region_compatible(available_languages)
|
94
|
+
#
|
95
|
+
def language_region_compatible_from(available_languages)
|
96
|
+
available_languages = sanitize_available_locales(available_languages)
|
97
|
+
user_preferred_languages.map do |preferred| #en-US
|
98
|
+
preferred = preferred.downcase
|
99
|
+
preferred_language = preferred.split('-', 2).first
|
100
|
+
|
101
|
+
lang_group = available_languages.select do |available| # en
|
102
|
+
preferred_language == available.downcase.split('-', 2).first
|
103
|
+
end
|
104
|
+
|
105
|
+
lang_group.find { |lang| lang.downcase == preferred } || lang_group.first #en-US, en-UK
|
106
|
+
end.compact.first
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'i18n'
|
2
|
+
require 'i18n/backend/fallbacks'
|
3
|
+
require_relative './accept_language_parser'
|
4
|
+
require_relative '../ordered_options'
|
5
|
+
|
6
|
+
module Embargoed
|
7
|
+
class Internationalization
|
8
|
+
class << self
|
9
|
+
attr_reader :env
|
10
|
+
attr_writer :env
|
11
|
+
|
12
|
+
def initialize_i18n(env)
|
13
|
+
@env = env
|
14
|
+
setup_i18n_config
|
15
|
+
end
|
16
|
+
|
17
|
+
def i18n_config
|
18
|
+
@i18n_config = Embargoed.config.i18n
|
19
|
+
@i18n_config = @i18n_config.is_a?(Embargoed::OrderedOptions) ? @i18n_config : Embargoed::InheritableOptions.new(@i18n_config)
|
20
|
+
end
|
21
|
+
|
22
|
+
def embargoed_page
|
23
|
+
@embargoed_page ||= Embargoed.config.default_maintenance_page
|
24
|
+
end
|
25
|
+
|
26
|
+
def http_accept_language
|
27
|
+
language = (env.nil? || env.empty?) ? nil : env["HTTP_ACCEPT_LANGUAGE"]
|
28
|
+
@http_accept_language ||= Embargoed::AcceptLanguageParser.new(language)
|
29
|
+
end
|
30
|
+
|
31
|
+
def setup_additional_helpers
|
32
|
+
i18n_additional_helpers = i18n_config.delete(:additional_helpers)
|
33
|
+
i18n_additional_helpers = i18n_additional_helpers.is_a?(Array) ? i18n_additional_helpers : []
|
34
|
+
|
35
|
+
i18n_additional_helpers.each do |helper|
|
36
|
+
embargoed_page.send(:include, helper) if helper.is_a?(Module)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def expanded(path)
|
41
|
+
result = []
|
42
|
+
if File.directory?(path)
|
43
|
+
result.concat(Dir.glob(File.join(path, '**', '**')).map { |file| file }.sort)
|
44
|
+
else
|
45
|
+
result << path
|
46
|
+
end
|
47
|
+
result.uniq!
|
48
|
+
result
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns all expanded paths but only if they exist in the filesystem.
|
52
|
+
def existent(path)
|
53
|
+
expanded(path).select { |f| File.exist?(f) }
|
54
|
+
end
|
55
|
+
|
56
|
+
# Setup i18n configuration.
|
57
|
+
def setup_i18n_config
|
58
|
+
return unless i18n_config.enabled
|
59
|
+
setup_additional_helpers
|
60
|
+
fallbacks = i18n_config.delete(:fallbacks)
|
61
|
+
|
62
|
+
|
63
|
+
# Avoid issues with setting the default_locale by disabling available locales
|
64
|
+
# check while configuring.
|
65
|
+
enforce_available_locales = i18n_config.delete(:enforce_available_locales)
|
66
|
+
enforce_available_locales = I18n.enforce_available_locales if enforce_available_locales.nil?
|
67
|
+
I18n.enforce_available_locales = false
|
68
|
+
|
69
|
+
i18n_config.except(:enabled, :use_language_header).each do |setting, value|
|
70
|
+
case setting
|
71
|
+
when :railties_load_path
|
72
|
+
I18n.load_path.unshift(*value.map { |file| existent(file) }.flatten)
|
73
|
+
when :load_path
|
74
|
+
I18n.load_path += value
|
75
|
+
else
|
76
|
+
I18n.send("#{setting}=", value)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks)
|
82
|
+
I18n.backend.load_translations
|
83
|
+
|
84
|
+
# Restore available locales check so it will take place from now on.
|
85
|
+
I18n.enforce_available_locales = enforce_available_locales
|
86
|
+
|
87
|
+
begin
|
88
|
+
if i18n_config.use_language_header
|
89
|
+
I18n.locale = http_accept_language.compatible_language_from(I18n.available_locales) || I18n.default_locale
|
90
|
+
else
|
91
|
+
I18n.locale = I18n.default_locale
|
92
|
+
end
|
93
|
+
rescue
|
94
|
+
#nothing
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
def array_wrap(object)
|
100
|
+
if object.nil?
|
101
|
+
[]
|
102
|
+
elsif object.respond_to?(:to_ary)
|
103
|
+
object.to_ary || [object]
|
104
|
+
else
|
105
|
+
[object]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def include_fallbacks_module
|
110
|
+
I18n.backend.class.send(:include, I18n::Backend::Fallbacks)
|
111
|
+
end
|
112
|
+
|
113
|
+
def init_fallbacks(fallbacks)
|
114
|
+
include_fallbacks_module
|
115
|
+
|
116
|
+
args = case fallbacks
|
117
|
+
when Embargoed::OrderedOptions
|
118
|
+
[*(fallbacks[:defaults] || []) << fallbacks[:map]].compact
|
119
|
+
when Hash, Array
|
120
|
+
array_wrap(fallbacks)
|
121
|
+
else # TrueClass
|
122
|
+
[]
|
123
|
+
end
|
124
|
+
|
125
|
+
I18n.fallbacks = I18n::Locale::Fallbacks.new(*args)
|
126
|
+
end
|
127
|
+
|
128
|
+
def validate_fallbacks(fallbacks)
|
129
|
+
case fallbacks
|
130
|
+
when Embargoed::OrderedOptions
|
131
|
+
!fallbacks.empty?
|
132
|
+
when TrueClass, Array, Hash
|
133
|
+
true
|
134
|
+
else
|
135
|
+
raise "Unexpected fallback type #{fallbacks.inspect}"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'maxmind/db'
|
2
|
+
|
3
|
+
module Embargoed
|
4
|
+
class IPLocator
|
5
|
+
|
6
|
+
def self.get_country_code(ip)
|
7
|
+
country_code = "none"
|
8
|
+
|
9
|
+
# Using an Open-Source IP db released under an Apache2 license: https://github.com/geoacumen/geoacumen-country
|
10
|
+
db = File.expand_path("../../../public/Geoacumen-Country.mmdb", __FILE__)
|
11
|
+
reader = MaxMind::DB.new(db, mode: MaxMind::DB::MODE_MEMORY)
|
12
|
+
|
13
|
+
begin
|
14
|
+
record = reader.get(ip)
|
15
|
+
rescue => error
|
16
|
+
puts error.inspect
|
17
|
+
end
|
18
|
+
|
19
|
+
if !record.nil?
|
20
|
+
country_code = record['country']['iso_code']
|
21
|
+
end
|
22
|
+
|
23
|
+
reader.close
|
24
|
+
|
25
|
+
return country_code
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Embargoed
|
5
|
+
class MaintenanceFile
|
6
|
+
attr_reader :path
|
7
|
+
|
8
|
+
SETTINGS = [:reason, :allowed_paths, :allowed_ips, :response_code, :retry_after]
|
9
|
+
attr_reader(*SETTINGS)
|
10
|
+
|
11
|
+
def initialize(path)
|
12
|
+
@path = path
|
13
|
+
@reason = Embargoed.config.default_reason
|
14
|
+
@allowed_paths = Embargoed.config.default_allowed_paths
|
15
|
+
@allowed_ips = Embargoed.config.default_allowed_ips
|
16
|
+
@response_code = Embargoed.config.default_response_code
|
17
|
+
@retry_after = Embargoed.config.default_retry_after
|
18
|
+
|
19
|
+
import_yaml if exists?
|
20
|
+
end
|
21
|
+
|
22
|
+
def exists?
|
23
|
+
File.exist? path
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_h
|
27
|
+
SETTINGS.each_with_object({}) do |att, hash|
|
28
|
+
hash[att] = send(att)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_yaml(key_mapper = :to_s)
|
33
|
+
to_h.each_with_object({}) { |(key, val), hash|
|
34
|
+
hash[key.send(key_mapper)] = val
|
35
|
+
}.to_yaml
|
36
|
+
end
|
37
|
+
|
38
|
+
def write
|
39
|
+
FileUtils.mkdir_p(dir_path) unless Dir.exist? dir_path
|
40
|
+
|
41
|
+
File.open(path, 'w') do |file|
|
42
|
+
file.write to_yaml
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete
|
47
|
+
File.delete(path) if exists?
|
48
|
+
end
|
49
|
+
|
50
|
+
def import(hash)
|
51
|
+
SETTINGS.map(&:to_s).each do |att|
|
52
|
+
self.send(:"#{att}=", hash[att]) unless hash[att].nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
true
|
56
|
+
end
|
57
|
+
alias :import_env_vars :import
|
58
|
+
|
59
|
+
# Find the first MaintenanceFile that exists
|
60
|
+
def self.find
|
61
|
+
path = named_paths.values.find { |p| File.exist? p }
|
62
|
+
self.new(path) if path
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.named(name)
|
66
|
+
path = named_paths[name.to_sym]
|
67
|
+
self.new(path) unless path.nil?
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.default
|
71
|
+
self.new(named_paths.values.first)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def retry_after=(value)
|
77
|
+
@retry_after = value
|
78
|
+
end
|
79
|
+
|
80
|
+
def reason=(reason)
|
81
|
+
@reason = reason.to_s
|
82
|
+
end
|
83
|
+
|
84
|
+
# Splits strings on commas for easier importing of environment variables
|
85
|
+
def allowed_paths=(paths)
|
86
|
+
if paths.is_a? String
|
87
|
+
# Grab everything between commas that aren't escaped with a backslash
|
88
|
+
paths = paths.to_s.split(/(?<!\\),\ ?/).map do |path|
|
89
|
+
path.strip.gsub('\,', ',') # remove the escape characters
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
@allowed_paths = paths
|
94
|
+
end
|
95
|
+
|
96
|
+
# Splits strings on commas for easier importing of environment variables
|
97
|
+
def allowed_ips=(ips)
|
98
|
+
ips = ips.to_s.split(',') if ips.is_a? String
|
99
|
+
|
100
|
+
@allowed_ips = ips
|
101
|
+
end
|
102
|
+
|
103
|
+
def response_code=(code)
|
104
|
+
@response_code = code.to_i
|
105
|
+
end
|
106
|
+
|
107
|
+
def dir_path
|
108
|
+
File.dirname(path)
|
109
|
+
end
|
110
|
+
|
111
|
+
def import_yaml
|
112
|
+
import YAML::load(File.open(path)) || {}
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.named_paths
|
116
|
+
Embargoed.config.named_maintenance_file_paths
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Embargoed
|
2
|
+
module MaintenancePage
|
3
|
+
class Base
|
4
|
+
attr_reader :reason
|
5
|
+
|
6
|
+
def initialize(reason = nil, options = {})
|
7
|
+
@options = options.is_a?(Hash) ? options : {}
|
8
|
+
@reason = reason
|
9
|
+
end
|
10
|
+
|
11
|
+
def rack_response(code = nil, retry_after = nil)
|
12
|
+
code ||= Embargoed.config.default_response_code
|
13
|
+
[code, headers(retry_after), body]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Override with an array of media type strings. i.e. text/html
|
17
|
+
def self.media_types
|
18
|
+
raise NotImplementedError, '.media_types must be overridden in subclasses'
|
19
|
+
end
|
20
|
+
def media_types() self.class.media_types end
|
21
|
+
|
22
|
+
# Override with a file extension value like 'html' or 'json'
|
23
|
+
def self.extension
|
24
|
+
raise NotImplementedError, '.extension must be overridden in subclasses'
|
25
|
+
end
|
26
|
+
def extension() self.class.extension end
|
27
|
+
|
28
|
+
def custom_path
|
29
|
+
Pathname.new(Embargoed.config.maintenance_pages_path).join(filename)
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def self.inherited(subclass)
|
35
|
+
MaintenancePage.all << subclass
|
36
|
+
end
|
37
|
+
|
38
|
+
def headers(retry_after = nil)
|
39
|
+
headers = {'Content-Type' => media_types.first, 'Content-Length' => length}
|
40
|
+
# Include the Retry-After header unless it wasn't specified
|
41
|
+
headers['Retry-After'] = retry_after.to_s unless retry_after.nil?
|
42
|
+
headers
|
43
|
+
end
|
44
|
+
|
45
|
+
def length
|
46
|
+
content.bytesize.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def body
|
50
|
+
[content]
|
51
|
+
end
|
52
|
+
|
53
|
+
def content
|
54
|
+
file_content.gsub(/{{\s?reason\s?}}/, reason)
|
55
|
+
end
|
56
|
+
|
57
|
+
def file_content
|
58
|
+
File.read(path)
|
59
|
+
end
|
60
|
+
|
61
|
+
def path
|
62
|
+
if File.exist? custom_path
|
63
|
+
custom_path
|
64
|
+
else
|
65
|
+
default_path
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def default_path
|
70
|
+
File.expand_path("../../../../public/#{filename}", __FILE__)
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
def filename
|
75
|
+
"maintenance.#{extension}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'tilt'
|
3
|
+
require 'tilt/erb'
|
4
|
+
require_relative './html'
|
5
|
+
require_relative '../i18n/internationalization'
|
6
|
+
|
7
|
+
module Embargoed
|
8
|
+
module MaintenancePage
|
9
|
+
class Erb < Embargoed::MaintenancePage::HTML
|
10
|
+
|
11
|
+
def content
|
12
|
+
Embargoed::Internationalization.initialize_i18n(@options[:env])
|
13
|
+
Tilt.new(File.expand_path(path)).render(self, {reason: reason}.merge(@options))
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.extension
|
17
|
+
'html.erb'
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative './base'
|
2
|
+
module Embargoed
|
3
|
+
module MaintenancePage
|
4
|
+
class HTML < Base
|
5
|
+
def reason
|
6
|
+
super.to_s.split("\n").map{|txt| "<p>#{txt}</p>" }.join("\n")
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.media_types
|
10
|
+
%w{
|
11
|
+
text/html
|
12
|
+
application/xhtml+xml
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.extension
|
17
|
+
'html'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Embargoed
|
4
|
+
module MaintenancePage
|
5
|
+
class JSON < Base
|
6
|
+
def reason
|
7
|
+
super.to_s.to_json
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.media_types
|
11
|
+
%w{
|
12
|
+
application/json
|
13
|
+
text/json
|
14
|
+
application/x-javascript
|
15
|
+
text/javascript
|
16
|
+
text/x-javascript
|
17
|
+
text/x-json
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.extension
|
22
|
+
'json'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|