sidekiq 6.5.12 → 7.3.9
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 +4 -4
- data/Changes.md +340 -20
- data/README.md +43 -35
- data/bin/multi_queue_bench +271 -0
- data/bin/sidekiq +3 -8
- data/bin/sidekiqload +213 -118
- data/bin/sidekiqmon +3 -0
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +75 -0
- data/lib/generators/sidekiq/job_generator.rb +2 -0
- data/lib/sidekiq/api.rb +243 -162
- data/lib/sidekiq/capsule.rb +132 -0
- data/lib/sidekiq/cli.rb +60 -75
- data/lib/sidekiq/client.rb +87 -38
- data/lib/sidekiq/component.rb +26 -1
- data/lib/sidekiq/config.rb +311 -0
- data/lib/sidekiq/deploy.rb +64 -0
- data/lib/sidekiq/embedded.rb +63 -0
- data/lib/sidekiq/fetch.rb +11 -14
- data/lib/sidekiq/iterable_job.rb +55 -0
- data/lib/sidekiq/job/interrupt_handler.rb +24 -0
- data/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
- data/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
- data/lib/sidekiq/job/iterable/enumerators.rb +135 -0
- data/lib/sidekiq/job/iterable.rb +294 -0
- data/lib/sidekiq/job.rb +382 -10
- data/lib/sidekiq/job_logger.rb +8 -7
- data/lib/sidekiq/job_retry.rb +42 -19
- data/lib/sidekiq/job_util.rb +53 -15
- data/lib/sidekiq/launcher.rb +71 -65
- data/lib/sidekiq/logger.rb +2 -27
- data/lib/sidekiq/manager.rb +9 -11
- data/lib/sidekiq/metrics/query.rb +9 -4
- data/lib/sidekiq/metrics/shared.rb +21 -9
- data/lib/sidekiq/metrics/tracking.rb +40 -26
- data/lib/sidekiq/middleware/chain.rb +19 -18
- data/lib/sidekiq/middleware/current_attributes.rb +85 -20
- data/lib/sidekiq/middleware/modules.rb +2 -0
- data/lib/sidekiq/monitor.rb +18 -4
- data/lib/sidekiq/paginator.rb +8 -2
- data/lib/sidekiq/processor.rb +62 -57
- data/lib/sidekiq/rails.rb +27 -10
- data/lib/sidekiq/redis_client_adapter.rb +31 -71
- data/lib/sidekiq/redis_connection.rb +44 -115
- data/lib/sidekiq/ring_buffer.rb +2 -0
- data/lib/sidekiq/scheduled.rb +22 -23
- data/lib/sidekiq/systemd.rb +2 -0
- data/lib/sidekiq/testing.rb +37 -46
- data/lib/sidekiq/transaction_aware_client.rb +11 -5
- data/lib/sidekiq/version.rb +6 -1
- data/lib/sidekiq/web/action.rb +29 -7
- data/lib/sidekiq/web/application.rb +82 -28
- data/lib/sidekiq/web/csrf_protection.rb +10 -7
- data/lib/sidekiq/web/helpers.rb +110 -49
- data/lib/sidekiq/web/router.rb +5 -2
- data/lib/sidekiq/web.rb +70 -17
- data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
- data/lib/sidekiq.rb +78 -274
- data/sidekiq.gemspec +13 -10
- data/web/assets/javascripts/application.js +44 -0
- data/web/assets/javascripts/base-charts.js +106 -0
- data/web/assets/javascripts/dashboard-charts.js +194 -0
- data/web/assets/javascripts/dashboard.js +17 -233
- data/web/assets/javascripts/metrics.js +151 -115
- data/web/assets/stylesheets/application-dark.css +4 -0
- data/web/assets/stylesheets/application-rtl.css +10 -89
- data/web/assets/stylesheets/application.css +56 -296
- data/web/locales/ar.yml +70 -70
- data/web/locales/cs.yml +62 -62
- data/web/locales/da.yml +60 -53
- data/web/locales/de.yml +65 -65
- data/web/locales/el.yml +2 -7
- data/web/locales/en.yml +81 -71
- data/web/locales/es.yml +68 -68
- data/web/locales/fa.yml +65 -65
- data/web/locales/fr.yml +80 -67
- data/web/locales/gd.yml +98 -0
- data/web/locales/he.yml +65 -64
- data/web/locales/hi.yml +59 -59
- data/web/locales/it.yml +85 -54
- data/web/locales/ja.yml +67 -70
- data/web/locales/ko.yml +52 -52
- data/web/locales/lt.yml +66 -66
- data/web/locales/nb.yml +61 -61
- data/web/locales/nl.yml +52 -52
- data/web/locales/pl.yml +45 -45
- data/web/locales/pt-br.yml +78 -69
- data/web/locales/pt.yml +51 -51
- data/web/locales/ru.yml +67 -66
- data/web/locales/sv.yml +53 -53
- data/web/locales/ta.yml +60 -60
- data/web/locales/tr.yml +100 -0
- data/web/locales/uk.yml +85 -61
- data/web/locales/ur.yml +64 -64
- data/web/locales/vi.yml +67 -67
- data/web/locales/zh-cn.yml +20 -19
- data/web/locales/zh-tw.yml +10 -2
- data/web/views/_footer.erb +16 -2
- data/web/views/_job_info.erb +18 -2
- data/web/views/_metrics_period_select.erb +12 -0
- data/web/views/_paging.erb +2 -0
- data/web/views/_poll_link.erb +1 -1
- data/web/views/_summary.erb +7 -7
- data/web/views/busy.erb +46 -35
- data/web/views/dashboard.erb +32 -8
- data/web/views/filtering.erb +6 -0
- data/web/views/layout.erb +6 -6
- data/web/views/metrics.erb +47 -26
- data/web/views/metrics_for_job.erb +43 -71
- data/web/views/morgue.erb +7 -11
- data/web/views/queue.erb +11 -15
- data/web/views/queues.erb +9 -3
- data/web/views/retries.erb +5 -9
- data/web/views/scheduled.erb +12 -13
- metadata +66 -41
- data/lib/sidekiq/delay.rb +0 -43
- data/lib/sidekiq/extensions/action_mailer.rb +0 -48
- data/lib/sidekiq/extensions/active_record.rb +0 -43
- data/lib/sidekiq/extensions/class_methods.rb +0 -43
- data/lib/sidekiq/extensions/generic_proxy.rb +0 -33
- data/lib/sidekiq/metrics/deploy.rb +0 -47
- data/lib/sidekiq/worker.rb +0 -370
- data/web/assets/javascripts/graph.js +0 -16
- /data/{LICENSE → LICENSE.txt} +0 -0
@@ -5,11 +5,18 @@ require "sidekiq/client"
|
|
5
5
|
|
6
6
|
module Sidekiq
|
7
7
|
class TransactionAwareClient
|
8
|
-
def initialize(
|
9
|
-
@redis_client = Client.new(
|
8
|
+
def initialize(pool: nil, config: nil)
|
9
|
+
@redis_client = Client.new(pool: pool, config: config)
|
10
|
+
end
|
11
|
+
|
12
|
+
def batching?
|
13
|
+
Thread.current[:sidekiq_batch]
|
10
14
|
end
|
11
15
|
|
12
16
|
def push(item)
|
17
|
+
# 6160 we can't support both Sidekiq::Batch and transactions.
|
18
|
+
return @redis_client.push(item) if batching?
|
19
|
+
|
13
20
|
# pre-allocate the JID so we can return it immediately and
|
14
21
|
# save it to the database as part of the transaction.
|
15
22
|
item["jid"] ||= SecureRandom.hex(12)
|
@@ -34,11 +41,10 @@ module Sidekiq
|
|
34
41
|
begin
|
35
42
|
require "after_commit_everywhere"
|
36
43
|
rescue LoadError
|
37
|
-
|
38
|
-
raise
|
44
|
+
raise %q(You need to add `gem "after_commit_everywhere"` to your Gemfile to use Sidekiq's transactional client)
|
39
45
|
end
|
40
46
|
|
41
|
-
default_job_options["client_class"] = Sidekiq::TransactionAwareClient
|
47
|
+
Sidekiq.default_job_options["client_class"] = Sidekiq::TransactionAwareClient
|
42
48
|
Sidekiq::JobUtil::TRANSIENT_ATTRIBUTES << "client_class"
|
43
49
|
true
|
44
50
|
end
|
data/lib/sidekiq/version.rb
CHANGED
data/lib/sidekiq/web/action.rb
CHANGED
@@ -15,13 +15,19 @@ module Sidekiq
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def halt(res)
|
18
|
-
throw :halt, [res, {
|
18
|
+
throw :halt, [res, {Rack::CONTENT_TYPE => "text/plain"}, [res.to_s]]
|
19
19
|
end
|
20
20
|
|
21
21
|
def redirect(location)
|
22
|
-
throw :halt, [302, {
|
22
|
+
throw :halt, [302, {Web::LOCATION => "#{request.base_url}#{location}"}, []]
|
23
23
|
end
|
24
24
|
|
25
|
+
def reload_page
|
26
|
+
current_location = request.referer.gsub(request.base_url, "")
|
27
|
+
redirect current_location
|
28
|
+
end
|
29
|
+
|
30
|
+
# deprecated, will warn in 8.0
|
25
31
|
def params
|
26
32
|
indifferent_hash = Hash.new { |hash, key| hash[key.to_s] if Symbol === key }
|
27
33
|
|
@@ -31,8 +37,19 @@ module Sidekiq
|
|
31
37
|
indifferent_hash
|
32
38
|
end
|
33
39
|
|
34
|
-
|
35
|
-
|
40
|
+
# Use like `url_params("page")` within your action blocks
|
41
|
+
def url_params(key)
|
42
|
+
request.params[key]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Use like `route_params(:name)` within your action blocks
|
46
|
+
# key is required in 8.0, nil is only used for backwards compatibility
|
47
|
+
def route_params(key = nil)
|
48
|
+
if key
|
49
|
+
env[WebRouter::ROUTE_PARAMS][key]
|
50
|
+
else
|
51
|
+
env[WebRouter::ROUTE_PARAMS]
|
52
|
+
end
|
36
53
|
end
|
37
54
|
|
38
55
|
def session
|
@@ -42,8 +59,13 @@ module Sidekiq
|
|
42
59
|
def erb(content, options = {})
|
43
60
|
if content.is_a? Symbol
|
44
61
|
unless respond_to?(:"_erb_#{content}")
|
45
|
-
|
46
|
-
|
62
|
+
views = options[:views] || Web.settings.views
|
63
|
+
filename = "#{views}/#{content}.erb"
|
64
|
+
src = ERB.new(File.read(filename)).src
|
65
|
+
|
66
|
+
# Need to use lineno less by 1 because erb generates a
|
67
|
+
# comment before the source code.
|
68
|
+
WebAction.class_eval <<-RUBY, filename, -1 # standard:disable Style/EvalWithLocation
|
47
69
|
def _erb_#{content}
|
48
70
|
#{src}
|
49
71
|
end
|
@@ -68,7 +90,7 @@ module Sidekiq
|
|
68
90
|
end
|
69
91
|
|
70
92
|
def json(payload)
|
71
|
-
[200, {
|
93
|
+
[200, {Rack::CONTENT_TYPE => "application/json", Rack::CACHE_CONTROL => "private, no-store"}, [Sidekiq.dump_json(payload)]]
|
72
94
|
end
|
73
95
|
|
74
96
|
def initialize(env, block)
|
@@ -5,7 +5,7 @@ module Sidekiq
|
|
5
5
|
extend WebRouter
|
6
6
|
|
7
7
|
REDIS_KEYS = %w[redis_version uptime_in_days connected_clients used_memory_human used_memory_peak_human]
|
8
|
-
|
8
|
+
CSP_HEADER_TEMPLATE = [
|
9
9
|
"default-src 'self' https: http:",
|
10
10
|
"child-src 'self'",
|
11
11
|
"connect-src 'self' https: http: wss: ws:",
|
@@ -15,11 +15,17 @@ module Sidekiq
|
|
15
15
|
"manifest-src 'self'",
|
16
16
|
"media-src 'self'",
|
17
17
|
"object-src 'none'",
|
18
|
-
"script-src 'self'
|
19
|
-
"style-src 'self' https: http: 'unsafe-inline'",
|
18
|
+
"script-src 'self' 'nonce-!placeholder!'",
|
19
|
+
"style-src 'self' https: http: 'unsafe-inline'", # TODO Nonce in 8.0
|
20
20
|
"worker-src 'self'",
|
21
21
|
"base-uri 'self'"
|
22
22
|
].join("; ").freeze
|
23
|
+
METRICS_PERIODS = {
|
24
|
+
"1h" => 60,
|
25
|
+
"2h" => 120,
|
26
|
+
"4h" => 240,
|
27
|
+
"8h" => 480
|
28
|
+
}
|
23
29
|
|
24
30
|
def initialize(klass)
|
25
31
|
@klass = klass
|
@@ -43,9 +49,9 @@ module Sidekiq
|
|
43
49
|
|
44
50
|
head "/" do
|
45
51
|
# HEAD / is the cheapest heartbeat possible,
|
46
|
-
# it hits Redis to ensure connectivity
|
47
|
-
|
48
|
-
""
|
52
|
+
# it hits Redis to ensure connectivity and returns
|
53
|
+
# the size of the default queue
|
54
|
+
Sidekiq.redis { |c| c.llen("queue:default") }.to_s
|
49
55
|
end
|
50
56
|
|
51
57
|
get "/" do
|
@@ -61,15 +67,25 @@ module Sidekiq
|
|
61
67
|
end
|
62
68
|
|
63
69
|
get "/metrics" do
|
70
|
+
x = params[:substr]
|
71
|
+
class_filter = (x.nil? || x == "") ? nil : Regexp.new(Regexp.escape(x), Regexp::IGNORECASE)
|
72
|
+
|
64
73
|
q = Sidekiq::Metrics::Query.new
|
65
|
-
@
|
74
|
+
@period = h((params[:period] || "")[0..1])
|
75
|
+
@periods = METRICS_PERIODS
|
76
|
+
minutes = @periods.fetch(@period, @periods.values.first)
|
77
|
+
@query_result = q.top_jobs(minutes: minutes, class_filter: class_filter)
|
78
|
+
|
66
79
|
erb(:metrics)
|
67
80
|
end
|
68
81
|
|
69
82
|
get "/metrics/:name" do
|
70
83
|
@name = route_params[:name]
|
84
|
+
@period = h((params[:period] || "")[0..1])
|
71
85
|
q = Sidekiq::Metrics::Query.new
|
72
|
-
@
|
86
|
+
@periods = METRICS_PERIODS
|
87
|
+
minutes = @periods.fetch(@period, @periods.values.first)
|
88
|
+
@query_result = q.for_job(@name, minutes: minutes)
|
73
89
|
erb(:metrics_for_job)
|
74
90
|
end
|
75
91
|
|
@@ -82,11 +98,14 @@ module Sidekiq
|
|
82
98
|
|
83
99
|
post "/busy" do
|
84
100
|
if params["identity"]
|
85
|
-
|
86
|
-
|
87
|
-
|
101
|
+
pro = Sidekiq::ProcessSet[params["identity"]]
|
102
|
+
|
103
|
+
pro.quiet! if params["quiet"]
|
104
|
+
pro.stop! if params["stop"]
|
88
105
|
else
|
89
106
|
processes.each do |pro|
|
107
|
+
next if pro.embedded?
|
108
|
+
|
90
109
|
pro.quiet! if params["quiet"]
|
91
110
|
pro.stop! if params["stop"]
|
92
111
|
end
|
@@ -138,9 +157,15 @@ module Sidekiq
|
|
138
157
|
end
|
139
158
|
|
140
159
|
get "/morgue" do
|
141
|
-
|
142
|
-
|
143
|
-
|
160
|
+
x = params[:substr]
|
161
|
+
|
162
|
+
if x && x != ""
|
163
|
+
@dead = search(Sidekiq::DeadSet.new, x)
|
164
|
+
else
|
165
|
+
@count = (params["count"] || 25).to_i
|
166
|
+
(@current_page, @total_size, @dead) = page("dead", params["page"], @count, reverse: true)
|
167
|
+
@dead = @dead.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
|
168
|
+
end
|
144
169
|
|
145
170
|
erb(:morgue)
|
146
171
|
end
|
@@ -159,7 +184,7 @@ module Sidekiq
|
|
159
184
|
end
|
160
185
|
|
161
186
|
post "/morgue" do
|
162
|
-
redirect(request.path) unless
|
187
|
+
redirect(request.path) unless url_params("key")
|
163
188
|
|
164
189
|
params["key"].each do |key|
|
165
190
|
job = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
|
@@ -182,7 +207,7 @@ module Sidekiq
|
|
182
207
|
end
|
183
208
|
|
184
209
|
post "/morgue/:key" do
|
185
|
-
key = route_params
|
210
|
+
key = route_params(:key)
|
186
211
|
halt(404) unless key
|
187
212
|
|
188
213
|
job = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
|
@@ -192,9 +217,15 @@ module Sidekiq
|
|
192
217
|
end
|
193
218
|
|
194
219
|
get "/retries" do
|
195
|
-
|
196
|
-
|
197
|
-
|
220
|
+
x = url_params("substr")
|
221
|
+
|
222
|
+
if x && x != ""
|
223
|
+
@retries = search(Sidekiq::RetrySet.new, x)
|
224
|
+
else
|
225
|
+
@count = (params["count"] || 25).to_i
|
226
|
+
(@current_page, @total_size, @retries) = page("retry", params["page"], @count)
|
227
|
+
@retries = @retries.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
|
228
|
+
end
|
198
229
|
|
199
230
|
erb(:retries)
|
200
231
|
end
|
@@ -247,9 +278,15 @@ module Sidekiq
|
|
247
278
|
end
|
248
279
|
|
249
280
|
get "/scheduled" do
|
250
|
-
|
251
|
-
|
252
|
-
|
281
|
+
x = params[:substr]
|
282
|
+
|
283
|
+
if x && x != ""
|
284
|
+
@scheduled = search(Sidekiq::ScheduledSet.new, x)
|
285
|
+
else
|
286
|
+
@count = (params["count"] || 25).to_i
|
287
|
+
(@current_page, @total_size, @scheduled) = page("schedule", params["page"], @count)
|
288
|
+
@scheduled = @scheduled.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
|
289
|
+
end
|
253
290
|
|
254
291
|
erb(:scheduled)
|
255
292
|
end
|
@@ -310,12 +347,24 @@ module Sidekiq
|
|
310
347
|
end
|
311
348
|
|
312
349
|
get "/stats/queues" do
|
313
|
-
json Sidekiq::Stats
|
350
|
+
json Sidekiq::Stats.new.queues
|
351
|
+
end
|
352
|
+
|
353
|
+
post "/change_locale" do
|
354
|
+
locale = params["locale"]
|
355
|
+
|
356
|
+
match = available_locales.find { |available|
|
357
|
+
locale == available
|
358
|
+
}
|
359
|
+
|
360
|
+
session[:locale] = match if match
|
361
|
+
|
362
|
+
reload_page
|
314
363
|
end
|
315
364
|
|
316
365
|
def call(env)
|
317
366
|
action = self.class.match(env)
|
318
|
-
return [404, {
|
367
|
+
return [404, {Rack::CONTENT_TYPE => "text/plain", Web::X_CASCADE => "pass"}, ["Not Found"]] unless action
|
319
368
|
|
320
369
|
app = @klass
|
321
370
|
resp = catch(:halt) do
|
@@ -332,16 +381,21 @@ module Sidekiq
|
|
332
381
|
else
|
333
382
|
# rendered content goes here
|
334
383
|
headers = {
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
384
|
+
Rack::CONTENT_TYPE => "text/html",
|
385
|
+
Rack::CACHE_CONTROL => "private, no-store",
|
386
|
+
Web::CONTENT_LANGUAGE => action.locale,
|
387
|
+
Web::CONTENT_SECURITY_POLICY => process_csp(env, CSP_HEADER_TEMPLATE),
|
388
|
+
Web::X_CONTENT_TYPE_OPTIONS => "nosniff"
|
339
389
|
}
|
340
390
|
# we'll let Rack calculate Content-Length for us.
|
341
391
|
[200, headers, [resp]]
|
342
392
|
end
|
343
393
|
end
|
344
394
|
|
395
|
+
def process_csp(env, input)
|
396
|
+
input.gsub("!placeholder!", env[:csp_nonce])
|
397
|
+
end
|
398
|
+
|
345
399
|
def self.helpers(mod = nil, &block)
|
346
400
|
if block
|
347
401
|
WebAction.class_eval(&block)
|
@@ -27,7 +27,6 @@
|
|
27
27
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
28
28
|
|
29
29
|
require "securerandom"
|
30
|
-
require "base64"
|
31
30
|
require "rack/request"
|
32
31
|
|
33
32
|
module Sidekiq
|
@@ -57,12 +56,12 @@ module Sidekiq
|
|
57
56
|
end
|
58
57
|
|
59
58
|
def logger(env)
|
60
|
-
@logger ||=
|
59
|
+
@logger ||= env["rack.logger"] || ::Logger.new(env["rack.errors"])
|
61
60
|
end
|
62
61
|
|
63
62
|
def deny(env)
|
64
63
|
logger(env).warn "attack prevented by #{self.class}"
|
65
|
-
[403, {
|
64
|
+
[403, {Rack::CONTENT_TYPE => "text/plain"}, ["Forbidden"]]
|
66
65
|
end
|
67
66
|
|
68
67
|
def session(env)
|
@@ -116,7 +115,7 @@ module Sidekiq
|
|
116
115
|
sess = session(env)
|
117
116
|
localtoken = sess[:csrf]
|
118
117
|
|
119
|
-
# Checks that Rack::Session::Cookie
|
118
|
+
# Checks that Rack::Session::Cookie actually contains the csrf token
|
120
119
|
return false if localtoken.nil?
|
121
120
|
|
122
121
|
# Rotate the session token after every use
|
@@ -143,7 +142,7 @@ module Sidekiq
|
|
143
142
|
one_time_pad = SecureRandom.random_bytes(token.length)
|
144
143
|
encrypted_token = xor_byte_strings(one_time_pad, token)
|
145
144
|
masked_token = one_time_pad + encrypted_token
|
146
|
-
|
145
|
+
encode_token(masked_token)
|
147
146
|
end
|
148
147
|
|
149
148
|
# Essentially the inverse of +mask_token+.
|
@@ -152,7 +151,7 @@ module Sidekiq
|
|
152
151
|
# value and decrypt it
|
153
152
|
token_length = masked_token.length / 2
|
154
153
|
one_time_pad = masked_token[0...token_length]
|
155
|
-
encrypted_token = masked_token[token_length
|
154
|
+
encrypted_token = masked_token[token_length..]
|
156
155
|
xor_byte_strings(one_time_pad, encrypted_token)
|
157
156
|
end
|
158
157
|
|
@@ -168,8 +167,12 @@ module Sidekiq
|
|
168
167
|
::Rack::Utils.secure_compare(token.to_s, decode_token(local).to_s)
|
169
168
|
end
|
170
169
|
|
170
|
+
def encode_token(token)
|
171
|
+
[token].pack("m0").tr("+/", "-_")
|
172
|
+
end
|
173
|
+
|
171
174
|
def decode_token(token)
|
172
|
-
|
175
|
+
token.tr("-_", "+/").unpack1("m0")
|
173
176
|
end
|
174
177
|
|
175
178
|
def xor_byte_strings(s1, s2)
|
data/lib/sidekiq/web/helpers.rb
CHANGED
@@ -6,21 +6,68 @@ require "yaml"
|
|
6
6
|
require "cgi"
|
7
7
|
|
8
8
|
module Sidekiq
|
9
|
-
#
|
9
|
+
# These methods are available to pages within the Web UI and UI extensions.
|
10
|
+
# They are not public APIs for applications to use.
|
10
11
|
module WebHelpers
|
12
|
+
def style_tag(location, **kwargs)
|
13
|
+
global = location.match?(/:\/\//)
|
14
|
+
location = root_path + location if !global && !location.start_with?(root_path)
|
15
|
+
attrs = {
|
16
|
+
type: "text/css",
|
17
|
+
media: "screen",
|
18
|
+
rel: "stylesheet",
|
19
|
+
nonce: csp_nonce,
|
20
|
+
href: location
|
21
|
+
}
|
22
|
+
html_tag(:link, attrs.merge(kwargs))
|
23
|
+
end
|
24
|
+
|
25
|
+
def script_tag(location, **kwargs)
|
26
|
+
global = location.match?(/:\/\//)
|
27
|
+
location = root_path + location if !global && !location.start_with?(root_path)
|
28
|
+
attrs = {
|
29
|
+
type: "text/javascript",
|
30
|
+
nonce: csp_nonce,
|
31
|
+
src: location
|
32
|
+
}
|
33
|
+
html_tag(:script, attrs.merge(kwargs)) {}
|
34
|
+
end
|
35
|
+
|
36
|
+
# NB: keys and values are not escaped; do not allow user input
|
37
|
+
# in the attributes
|
38
|
+
private def html_tag(tagname, attrs)
|
39
|
+
s = +"<#{tagname}"
|
40
|
+
attrs.each_pair do |k, v|
|
41
|
+
next unless v
|
42
|
+
s << " #{k}=\"#{v}\""
|
43
|
+
end
|
44
|
+
if block_given?
|
45
|
+
s << ">"
|
46
|
+
yield s
|
47
|
+
s << "</#{tagname}>"
|
48
|
+
else
|
49
|
+
s << " />"
|
50
|
+
end
|
51
|
+
s
|
52
|
+
end
|
53
|
+
|
11
54
|
def strings(lang)
|
12
|
-
|
55
|
+
@@strings ||= {}
|
13
56
|
|
14
57
|
# Allow sidekiq-web extensions to add locale paths
|
15
58
|
# so extensions can be localized
|
16
|
-
|
59
|
+
@@strings[lang] ||= settings.locales.each_with_object({}) do |path, global|
|
17
60
|
find_locale_files(lang).each do |file|
|
18
|
-
strs = YAML.safe_load(File.
|
61
|
+
strs = YAML.safe_load(File.read(file))
|
19
62
|
global.merge!(strs[lang])
|
20
63
|
end
|
21
64
|
end
|
22
65
|
end
|
23
66
|
|
67
|
+
def to_json(x)
|
68
|
+
Sidekiq.dump_json(x)
|
69
|
+
end
|
70
|
+
|
24
71
|
def singularize(str, count)
|
25
72
|
if count == 1 && str.respond_to?(:singularize) # rails
|
26
73
|
str.singularize
|
@@ -30,27 +77,48 @@ module Sidekiq
|
|
30
77
|
end
|
31
78
|
|
32
79
|
def clear_caches
|
33
|
-
|
34
|
-
|
35
|
-
|
80
|
+
@@strings = nil
|
81
|
+
@@locale_files = nil
|
82
|
+
@@available_locales = nil
|
36
83
|
end
|
37
84
|
|
38
85
|
def locale_files
|
39
|
-
|
86
|
+
@@locale_files ||= settings.locales.flat_map { |path|
|
40
87
|
Dir["#{path}/*.yml"]
|
41
88
|
}
|
42
89
|
end
|
43
90
|
|
44
91
|
def available_locales
|
45
|
-
|
92
|
+
@@available_locales ||= Set.new(locale_files.map { |path| File.basename(path, ".yml") })
|
46
93
|
end
|
47
94
|
|
48
95
|
def find_locale_files(lang)
|
49
96
|
locale_files.select { |file| file =~ /\/#{lang}\.yml$/ }
|
50
97
|
end
|
51
98
|
|
52
|
-
|
53
|
-
|
99
|
+
def search(jobset, substr)
|
100
|
+
resultset = jobset.scan(substr).to_a
|
101
|
+
@current_page = 1
|
102
|
+
@count = @total_size = resultset.size
|
103
|
+
resultset
|
104
|
+
end
|
105
|
+
|
106
|
+
def filtering(which)
|
107
|
+
erb(:filtering, locals: {which: which})
|
108
|
+
end
|
109
|
+
|
110
|
+
def filter_link(jid, within = "retries")
|
111
|
+
if within.nil?
|
112
|
+
::Rack::Utils.escape_html(jid)
|
113
|
+
else
|
114
|
+
"<a href='#{root_path}#{within}?substr=#{jid}'>#{::Rack::Utils.escape_html(jid)}</a>"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def display_tags(job, within = "retries")
|
119
|
+
job.tags.map { |tag|
|
120
|
+
"<span class='label label-info jobtag'>#{filter_link(tag, within)}</span>"
|
121
|
+
}.join(" ")
|
54
122
|
end
|
55
123
|
|
56
124
|
# This view helper provide ability display you html code in
|
@@ -96,7 +164,10 @@ module Sidekiq
|
|
96
164
|
#
|
97
165
|
# Inspiration taken from https://github.com/iain/http_accept_language/blob/master/lib/http_accept_language/parser.rb
|
98
166
|
def locale
|
99
|
-
|
167
|
+
# session[:locale] is set via the locale selector from the footer
|
168
|
+
@locale ||= if (l = session&.fetch(:locale, nil)) && available_locales.include?(l)
|
169
|
+
l
|
170
|
+
else
|
100
171
|
matched_locale = user_preferred_languages.map { |preferred|
|
101
172
|
preferred_language = preferred.split("-", 2).first
|
102
173
|
|
@@ -111,16 +182,10 @@ module Sidekiq
|
|
111
182
|
end
|
112
183
|
end
|
113
184
|
|
114
|
-
#
|
115
|
-
def display_tags(job, within = nil)
|
116
|
-
job.tags.map { |tag|
|
117
|
-
"<span class='label label-info jobtag'>#{::Rack::Utils.escape_html(tag)}</span>"
|
118
|
-
}.join(" ")
|
119
|
-
end
|
120
|
-
|
121
|
-
# mperham/sidekiq#3243
|
185
|
+
# sidekiq/sidekiq#3243
|
122
186
|
def unfiltered?
|
123
|
-
|
187
|
+
s = url_params("substr")
|
188
|
+
yield unless s && s.size > 0
|
124
189
|
end
|
125
190
|
|
126
191
|
def get_locale
|
@@ -161,22 +226,26 @@ module Sidekiq
|
|
161
226
|
end
|
162
227
|
end
|
163
228
|
|
229
|
+
def busy_weights(capsule_weights)
|
230
|
+
# backwards compat with 7.0.0, remove in 7.1
|
231
|
+
cw = [capsule_weights].flatten
|
232
|
+
cw.map { |hash|
|
233
|
+
hash.map { |name, weight| (weight > 0) ? +name << ": " << weight.to_s : name }.join(", ")
|
234
|
+
}.join("; ")
|
235
|
+
end
|
236
|
+
|
164
237
|
def stats
|
165
238
|
@stats ||= Sidekiq::Stats.new
|
166
239
|
end
|
167
240
|
|
168
|
-
def
|
241
|
+
def redis_url
|
169
242
|
Sidekiq.redis do |conn|
|
170
|
-
conn.
|
243
|
+
conn.config.server_url
|
171
244
|
end
|
172
245
|
end
|
173
246
|
|
174
|
-
def namespace
|
175
|
-
@ns ||= Sidekiq.redis { |conn| conn.respond_to?(:namespace) ? conn.namespace : nil }
|
176
|
-
end
|
177
|
-
|
178
247
|
def redis_info
|
179
|
-
Sidekiq.redis_info
|
248
|
+
Sidekiq.default_configuration.redis_info
|
180
249
|
end
|
181
250
|
|
182
251
|
def root_path
|
@@ -241,6 +310,10 @@ module Sidekiq
|
|
241
310
|
"<input type='hidden' name='authenticity_token' value='#{env[:csrf_token]}'/>"
|
242
311
|
end
|
243
312
|
|
313
|
+
def csp_nonce
|
314
|
+
env[:csp_nonce]
|
315
|
+
end
|
316
|
+
|
244
317
|
def to_display(arg)
|
245
318
|
arg.inspect
|
246
319
|
rescue
|
@@ -274,27 +347,17 @@ module Sidekiq
|
|
274
347
|
elsif rss_kb < 10_000_000
|
275
348
|
"#{number_with_delimiter((rss_kb / 1024.0).to_i)} MB"
|
276
349
|
else
|
277
|
-
"#{number_with_delimiter((rss_kb / (1024.0 * 1024.0))
|
350
|
+
"#{number_with_delimiter((rss_kb / (1024.0 * 1024.0)), precision: 1)} GB"
|
278
351
|
end
|
279
352
|
end
|
280
353
|
|
281
|
-
def number_with_delimiter(number)
|
282
|
-
|
283
|
-
|
284
|
-
begin
|
285
|
-
Float(number)
|
286
|
-
rescue ArgumentError, TypeError
|
287
|
-
return number
|
288
|
-
end
|
289
|
-
|
290
|
-
options = {delimiter: ",", separator: "."}
|
291
|
-
parts = number.to_s.to_str.split(".")
|
292
|
-
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
|
293
|
-
parts.join(options[:separator])
|
354
|
+
def number_with_delimiter(number, options = {})
|
355
|
+
precision = options[:precision] || 0
|
356
|
+
%(<span data-nwp="#{precision}">#{number.round(precision)}</span>)
|
294
357
|
end
|
295
358
|
|
296
359
|
def h(text)
|
297
|
-
::Rack::Utils.escape_html(text)
|
360
|
+
::Rack::Utils.escape_html(text.to_s)
|
298
361
|
rescue ArgumentError => e
|
299
362
|
raise unless e.message.eql?("invalid byte sequence in UTF-8")
|
300
363
|
text.encode!("UTF-16", "UTF-8", invalid: :replace, replace: "").encode!("UTF-8", "UTF-16")
|
@@ -314,7 +377,7 @@ module Sidekiq
|
|
314
377
|
end
|
315
378
|
|
316
379
|
def environment_title_prefix
|
317
|
-
environment = Sidekiq[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
380
|
+
environment = Sidekiq.default_configuration[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
318
381
|
|
319
382
|
"[#{environment.upcase}] " unless environment == "production"
|
320
383
|
end
|
@@ -327,11 +390,9 @@ module Sidekiq
|
|
327
390
|
Time.now.utc.strftime("%H:%M:%S UTC")
|
328
391
|
end
|
329
392
|
|
330
|
-
def
|
331
|
-
|
332
|
-
|
333
|
-
"#{redis_connection}#{namespace_suffix}"
|
334
|
-
end
|
393
|
+
def pollable?
|
394
|
+
# there's no point to refreshing the metrics pages every N seconds
|
395
|
+
!(current_path == "" || current_path.index("metrics"))
|
335
396
|
end
|
336
397
|
|
337
398
|
def retry_or_delete_or_kill(job, params)
|
data/lib/sidekiq/web/router.rb
CHANGED
@@ -39,10 +39,13 @@ module Sidekiq
|
|
39
39
|
route(DELETE, path, &block)
|
40
40
|
end
|
41
41
|
|
42
|
-
def route(
|
42
|
+
def route(*methods, path, &block)
|
43
43
|
@routes ||= {GET => [], POST => [], PUT => [], PATCH => [], DELETE => [], HEAD => []}
|
44
44
|
|
45
|
-
|
45
|
+
methods.each do |method|
|
46
|
+
method = method.to_s.upcase
|
47
|
+
@routes[method] << WebRoute.new(method, path, block)
|
48
|
+
end
|
46
49
|
end
|
47
50
|
|
48
51
|
def match(env)
|