solid_queue_monitor 1.0.1 → 1.2.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 +4 -4
- data/README.md +16 -1
- data/app/controllers/solid_queue_monitor/base_controller.rb +49 -33
- data/app/controllers/solid_queue_monitor/failed_jobs_controller.rb +7 -3
- data/app/controllers/solid_queue_monitor/in_progress_jobs_controller.rb +9 -7
- data/app/controllers/solid_queue_monitor/overview_controller.rb +13 -9
- data/app/controllers/solid_queue_monitor/queues_controller.rb +39 -16
- data/app/controllers/solid_queue_monitor/ready_jobs_controller.rb +7 -3
- data/app/controllers/solid_queue_monitor/recurring_jobs_controller.rb +7 -3
- data/app/controllers/solid_queue_monitor/scheduled_jobs_controller.rb +7 -3
- data/app/controllers/solid_queue_monitor/search_controller.rb +12 -0
- data/app/controllers/solid_queue_monitor/workers_controller.rb +7 -4
- data/app/presenters/solid_queue_monitor/base_presenter.rb +47 -5
- data/app/presenters/solid_queue_monitor/failed_jobs_presenter.rb +6 -6
- data/app/presenters/solid_queue_monitor/in_progress_jobs_presenter.rb +5 -4
- data/app/presenters/solid_queue_monitor/jobs_presenter.rb +5 -4
- data/app/presenters/solid_queue_monitor/queue_details_presenter.rb +4 -3
- data/app/presenters/solid_queue_monitor/queues_presenter.rb +10 -22
- data/app/presenters/solid_queue_monitor/ready_jobs_presenter.rb +6 -5
- data/app/presenters/solid_queue_monitor/recurring_jobs_presenter.rb +6 -5
- data/app/presenters/solid_queue_monitor/scheduled_jobs_presenter.rb +5 -4
- data/app/presenters/solid_queue_monitor/search_results_presenter.rb +190 -0
- data/app/presenters/solid_queue_monitor/stats_presenter.rb +1 -2
- data/app/presenters/solid_queue_monitor/workers_presenter.rb +4 -3
- data/app/services/solid_queue_monitor/chart_data_service.rb +53 -57
- data/app/services/solid_queue_monitor/html_generator.rb +23 -2
- data/app/services/solid_queue_monitor/search_service.rb +126 -0
- data/app/services/solid_queue_monitor/stats_calculator.rb +12 -8
- data/app/services/solid_queue_monitor/stylesheet_generator.rb +118 -0
- data/config/routes.rb +1 -0
- data/lib/generators/solid_queue_monitor/templates/initializer.rb +3 -0
- data/lib/solid_queue_monitor/version.rb +1 -1
- data/lib/solid_queue_monitor.rb +2 -1
- metadata +5 -2
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidQueueMonitor
|
|
4
|
+
class SearchService
|
|
5
|
+
RESULTS_LIMIT = 25
|
|
6
|
+
|
|
7
|
+
def initialize(query)
|
|
8
|
+
@query = query
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def search
|
|
12
|
+
return empty_results if @query.blank?
|
|
13
|
+
|
|
14
|
+
term = "%#{sanitize_query(@query)}%"
|
|
15
|
+
|
|
16
|
+
{
|
|
17
|
+
ready: search_ready_jobs(term),
|
|
18
|
+
scheduled: search_scheduled_jobs(term),
|
|
19
|
+
failed: search_failed_jobs(term),
|
|
20
|
+
in_progress: search_in_progress_jobs(term),
|
|
21
|
+
completed: search_completed_jobs(term),
|
|
22
|
+
recurring: search_recurring_tasks(term)
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def empty_results
|
|
29
|
+
{
|
|
30
|
+
ready: [],
|
|
31
|
+
scheduled: [],
|
|
32
|
+
failed: [],
|
|
33
|
+
in_progress: [],
|
|
34
|
+
completed: [],
|
|
35
|
+
recurring: []
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def sanitize_query(query)
|
|
40
|
+
# Escape % to prevent LIKE pattern injection
|
|
41
|
+
# We don't escape _ because it requires database-specific ESCAPE clauses
|
|
42
|
+
query.to_s.gsub('%', '\%')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def search_ready_jobs(term)
|
|
46
|
+
SolidQueue::ReadyExecution
|
|
47
|
+
.joins(:job)
|
|
48
|
+
.where(job_search_conditions, term: term)
|
|
49
|
+
.includes(:job)
|
|
50
|
+
.limit(RESULTS_LIMIT)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def search_scheduled_jobs(term)
|
|
54
|
+
SolidQueue::ScheduledExecution
|
|
55
|
+
.joins(:job)
|
|
56
|
+
.where(job_search_conditions, term: term)
|
|
57
|
+
.includes(:job)
|
|
58
|
+
.limit(RESULTS_LIMIT)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def search_failed_jobs(term)
|
|
62
|
+
SolidQueue::FailedExecution
|
|
63
|
+
.joins(:job)
|
|
64
|
+
.where(failed_job_search_conditions, term: term)
|
|
65
|
+
.includes(:job)
|
|
66
|
+
.limit(RESULTS_LIMIT)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def search_in_progress_jobs(term)
|
|
70
|
+
SolidQueue::ClaimedExecution
|
|
71
|
+
.joins(:job)
|
|
72
|
+
.where(job_search_conditions, term: term)
|
|
73
|
+
.includes(:job)
|
|
74
|
+
.limit(RESULTS_LIMIT)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def search_completed_jobs(term)
|
|
78
|
+
SolidQueue::Job
|
|
79
|
+
.where.not(finished_at: nil)
|
|
80
|
+
.where(completed_job_search_conditions, term: term)
|
|
81
|
+
.order(finished_at: :desc)
|
|
82
|
+
.limit(RESULTS_LIMIT)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def search_recurring_tasks(term)
|
|
86
|
+
SolidQueue::RecurringTask
|
|
87
|
+
.where(recurring_task_search_conditions, term: term)
|
|
88
|
+
.limit(RESULTS_LIMIT)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def job_search_conditions
|
|
92
|
+
<<~SQL.squish
|
|
93
|
+
solid_queue_jobs.class_name LIKE :term
|
|
94
|
+
OR solid_queue_jobs.queue_name LIKE :term
|
|
95
|
+
OR solid_queue_jobs.arguments LIKE :term
|
|
96
|
+
OR solid_queue_jobs.active_job_id LIKE :term
|
|
97
|
+
SQL
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def failed_job_search_conditions
|
|
101
|
+
<<~SQL.squish
|
|
102
|
+
solid_queue_jobs.class_name LIKE :term
|
|
103
|
+
OR solid_queue_jobs.queue_name LIKE :term
|
|
104
|
+
OR solid_queue_jobs.arguments LIKE :term
|
|
105
|
+
OR solid_queue_jobs.active_job_id LIKE :term
|
|
106
|
+
OR solid_queue_failed_executions.error LIKE :term
|
|
107
|
+
SQL
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def completed_job_search_conditions
|
|
111
|
+
<<~SQL.squish
|
|
112
|
+
class_name LIKE :term
|
|
113
|
+
OR queue_name LIKE :term
|
|
114
|
+
OR arguments LIKE :term
|
|
115
|
+
OR active_job_id LIKE :term
|
|
116
|
+
SQL
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def recurring_task_search_conditions
|
|
120
|
+
<<~SQL.squish
|
|
121
|
+
solid_queue_recurring_tasks.key LIKE :term
|
|
122
|
+
OR solid_queue_recurring_tasks.class_name LIKE :term
|
|
123
|
+
SQL
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -3,15 +3,19 @@
|
|
|
3
3
|
module SolidQueueMonitor
|
|
4
4
|
class StatsCalculator
|
|
5
5
|
def self.calculate
|
|
6
|
+
scheduled = SolidQueue::ScheduledExecution.count
|
|
7
|
+
ready = SolidQueue::ReadyExecution.count
|
|
8
|
+
failed = SolidQueue::FailedExecution.count
|
|
9
|
+
in_progress = SolidQueue::ClaimedExecution.count
|
|
10
|
+
recurring = SolidQueue::RecurringTask.count
|
|
11
|
+
|
|
6
12
|
{
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
completed: SolidQueue::Job.where.not(finished_at: nil).count,
|
|
14
|
-
recurring: SolidQueue::RecurringTask.count
|
|
13
|
+
active_jobs: ready + scheduled + in_progress + failed,
|
|
14
|
+
scheduled: scheduled,
|
|
15
|
+
ready: ready,
|
|
16
|
+
failed: failed,
|
|
17
|
+
in_progress: in_progress,
|
|
18
|
+
recurring: recurring
|
|
15
19
|
}
|
|
16
20
|
end
|
|
17
21
|
end
|
|
@@ -66,6 +66,16 @@ module SolidQueueMonitor
|
|
|
66
66
|
margin-bottom: 0.5rem;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
.solid_queue_monitor .header-title-link {
|
|
70
|
+
color: var(--text-color);
|
|
71
|
+
text-decoration: none;
|
|
72
|
+
transition: color 0.2s;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.solid_queue_monitor .header-title-link:hover {
|
|
76
|
+
color: var(--primary-color);
|
|
77
|
+
}
|
|
78
|
+
|
|
69
79
|
.solid_queue_monitor .navigation {
|
|
70
80
|
display: flex;
|
|
71
81
|
flex-wrap: wrap;
|
|
@@ -195,6 +205,22 @@ module SolidQueueMonitor
|
|
|
195
205
|
color: var(--text-muted);
|
|
196
206
|
}
|
|
197
207
|
|
|
208
|
+
.solid_queue_monitor .sortable-header {
|
|
209
|
+
color: var(--text-muted);
|
|
210
|
+
text-decoration: none;
|
|
211
|
+
cursor: pointer;
|
|
212
|
+
transition: color 0.2s;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.solid_queue_monitor .sortable-header:hover {
|
|
216
|
+
color: var(--primary-color);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.solid_queue_monitor .sortable-header.active {
|
|
220
|
+
color: var(--primary-color);
|
|
221
|
+
font-weight: 600;
|
|
222
|
+
}
|
|
223
|
+
|
|
198
224
|
.solid_queue_monitor .status-badge {
|
|
199
225
|
display: inline-block;
|
|
200
226
|
padding: 0.25rem 0.5rem;
|
|
@@ -1178,6 +1204,98 @@ module SolidQueueMonitor
|
|
|
1178
1204
|
gap: 0.75rem;
|
|
1179
1205
|
}
|
|
1180
1206
|
|
|
1207
|
+
/* Header Search Box */
|
|
1208
|
+
.solid_queue_monitor .header-search-form {
|
|
1209
|
+
display: flex;
|
|
1210
|
+
align-items: center;
|
|
1211
|
+
gap: 0;
|
|
1212
|
+
flex: 1;
|
|
1213
|
+
max-width: 400px;
|
|
1214
|
+
margin: 0 1rem;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
.solid_queue_monitor .header-search-input {
|
|
1218
|
+
flex: 1;
|
|
1219
|
+
padding: 0.5rem 0.75rem;
|
|
1220
|
+
border: 1px solid var(--input-border);
|
|
1221
|
+
border-right: none;
|
|
1222
|
+
border-radius: 0.375rem 0 0 0.375rem;
|
|
1223
|
+
font-size: 0.875rem;
|
|
1224
|
+
background: var(--input-background);
|
|
1225
|
+
color: var(--text-color);
|
|
1226
|
+
outline: none;
|
|
1227
|
+
transition: border-color 0.2s, box-shadow 0.2s;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
.solid_queue_monitor .header-search-input:focus {
|
|
1231
|
+
border-color: var(--primary-color);
|
|
1232
|
+
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
.solid_queue_monitor .header-search-input::placeholder {
|
|
1236
|
+
color: var(--text-muted);
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
.solid_queue_monitor .header-search-button {
|
|
1240
|
+
display: flex;
|
|
1241
|
+
align-items: center;
|
|
1242
|
+
justify-content: center;
|
|
1243
|
+
padding: 0.5rem 0.75rem;
|
|
1244
|
+
background: var(--primary-color);
|
|
1245
|
+
color: white;
|
|
1246
|
+
border: 1px solid var(--primary-color);
|
|
1247
|
+
border-radius: 0 0.375rem 0.375rem 0;
|
|
1248
|
+
cursor: pointer;
|
|
1249
|
+
transition: background-color 0.2s;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
.solid_queue_monitor .header-search-button:hover {
|
|
1253
|
+
background: #2563eb;
|
|
1254
|
+
border-color: #2563eb;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
.solid_queue_monitor .header-search-button svg {
|
|
1258
|
+
width: 16px;
|
|
1259
|
+
height: 16px;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
@media (max-width: 768px) {
|
|
1263
|
+
.solid_queue_monitor .header-search-form {
|
|
1264
|
+
max-width: 100%;
|
|
1265
|
+
margin: 0.5rem 0;
|
|
1266
|
+
order: 3;
|
|
1267
|
+
width: 100%;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
/* Search Results Page */
|
|
1272
|
+
.solid_queue_monitor .results-summary {
|
|
1273
|
+
margin: 1rem 0;
|
|
1274
|
+
padding: 0.75rem 1rem;
|
|
1275
|
+
background: var(--card-background);
|
|
1276
|
+
border-radius: 0.375rem;
|
|
1277
|
+
box-shadow: var(--card-shadow);
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
.solid_queue_monitor .results-summary p {
|
|
1281
|
+
margin: 0;
|
|
1282
|
+
color: var(--text-muted);
|
|
1283
|
+
font-size: 0.875rem;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
.solid_queue_monitor .search-results-section {
|
|
1287
|
+
margin-top: 1.5rem;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
.solid_queue_monitor .search-results-section h3 {
|
|
1291
|
+
font-size: 1rem;
|
|
1292
|
+
font-weight: 600;
|
|
1293
|
+
color: var(--text-color);
|
|
1294
|
+
margin-bottom: 0.75rem;
|
|
1295
|
+
padding-bottom: 0.5rem;
|
|
1296
|
+
border-bottom: 1px solid var(--border-color);
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1181
1299
|
/* Workers Page Styles */
|
|
1182
1300
|
.solid_queue_monitor .workers-summary {
|
|
1183
1301
|
display: grid;
|
data/config/routes.rb
CHANGED
|
@@ -7,6 +7,7 @@ SolidQueueMonitor::Engine.routes.draw do
|
|
|
7
7
|
root to: 'overview#index'
|
|
8
8
|
|
|
9
9
|
get 'chart_data', to: 'overview#chart_data', as: :chart_data
|
|
10
|
+
get 'search', to: 'search#index', as: :search
|
|
10
11
|
|
|
11
12
|
resources :ready_jobs, only: [:index]
|
|
12
13
|
resources :scheduled_jobs, only: [:index]
|
data/lib/solid_queue_monitor.rb
CHANGED
|
@@ -7,7 +7,7 @@ module SolidQueueMonitor
|
|
|
7
7
|
class Error < StandardError; end
|
|
8
8
|
class << self
|
|
9
9
|
attr_accessor :username, :password, :jobs_per_page, :authentication_enabled,
|
|
10
|
-
:auto_refresh_enabled, :auto_refresh_interval
|
|
10
|
+
:auto_refresh_enabled, :auto_refresh_interval, :show_chart
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
@username = 'admin'
|
|
@@ -16,6 +16,7 @@ module SolidQueueMonitor
|
|
|
16
16
|
@authentication_enabled = false
|
|
17
17
|
@auto_refresh_enabled = true
|
|
18
18
|
@auto_refresh_interval = 30 # seconds
|
|
19
|
+
@show_chart = true
|
|
19
20
|
|
|
20
21
|
def self.setup
|
|
21
22
|
yield self
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: solid_queue_monitor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vishal Sadriya
|
|
@@ -58,6 +58,7 @@ files:
|
|
|
58
58
|
- app/controllers/solid_queue_monitor/ready_jobs_controller.rb
|
|
59
59
|
- app/controllers/solid_queue_monitor/recurring_jobs_controller.rb
|
|
60
60
|
- app/controllers/solid_queue_monitor/scheduled_jobs_controller.rb
|
|
61
|
+
- app/controllers/solid_queue_monitor/search_controller.rb
|
|
61
62
|
- app/controllers/solid_queue_monitor/workers_controller.rb
|
|
62
63
|
- app/presenters/solid_queue_monitor/base_presenter.rb
|
|
63
64
|
- app/presenters/solid_queue_monitor/failed_jobs_presenter.rb
|
|
@@ -69,6 +70,7 @@ files:
|
|
|
69
70
|
- app/presenters/solid_queue_monitor/ready_jobs_presenter.rb
|
|
70
71
|
- app/presenters/solid_queue_monitor/recurring_jobs_presenter.rb
|
|
71
72
|
- app/presenters/solid_queue_monitor/scheduled_jobs_presenter.rb
|
|
73
|
+
- app/presenters/solid_queue_monitor/search_results_presenter.rb
|
|
72
74
|
- app/presenters/solid_queue_monitor/stats_presenter.rb
|
|
73
75
|
- app/presenters/solid_queue_monitor/workers_presenter.rb
|
|
74
76
|
- app/services/solid_queue_monitor/authentication_service.rb
|
|
@@ -80,6 +82,7 @@ files:
|
|
|
80
82
|
- app/services/solid_queue_monitor/pagination_service.rb
|
|
81
83
|
- app/services/solid_queue_monitor/queue_pause_service.rb
|
|
82
84
|
- app/services/solid_queue_monitor/reject_job_service.rb
|
|
85
|
+
- app/services/solid_queue_monitor/search_service.rb
|
|
83
86
|
- app/services/solid_queue_monitor/stats_calculator.rb
|
|
84
87
|
- app/services/solid_queue_monitor/status_calculator.rb
|
|
85
88
|
- app/services/solid_queue_monitor/stylesheet_generator.rb
|
|
@@ -116,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
116
119
|
- !ruby/object:Gem::Version
|
|
117
120
|
version: '0'
|
|
118
121
|
requirements: []
|
|
119
|
-
rubygems_version:
|
|
122
|
+
rubygems_version: 4.0.6
|
|
120
123
|
specification_version: 4
|
|
121
124
|
summary: Simple monitoring interface for Solid Queue
|
|
122
125
|
test_files: []
|