job_harbor 0.7.0 → 0.7.1
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/app/components/job_harbor/job_filters_component.rb +7 -2
- data/app/components/job_harbor/job_row_component.rb +8 -3
- data/app/controllers/job_harbor/jobs_controller.rb +27 -2
- data/app/controllers/job_harbor/workers_controller.rb +1 -0
- data/app/models/job_harbor/job_presenter.rb +3 -2
- data/app/views/job_harbor/workers/index.html.erb +4 -2
- data/lib/job_harbor/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 61fd4075abe3acb8265885f2ff9e5d007bede4df800c2c466b98e11cbc1810b2
|
|
4
|
+
data.tar.gz: c7b3836df141e4711ccb3f4828eb926da10f6f5d9901a996338e7977cb011641
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fca96f13699a78acf5e848527c86df9f98e53ca6f1ce7ab7bd4b7d0ba923e737e8badadc86d990627bed83ffe8c81d07b1c324e0654f4f0704a5790bc223bf26
|
|
7
|
+
data.tar.gz: f3b64f11f8a06145c40830703b2d48f7ced097e01a173b3c63a9c645bb880701bf4e13174c2c3ecc9a570d7bbec9e4a2bd422888e9751c645138429720bfb412
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module JobHarbor
|
|
4
4
|
class JobFiltersComponent < ApplicationComponent
|
|
5
5
|
def initialize(class_names:, queue_names:, current_class:, current_queue:, current_path:, params: {})
|
|
6
|
-
@class_names = class_names
|
|
7
|
-
@queue_names = queue_names
|
|
6
|
+
@class_names = with_selected(class_names, current_class)
|
|
7
|
+
@queue_names = with_selected(queue_names, current_queue)
|
|
8
8
|
@current_class = current_class
|
|
9
9
|
@current_queue = current_queue
|
|
10
10
|
@current_path = current_path
|
|
@@ -22,6 +22,11 @@ module JobHarbor
|
|
|
22
22
|
|
|
23
23
|
private
|
|
24
24
|
|
|
25
|
+
# The active filter stays selectable even after its last job is gone.
|
|
26
|
+
def with_selected(names, current)
|
|
27
|
+
current.present? ? (names | [ current ]).sort : names
|
|
28
|
+
end
|
|
29
|
+
|
|
25
30
|
def class_filter
|
|
26
31
|
content_tag(:div, class: "flex items-center gap-1.5") do
|
|
27
32
|
safe_join([
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module JobHarbor
|
|
4
4
|
class JobRowComponent < ApplicationComponent
|
|
5
|
-
def initialize(job:)
|
|
5
|
+
def initialize(job:, return_to: nil)
|
|
6
6
|
@job = job
|
|
7
|
+
@return_to = return_to
|
|
7
8
|
end
|
|
8
9
|
|
|
9
10
|
def call
|
|
@@ -92,16 +93,20 @@ module JobHarbor
|
|
|
92
93
|
end
|
|
93
94
|
end
|
|
94
95
|
|
|
96
|
+
def return_to
|
|
97
|
+
@return_to || request.fullpath
|
|
98
|
+
end
|
|
99
|
+
|
|
95
100
|
def retry_button
|
|
96
101
|
return unless @job.can_retry?
|
|
97
102
|
|
|
98
|
-
button_to "Retry", retry_job_path(@job), method: :post, class: "btn btn-secondary btn-xs"
|
|
103
|
+
button_to "Retry", retry_job_path(@job), method: :post, params: { return_to: return_to }, class: "btn btn-secondary btn-xs"
|
|
99
104
|
end
|
|
100
105
|
|
|
101
106
|
def discard_button
|
|
102
107
|
return unless @job.can_discard?
|
|
103
108
|
|
|
104
|
-
button_to "Discard", discard_job_path(@job), method: :delete, class: "btn btn-destructive btn-xs",
|
|
109
|
+
button_to "Discard", discard_job_path(@job), method: :delete, params: { return_to: return_to }, class: "btn btn-destructive btn-xs",
|
|
105
110
|
form: { data: { confirm: "Are you sure you want to discard this job?" } }
|
|
106
111
|
end
|
|
107
112
|
end
|
|
@@ -44,7 +44,7 @@ module JobHarbor
|
|
|
44
44
|
def retry
|
|
45
45
|
if @job.can_retry?
|
|
46
46
|
perform_retry(@job)
|
|
47
|
-
redirect_to job_path(@job), notice: "Job has been queued for retry."
|
|
47
|
+
redirect_to return_path || job_path(@job), allow_other_host: false, notice: "Job has been queued for retry."
|
|
48
48
|
else
|
|
49
49
|
redirect_to job_path(@job), alert: "This job cannot be retried."
|
|
50
50
|
end
|
|
@@ -52,7 +52,7 @@ module JobHarbor
|
|
|
52
52
|
|
|
53
53
|
def discard
|
|
54
54
|
if @job.can_discard? && @job.discard
|
|
55
|
-
redirect_to jobs_path(status: params[:return_status]), notice: "Job has been discarded."
|
|
55
|
+
redirect_to return_path || jobs_path(status: params[:return_status]), allow_other_host: false, notice: "Job has been discarded."
|
|
56
56
|
else
|
|
57
57
|
redirect_to job_path(@job), alert: "This job cannot be discarded."
|
|
58
58
|
end
|
|
@@ -78,6 +78,31 @@ module JobHarbor
|
|
|
78
78
|
redirect_to jobs_path, alert: "Job not found."
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
+
# Only a path inside this engine's mount, so a crafted param can't send the operator elsewhere.
|
|
82
|
+
def return_path
|
|
83
|
+
path = url_from(params[:return_to]) if params[:return_to].is_a?(String)
|
|
84
|
+
return unless path && within_mount?(path)
|
|
85
|
+
|
|
86
|
+
queue_page_gone?(path) ? queues_path : path
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def within_mount?(path)
|
|
90
|
+
mount = request.script_name
|
|
91
|
+
return false unless path == mount || path.start_with?("#{mount}/", "#{mount}?")
|
|
92
|
+
|
|
93
|
+
raw = path.split("?", 2).first
|
|
94
|
+
# %2e%2e and %2f are resolved by the browser, so judge the decoded path too.
|
|
95
|
+
[ raw, URI.decode_www_form_component(raw) ].none? { |candidate| candidate.match?(%r{//|\\|\s}) || candidate.split("/").include?("..") }
|
|
96
|
+
rescue ArgumentError
|
|
97
|
+
false
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# A queue with no jobs left stops existing, so its page would answer "Queue not found" and drop the notice.
|
|
101
|
+
def queue_page_gone?(path)
|
|
102
|
+
name = path.split("?", 2).first[%r{\A#{Regexp.escape(request.script_name)}/queues/([^/]+)\z}, 1]
|
|
103
|
+
name.present? && QueueStats.find(Rack::Utils.unescape_path(name)).nil?
|
|
104
|
+
end
|
|
105
|
+
|
|
81
106
|
def per_page_param
|
|
82
107
|
per_page = params[:per_page].to_i
|
|
83
108
|
valid_sizes = JobHarbor::PerPageSelectorComponent::PAGE_SIZES
|
|
@@ -5,6 +5,7 @@ module JobHarbor
|
|
|
5
5
|
def index
|
|
6
6
|
@workers = SolidQueue::Process.order(last_heartbeat_at: :desc)
|
|
7
7
|
@active_count = Processes.alive.count
|
|
8
|
+
@live_workers_count = Processes.live_workers.count
|
|
8
9
|
@stale_count = @workers.count - @active_count
|
|
9
10
|
set_page_title "Workers"
|
|
10
11
|
end
|
|
@@ -192,9 +192,10 @@ module JobHarbor
|
|
|
192
192
|
end
|
|
193
193
|
|
|
194
194
|
def paginate(scope, page:, per_page:)
|
|
195
|
-
page = [ page.to_i, 1 ].max
|
|
196
|
-
offset = (page - 1) * per_page
|
|
197
195
|
total = scope.count
|
|
196
|
+
pages = (total.to_f / per_page).ceil
|
|
197
|
+
page = [ [ page.to_i, 1 ].max, [ pages, 1 ].max ].min
|
|
198
|
+
offset = (page - 1) * per_page
|
|
198
199
|
|
|
199
200
|
jobs = scope.preload(*EXECUTION_STATUSES.keys).order(id: :desc).limit(per_page).offset(offset).map { |job| new(job) }
|
|
200
201
|
[ PageInfo.new(page: page, per_page: per_page, count: total), jobs ]
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-6">
|
|
2
2
|
<%= render JobHarbor::StatCardComponent.new(
|
|
3
|
-
label: "
|
|
3
|
+
label: "Processes",
|
|
4
4
|
value: @workers.count,
|
|
5
5
|
type: :workers
|
|
6
6
|
) %>
|
|
7
7
|
|
|
8
8
|
<%= render JobHarbor::StatCardComponent.new(
|
|
9
|
-
label: "
|
|
9
|
+
label: "Live",
|
|
10
10
|
value: @active_count,
|
|
11
11
|
type: :finished
|
|
12
12
|
) %>
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
) %>
|
|
19
19
|
</div>
|
|
20
20
|
|
|
21
|
+
<p class="text-sm text-muted-foreground mb-6" data-live-workers>Live workers: <%= @live_workers_count %></p>
|
|
22
|
+
|
|
21
23
|
<% if @workers.empty? %>
|
|
22
24
|
<%= render JobHarbor::EmptyStateComponent.new(
|
|
23
25
|
title: "No workers found",
|
data/lib/job_harbor/version.rb
CHANGED