sidekiq 5.2.8 → 6.1.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sidekiq might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +20 -0
- data/.github/workflows/ci.yml +41 -0
- data/.gitignore +0 -2
- data/.standard.yml +20 -0
- data/5.0-Upgrade.md +1 -1
- data/6.0-Upgrade.md +72 -0
- data/Changes.md +196 -0
- data/Ent-2.0-Upgrade.md +37 -0
- data/Ent-Changes.md +72 -1
- data/Gemfile +12 -11
- data/Gemfile.lock +193 -0
- data/Pro-5.0-Upgrade.md +25 -0
- data/Pro-Changes.md +56 -2
- data/README.md +18 -34
- data/Rakefile +5 -4
- data/bin/sidekiq +26 -2
- data/bin/sidekiqload +32 -24
- data/bin/sidekiqmon +8 -0
- data/lib/generators/sidekiq/templates/worker_test.rb.erb +1 -1
- data/lib/generators/sidekiq/worker_generator.rb +21 -13
- data/lib/sidekiq/api.rb +245 -219
- data/lib/sidekiq/cli.rb +144 -180
- data/lib/sidekiq/client.rb +68 -48
- data/lib/sidekiq/delay.rb +5 -6
- data/lib/sidekiq/exception_handler.rb +10 -12
- data/lib/sidekiq/extensions/action_mailer.rb +13 -22
- data/lib/sidekiq/extensions/active_record.rb +13 -10
- data/lib/sidekiq/extensions/class_methods.rb +14 -11
- data/lib/sidekiq/extensions/generic_proxy.rb +4 -4
- data/lib/sidekiq/fetch.rb +29 -30
- data/lib/sidekiq/job_logger.rb +45 -7
- data/lib/sidekiq/job_retry.rb +62 -61
- data/lib/sidekiq/launcher.rb +112 -54
- data/lib/sidekiq/logger.rb +166 -0
- data/lib/sidekiq/manager.rb +11 -13
- data/lib/sidekiq/middleware/chain.rb +15 -5
- data/lib/sidekiq/middleware/i18n.rb +5 -7
- data/lib/sidekiq/monitor.rb +133 -0
- data/lib/sidekiq/paginator.rb +18 -14
- data/lib/sidekiq/processor.rb +71 -70
- data/lib/sidekiq/rails.rb +29 -37
- data/lib/sidekiq/redis_connection.rb +50 -48
- data/lib/sidekiq/scheduled.rb +28 -29
- data/lib/sidekiq/sd_notify.rb +149 -0
- data/lib/sidekiq/systemd.rb +24 -0
- data/lib/sidekiq/testing/inline.rb +2 -1
- data/lib/sidekiq/testing.rb +35 -24
- data/lib/sidekiq/util.rb +17 -16
- data/lib/sidekiq/version.rb +2 -1
- data/lib/sidekiq/web/action.rb +14 -10
- data/lib/sidekiq/web/application.rb +74 -72
- data/lib/sidekiq/web/csrf_protection.rb +156 -0
- data/lib/sidekiq/web/helpers.rb +97 -77
- data/lib/sidekiq/web/router.rb +18 -17
- data/lib/sidekiq/web.rb +53 -53
- data/lib/sidekiq/worker.rb +126 -102
- data/lib/sidekiq.rb +69 -44
- data/sidekiq.gemspec +15 -16
- data/web/assets/javascripts/application.js +25 -27
- data/web/assets/javascripts/dashboard.js +4 -23
- data/web/assets/stylesheets/application-dark.css +149 -0
- data/web/assets/stylesheets/application.css +28 -6
- data/web/locales/de.yml +14 -2
- data/web/locales/en.yml +2 -0
- data/web/locales/fr.yml +3 -3
- data/web/locales/ja.yml +4 -1
- data/web/locales/lt.yml +83 -0
- data/web/locales/pl.yml +4 -4
- data/web/locales/ru.yml +4 -0
- data/web/locales/vi.yml +83 -0
- data/web/views/_job_info.erb +2 -1
- data/web/views/busy.erb +8 -3
- data/web/views/dead.erb +2 -2
- data/web/views/layout.erb +1 -0
- data/web/views/morgue.erb +5 -2
- data/web/views/queue.erb +10 -1
- data/web/views/queues.erb +9 -1
- data/web/views/retries.erb +5 -2
- data/web/views/retry.erb +2 -2
- data/web/views/scheduled.erb +5 -2
- metadata +31 -49
- data/.circleci/config.yml +0 -61
- data/.github/issue_template.md +0 -11
- data/.travis.yml +0 -11
- data/bin/sidekiqctl +0 -20
- data/lib/sidekiq/core_ext.rb +0 -1
- data/lib/sidekiq/ctl.rb +0 -221
- data/lib/sidekiq/logging.rb +0 -122
- data/lib/sidekiq/middleware/server/active_record.rb +0 -23
data/lib/sidekiq/web/helpers.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
require "set"
|
5
|
+
require "yaml"
|
6
|
+
require "cgi"
|
6
7
|
|
7
8
|
module Sidekiq
|
8
9
|
# This is not a public API
|
9
10
|
module WebHelpers
|
10
11
|
def strings(lang)
|
11
|
-
|
12
|
-
|
12
|
+
@strings ||= {}
|
13
|
+
@strings[lang] ||= begin
|
13
14
|
# Allow sidekiq-web extensions to add locale paths
|
14
15
|
# so extensions can be localized
|
15
16
|
settings.locales.each_with_object({}) do |path, global|
|
@@ -22,19 +23,19 @@ module Sidekiq
|
|
22
23
|
end
|
23
24
|
|
24
25
|
def clear_caches
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
@strings = nil
|
27
|
+
@locale_files = nil
|
28
|
+
@available_locales = nil
|
28
29
|
end
|
29
30
|
|
30
31
|
def locale_files
|
31
|
-
|
32
|
+
@locale_files ||= settings.locales.flat_map { |path|
|
32
33
|
Dir["#{path}/*.yml"]
|
33
|
-
|
34
|
+
}
|
34
35
|
end
|
35
36
|
|
36
37
|
def available_locales
|
37
|
-
|
38
|
+
@available_locales ||= locale_files.map { |path| File.basename(path, ".yml") }.uniq
|
38
39
|
end
|
39
40
|
|
40
41
|
def find_locale_files(lang)
|
@@ -63,32 +64,35 @@ module Sidekiq
|
|
63
64
|
end
|
64
65
|
|
65
66
|
def poll_path
|
66
|
-
if current_path !=
|
67
|
-
root_path + current_path
|
67
|
+
if current_path != "" && params["poll"]
|
68
|
+
path = root_path + current_path
|
69
|
+
query_string = to_query_string(params.slice(*params.keys - %w[page poll]))
|
70
|
+
path += "?#{query_string}" unless query_string.empty?
|
71
|
+
path
|
68
72
|
else
|
69
73
|
""
|
70
74
|
end
|
71
75
|
end
|
72
76
|
|
73
77
|
def text_direction
|
74
|
-
get_locale[
|
78
|
+
get_locale["TextDirection"] || "ltr"
|
75
79
|
end
|
76
80
|
|
77
81
|
def rtl?
|
78
|
-
text_direction ==
|
82
|
+
text_direction == "rtl"
|
79
83
|
end
|
80
84
|
|
81
85
|
# See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
|
82
86
|
def user_preferred_languages
|
83
|
-
languages = env[
|
84
|
-
languages.to_s.downcase.gsub(/\s+/,
|
85
|
-
locale, quality = language.split(
|
86
|
-
locale
|
87
|
+
languages = env["HTTP_ACCEPT_LANGUAGE"]
|
88
|
+
languages.to_s.downcase.gsub(/\s+/, "").split(",").map { |language|
|
89
|
+
locale, quality = language.split(";q=", 2)
|
90
|
+
locale = nil if locale == "*" # Ignore wildcards
|
87
91
|
quality = quality ? quality.to_f : 1.0
|
88
92
|
[locale, quality]
|
89
|
-
|
93
|
+
}.sort { |(_, left), (_, right)|
|
90
94
|
right <=> left
|
91
|
-
|
95
|
+
}.map(&:first).compact
|
92
96
|
end
|
93
97
|
|
94
98
|
# Given an Accept-Language header like "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,ru;q=0.2"
|
@@ -97,31 +101,38 @@ module Sidekiq
|
|
97
101
|
# Inspiration taken from https://github.com/iain/http_accept_language/blob/master/lib/http_accept_language/parser.rb
|
98
102
|
def locale
|
99
103
|
@locale ||= begin
|
100
|
-
matched_locale = user_preferred_languages.map
|
101
|
-
preferred_language = preferred.split(
|
104
|
+
matched_locale = user_preferred_languages.map { |preferred|
|
105
|
+
preferred_language = preferred.split("-", 2).first
|
102
106
|
|
103
|
-
lang_group = available_locales.select
|
104
|
-
preferred_language == available.split(
|
105
|
-
|
107
|
+
lang_group = available_locales.select { |available|
|
108
|
+
preferred_language == available.split("-", 2).first
|
109
|
+
}
|
106
110
|
|
107
111
|
lang_group.find { |lang| lang == preferred } || lang_group.min_by(&:length)
|
108
|
-
|
112
|
+
}.compact.first
|
109
113
|
|
110
|
-
matched_locale ||
|
114
|
+
matched_locale || "en"
|
111
115
|
end
|
112
116
|
end
|
113
117
|
|
118
|
+
# within is used by Sidekiq Pro
|
119
|
+
def display_tags(job, within = nil)
|
120
|
+
job.tags.map { |tag|
|
121
|
+
"<span class='jobtag label label-info'>#{::Rack::Utils.escape_html(tag)}</span>"
|
122
|
+
}.join(" ")
|
123
|
+
end
|
124
|
+
|
114
125
|
# mperham/sidekiq#3243
|
115
126
|
def unfiltered?
|
116
|
-
yield unless env[
|
127
|
+
yield unless env["PATH_INFO"].start_with?("/filter/")
|
117
128
|
end
|
118
129
|
|
119
130
|
def get_locale
|
120
131
|
strings(locale)
|
121
132
|
end
|
122
133
|
|
123
|
-
def t(msg, options={})
|
124
|
-
string = get_locale[msg] || strings(
|
134
|
+
def t(msg, options = {})
|
135
|
+
string = get_locale[msg] || strings("en")[msg] || msg
|
125
136
|
if options.empty?
|
126
137
|
string
|
127
138
|
else
|
@@ -129,6 +140,10 @@ module Sidekiq
|
|
129
140
|
end
|
130
141
|
end
|
131
142
|
|
143
|
+
def sort_direction_label
|
144
|
+
params[:direction] == "asc" ? "↑" : "↓"
|
145
|
+
end
|
146
|
+
|
132
147
|
def workers
|
133
148
|
@workers ||= Sidekiq::Workers.new
|
134
149
|
end
|
@@ -141,12 +156,6 @@ module Sidekiq
|
|
141
156
|
@stats ||= Sidekiq::Stats.new
|
142
157
|
end
|
143
158
|
|
144
|
-
def retries_with_score(score)
|
145
|
-
Sidekiq.redis do |conn|
|
146
|
-
conn.zrangebyscore('retry', score, score)
|
147
|
-
end.map { |msg| Sidekiq.load_json(msg) }
|
148
|
-
end
|
149
|
-
|
150
159
|
def redis_connection
|
151
160
|
Sidekiq.redis do |conn|
|
152
161
|
c = conn.connection
|
@@ -163,24 +172,24 @@ module Sidekiq
|
|
163
172
|
end
|
164
173
|
|
165
174
|
def root_path
|
166
|
-
"#{env[
|
175
|
+
"#{env["SCRIPT_NAME"]}/"
|
167
176
|
end
|
168
177
|
|
169
178
|
def current_path
|
170
|
-
@current_path ||= request.path_info.gsub(/^\//,
|
179
|
+
@current_path ||= request.path_info.gsub(/^\//, "")
|
171
180
|
end
|
172
181
|
|
173
182
|
def current_status
|
174
|
-
workers.size == 0 ?
|
183
|
+
workers.size == 0 ? "idle" : "active"
|
175
184
|
end
|
176
185
|
|
177
186
|
def relative_time(time)
|
178
187
|
stamp = time.getutc.iso8601
|
179
|
-
%
|
188
|
+
%(<time class="ltr" dir="ltr" title="#{stamp}" datetime="#{stamp}">#{time}</time>)
|
180
189
|
end
|
181
190
|
|
182
191
|
def job_params(job, score)
|
183
|
-
"#{score}-#{job[
|
192
|
+
"#{score}-#{job["jid"]}"
|
184
193
|
end
|
185
194
|
|
186
195
|
def parse_params(params)
|
@@ -188,18 +197,19 @@ module Sidekiq
|
|
188
197
|
[score.to_f, jid]
|
189
198
|
end
|
190
199
|
|
191
|
-
SAFE_QPARAMS = %w
|
200
|
+
SAFE_QPARAMS = %w[page poll direction]
|
192
201
|
|
193
202
|
# Merge options with current params, filter safe params, and stringify to query string
|
194
203
|
def qparams(options)
|
195
|
-
|
196
|
-
options.keys.each do |key|
|
197
|
-
options[key.to_s] = options.delete(key)
|
198
|
-
end
|
204
|
+
stringified_options = options.transform_keys(&:to_s)
|
199
205
|
|
200
|
-
params.merge(
|
206
|
+
to_query_string(params.merge(stringified_options))
|
207
|
+
end
|
208
|
+
|
209
|
+
def to_query_string(params)
|
210
|
+
params.map { |key, value|
|
201
211
|
SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
|
202
|
-
|
212
|
+
}.compact.join("&")
|
203
213
|
end
|
204
214
|
|
205
215
|
def truncate(text, truncate_after_chars = 2000)
|
@@ -207,40 +217,38 @@ module Sidekiq
|
|
207
217
|
end
|
208
218
|
|
209
219
|
def display_args(args, truncate_after_chars = 2000)
|
210
|
-
return "Invalid job payload, args is nil" if args
|
211
|
-
return "Invalid job payload, args must be an Array, not #{args.class.name}"
|
220
|
+
return "Invalid job payload, args is nil" if args.nil?
|
221
|
+
return "Invalid job payload, args must be an Array, not #{args.class.name}" unless args.is_a?(Array)
|
212
222
|
|
213
223
|
begin
|
214
|
-
args.map
|
224
|
+
args.map { |arg|
|
215
225
|
h(truncate(to_display(arg), truncate_after_chars))
|
216
|
-
|
226
|
+
}.join(", ")
|
217
227
|
rescue
|
218
228
|
"Illegal job arguments: #{h args.inspect}"
|
219
229
|
end
|
220
230
|
end
|
221
231
|
|
222
232
|
def csrf_tag
|
223
|
-
"<input type='hidden' name='authenticity_token' value='#{
|
233
|
+
"<input type='hidden' name='authenticity_token' value='#{env[:csrf_token]}'/>"
|
224
234
|
end
|
225
235
|
|
226
236
|
def to_display(arg)
|
237
|
+
arg.inspect
|
238
|
+
rescue
|
227
239
|
begin
|
228
|
-
arg.
|
229
|
-
rescue
|
230
|
-
|
231
|
-
arg.to_s
|
232
|
-
rescue => ex
|
233
|
-
"Cannot display argument: [#{ex.class.name}] #{ex.message}"
|
234
|
-
end
|
240
|
+
arg.to_s
|
241
|
+
rescue => ex
|
242
|
+
"Cannot display argument: [#{ex.class.name}] #{ex.message}"
|
235
243
|
end
|
236
244
|
end
|
237
245
|
|
238
|
-
RETRY_JOB_KEYS = Set.new(%w
|
246
|
+
RETRY_JOB_KEYS = Set.new(%w[
|
239
247
|
queue class args retry_count retried_at failed_at
|
240
248
|
jid error_message error_class backtrace
|
241
249
|
error_backtrace enqueued_at retry wrapped
|
242
|
-
created_at
|
243
|
-
)
|
250
|
+
created_at tags
|
251
|
+
])
|
244
252
|
|
245
253
|
def retry_extra_items(retry_job)
|
246
254
|
@retry_extra_items ||= {}.tap do |extra|
|
@@ -250,15 +258,27 @@ module Sidekiq
|
|
250
258
|
end
|
251
259
|
end
|
252
260
|
|
261
|
+
def format_memory(rss_kb)
|
262
|
+
return "" if rss_kb.nil? || rss_kb == 0
|
263
|
+
|
264
|
+
if rss_kb < 100_000
|
265
|
+
"#{number_with_delimiter(rss_kb)} KB"
|
266
|
+
else
|
267
|
+
"#{number_with_delimiter((rss_kb / 1024.0).to_i)} MB"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
253
271
|
def number_with_delimiter(number)
|
272
|
+
return "" if number.nil?
|
273
|
+
|
254
274
|
begin
|
255
275
|
Float(number)
|
256
276
|
rescue ArgumentError, TypeError
|
257
277
|
return number
|
258
278
|
end
|
259
279
|
|
260
|
-
options = {delimiter:
|
261
|
-
parts = number.to_s.to_str.split(
|
280
|
+
options = {delimiter: ",", separator: "."}
|
281
|
+
parts = number.to_s.to_str.split(".")
|
262
282
|
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
|
263
283
|
parts.join(options[:separator])
|
264
284
|
end
|
@@ -266,8 +286,8 @@ module Sidekiq
|
|
266
286
|
def h(text)
|
267
287
|
::Rack::Utils.escape_html(text)
|
268
288
|
rescue ArgumentError => e
|
269
|
-
raise unless e.message.eql?(
|
270
|
-
text.encode!(
|
289
|
+
raise unless e.message.eql?("invalid byte sequence in UTF-8")
|
290
|
+
text.encode!("UTF-16", "UTF-8", invalid: :replace, replace: "").encode!("UTF-8", "UTF-16")
|
271
291
|
retry
|
272
292
|
end
|
273
293
|
|
@@ -284,7 +304,7 @@ module Sidekiq
|
|
284
304
|
end
|
285
305
|
|
286
306
|
def environment_title_prefix
|
287
|
-
environment = Sidekiq.options[:environment] || ENV[
|
307
|
+
environment = Sidekiq.options[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
288
308
|
|
289
309
|
"[#{environment.upcase}] " unless environment == "production"
|
290
310
|
end
|
@@ -294,30 +314,30 @@ module Sidekiq
|
|
294
314
|
end
|
295
315
|
|
296
316
|
def server_utc_time
|
297
|
-
Time.now.utc.strftime(
|
317
|
+
Time.now.utc.strftime("%H:%M:%S UTC")
|
298
318
|
end
|
299
319
|
|
300
320
|
def redis_connection_and_namespace
|
301
321
|
@redis_connection_and_namespace ||= begin
|
302
|
-
namespace_suffix = namespace
|
322
|
+
namespace_suffix = namespace.nil? ? "" : "##{namespace}"
|
303
323
|
"#{redis_connection}#{namespace_suffix}"
|
304
324
|
end
|
305
325
|
end
|
306
326
|
|
307
327
|
def retry_or_delete_or_kill(job, params)
|
308
|
-
if params[
|
328
|
+
if params["retry"]
|
309
329
|
job.retry
|
310
|
-
elsif params[
|
330
|
+
elsif params["delete"]
|
311
331
|
job.delete
|
312
|
-
elsif params[
|
332
|
+
elsif params["kill"]
|
313
333
|
job.kill
|
314
334
|
end
|
315
335
|
end
|
316
336
|
|
317
337
|
def delete_or_add_queue(job, params)
|
318
|
-
if params[
|
338
|
+
if params["delete"]
|
319
339
|
job.delete
|
320
|
-
elsif params[
|
340
|
+
elsif params["add_to_queue"]
|
321
341
|
job.add_to_queue
|
322
342
|
end
|
323
343
|
end
|
data/lib/sidekiq/web/router.rb
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
require "rack"
|
3
4
|
|
4
5
|
module Sidekiq
|
5
6
|
module WebRouter
|
6
|
-
GET =
|
7
|
-
DELETE =
|
8
|
-
POST =
|
9
|
-
PUT =
|
10
|
-
PATCH =
|
11
|
-
HEAD =
|
7
|
+
GET = "GET"
|
8
|
+
DELETE = "DELETE"
|
9
|
+
POST = "POST"
|
10
|
+
PUT = "PUT"
|
11
|
+
PATCH = "PATCH"
|
12
|
+
HEAD = "HEAD"
|
12
13
|
|
13
|
-
ROUTE_PARAMS =
|
14
|
-
REQUEST_METHOD =
|
15
|
-
PATH_INFO =
|
14
|
+
ROUTE_PARAMS = "rack.route_params"
|
15
|
+
REQUEST_METHOD = "REQUEST_METHOD"
|
16
|
+
PATH_INFO = "PATH_INFO"
|
16
17
|
|
17
18
|
def get(path, &block)
|
18
19
|
route(GET, path, &block)
|
@@ -35,7 +36,7 @@ module Sidekiq
|
|
35
36
|
end
|
36
37
|
|
37
38
|
def route(method, path, &block)
|
38
|
-
@routes ||= {
|
39
|
+
@routes ||= {GET => [], POST => [], PUT => [], PATCH => [], DELETE => [], HEAD => []}
|
39
40
|
|
40
41
|
@routes[method] << WebRoute.new(method, path, block)
|
41
42
|
@routes[HEAD] << WebRoute.new(method, path, block) if method == GET
|
@@ -50,7 +51,8 @@ module Sidekiq
|
|
50
51
|
path_info = "/" if path_info == ""
|
51
52
|
|
52
53
|
@routes[request_method].each do |route|
|
53
|
-
|
54
|
+
params = route.match(request_method, path_info)
|
55
|
+
if params
|
54
56
|
env[ROUTE_PARAMS] = params
|
55
57
|
|
56
58
|
return WebAction.new(env, route.block)
|
@@ -64,7 +66,7 @@ module Sidekiq
|
|
64
66
|
class WebRoute
|
65
67
|
attr_accessor :request_method, :pattern, :block, :name
|
66
68
|
|
67
|
-
NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([
|
69
|
+
NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([^.:$\/]+)/
|
68
70
|
|
69
71
|
def initialize(request_method, pattern, block)
|
70
72
|
@request_method = request_method
|
@@ -77,7 +79,7 @@ module Sidekiq
|
|
77
79
|
end
|
78
80
|
|
79
81
|
def compile
|
80
|
-
if pattern.match(NAMED_SEGMENTS_PATTERN)
|
82
|
+
if pattern.match?(NAMED_SEGMENTS_PATTERN)
|
81
83
|
p = pattern.gsub(NAMED_SEGMENTS_PATTERN, '/\1(?<\2>[^$/]+)')
|
82
84
|
|
83
85
|
Regexp.new("\\A#{p}\\Z")
|
@@ -91,9 +93,8 @@ module Sidekiq
|
|
91
93
|
when String
|
92
94
|
{} if path == matcher
|
93
95
|
else
|
94
|
-
|
95
|
-
|
96
|
-
end
|
96
|
+
path_match = path.match(matcher)
|
97
|
+
path_match&.named_captures&.transform_keys(&:to_sym)
|
97
98
|
end
|
98
99
|
end
|
99
100
|
end
|
data/lib/sidekiq/web.rb
CHANGED
@@ -1,20 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'erb'
|
3
2
|
|
4
|
-
require
|
5
|
-
require 'sidekiq/api'
|
6
|
-
require 'sidekiq/paginator'
|
7
|
-
require 'sidekiq/web/helpers'
|
3
|
+
require "erb"
|
8
4
|
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
5
|
+
require "sidekiq"
|
6
|
+
require "sidekiq/api"
|
7
|
+
require "sidekiq/paginator"
|
8
|
+
require "sidekiq/web/helpers"
|
12
9
|
|
13
|
-
require
|
10
|
+
require "sidekiq/web/router"
|
11
|
+
require "sidekiq/web/action"
|
12
|
+
require "sidekiq/web/application"
|
13
|
+
require "sidekiq/web/csrf_protection"
|
14
14
|
|
15
|
-
require
|
16
|
-
|
17
|
-
require
|
15
|
+
require "rack/content_length"
|
16
|
+
|
17
|
+
require "rack/builder"
|
18
|
+
require "rack/file"
|
19
|
+
require "rack/session/cookie"
|
18
20
|
|
19
21
|
module Sidekiq
|
20
22
|
class Web
|
@@ -25,12 +27,12 @@ module Sidekiq
|
|
25
27
|
ASSETS = "#{ROOT}/assets"
|
26
28
|
|
27
29
|
DEFAULT_TABS = {
|
28
|
-
"Dashboard" =>
|
29
|
-
"Busy"
|
30
|
-
"Queues"
|
31
|
-
"Retries"
|
32
|
-
"Scheduled" =>
|
33
|
-
"Dead"
|
30
|
+
"Dashboard" => "",
|
31
|
+
"Busy" => "busy",
|
32
|
+
"Queues" => "queues",
|
33
|
+
"Retries" => "retries",
|
34
|
+
"Scheduled" => "scheduled",
|
35
|
+
"Dead" => "morgue"
|
34
36
|
}
|
35
37
|
|
36
38
|
class << self
|
@@ -64,11 +66,11 @@ module Sidekiq
|
|
64
66
|
end
|
65
67
|
|
66
68
|
def enable(*opts)
|
67
|
-
opts.each {|key| set(key, true) }
|
69
|
+
opts.each { |key| set(key, true) }
|
68
70
|
end
|
69
71
|
|
70
72
|
def disable(*opts)
|
71
|
-
opts.each {|key| set(key, false) }
|
73
|
+
opts.each { |key| set(key, false) }
|
72
74
|
end
|
73
75
|
|
74
76
|
# Helper for the Sinatra syntax: Sidekiq::Web.set(:session_secret, Rails.application.secrets...)
|
@@ -81,10 +83,10 @@ module Sidekiq
|
|
81
83
|
end
|
82
84
|
|
83
85
|
def self.inherited(child)
|
84
|
-
child.app_url =
|
85
|
-
child.session_secret =
|
86
|
-
child.redis_pool =
|
87
|
-
child.sessions =
|
86
|
+
child.app_url = app_url
|
87
|
+
child.session_secret = session_secret
|
88
|
+
child.redis_pool = redis_pool
|
89
|
+
child.sessions = sessions
|
88
90
|
end
|
89
91
|
|
90
92
|
def settings
|
@@ -113,11 +115,11 @@ module Sidekiq
|
|
113
115
|
end
|
114
116
|
|
115
117
|
def enable(*opts)
|
116
|
-
opts.each {|key| set(key, true) }
|
118
|
+
opts.each { |key| set(key, true) }
|
117
119
|
end
|
118
120
|
|
119
121
|
def disable(*opts)
|
120
|
-
opts.each {|key| set(key, false) }
|
122
|
+
opts.each { |key| set(key, false) }
|
121
123
|
end
|
122
124
|
|
123
125
|
def set(attribute, value)
|
@@ -145,32 +147,39 @@ module Sidekiq
|
|
145
147
|
private
|
146
148
|
|
147
149
|
def using?(middleware)
|
148
|
-
middlewares.any? do |(m,_)|
|
149
|
-
m.
|
150
|
+
middlewares.any? do |(m, _)|
|
151
|
+
m.is_a?(Array) && (m[0] == middleware || m[0].is_a?(middleware))
|
150
152
|
end
|
151
153
|
end
|
152
154
|
|
153
155
|
def build_sessions
|
154
156
|
middlewares = self.middlewares
|
155
157
|
|
156
|
-
unless using?(::Rack::Protection) || ENV['RACK_ENV'] == 'test'
|
157
|
-
middlewares.unshift [[::Rack::Protection, { use: :authenticity_token }], nil]
|
158
|
-
end
|
159
|
-
|
160
158
|
s = sessions
|
161
|
-
return unless s
|
162
159
|
|
163
|
-
|
164
|
-
|
165
|
-
|
160
|
+
# turn on CSRF protection if sessions are enabled and this is not the test env
|
161
|
+
if s && !using?(CsrfProtection) && ENV["RACK_ENV"] != "test"
|
162
|
+
middlewares.unshift [[CsrfProtection], nil]
|
163
|
+
end
|
164
|
+
|
165
|
+
if s && !using?(::Rack::Session::Cookie)
|
166
|
+
unless (secret = Web.session_secret)
|
167
|
+
require "securerandom"
|
166
168
|
secret = SecureRandom.hex(64)
|
167
169
|
end
|
168
170
|
|
169
|
-
options = {
|
171
|
+
options = {secret: secret}
|
170
172
|
options = options.merge(s.to_hash) if s.respond_to? :to_hash
|
171
173
|
|
172
174
|
middlewares.unshift [[::Rack::Session::Cookie, options], nil]
|
173
175
|
end
|
176
|
+
|
177
|
+
# Since Sidekiq::WebApplication no longer calculates its own
|
178
|
+
# Content-Length response header, we must ensure that the Rack middleware
|
179
|
+
# that does this is loaded
|
180
|
+
unless using? ::Rack::ContentLength
|
181
|
+
middlewares.unshift [[::Rack::ContentLength], nil]
|
182
|
+
end
|
174
183
|
end
|
175
184
|
|
176
185
|
def build
|
@@ -180,13 +189,13 @@ module Sidekiq
|
|
180
189
|
klass = self.class
|
181
190
|
|
182
191
|
::Rack::Builder.new do
|
183
|
-
%w
|
192
|
+
%w[stylesheets javascripts images].each do |asset_dir|
|
184
193
|
map "/#{asset_dir}" do
|
185
|
-
run ::Rack::File.new("#{ASSETS}/#{asset_dir}", {
|
194
|
+
run ::Rack::File.new("#{ASSETS}/#{asset_dir}", {"Cache-Control" => "public, max-age=86400"})
|
186
195
|
end
|
187
196
|
end
|
188
197
|
|
189
|
-
middlewares.each {|middleware, block| use(*middleware, &block) }
|
198
|
+
middlewares.each { |middleware, block| use(*middleware, &block) }
|
190
199
|
|
191
200
|
run WebApplication.new(klass)
|
192
201
|
end
|
@@ -196,18 +205,9 @@ module Sidekiq
|
|
196
205
|
Sidekiq::WebApplication.helpers WebHelpers
|
197
206
|
Sidekiq::WebApplication.helpers Sidekiq::Paginator
|
198
207
|
|
199
|
-
Sidekiq::WebAction.class_eval
|
200
|
-
|
201
|
-
|
202
|
-
if defined?(::ActionDispatch::Request::Session) &&
|
203
|
-
!::ActionDispatch::Request::Session.method_defined?(:each)
|
204
|
-
# mperham/sidekiq#2460
|
205
|
-
# Rack apps can't reuse the Rails session store without
|
206
|
-
# this monkeypatch, fixed in Rails 5.
|
207
|
-
class ActionDispatch::Request::Session
|
208
|
-
def each(&block)
|
209
|
-
hash = self.to_hash
|
210
|
-
hash.each(&block)
|
208
|
+
Sidekiq::WebAction.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
209
|
+
def _render
|
210
|
+
#{ERB.new(File.read(Web::LAYOUT)).src}
|
211
211
|
end
|
212
|
-
|
212
|
+
RUBY
|
213
213
|
end
|