solid_queue_monitor 0.1.1 → 0.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.
Files changed (24) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -4
  3. data/app/controllers/solid_queue_monitor/base_controller.rb +194 -0
  4. data/app/controllers/solid_queue_monitor/failed_jobs_controller.rb +62 -0
  5. data/app/controllers/solid_queue_monitor/in_progress_jobs_controller.rb +27 -0
  6. data/app/controllers/solid_queue_monitor/monitor_controller.rb +75 -7
  7. data/app/controllers/solid_queue_monitor/overview_controller.rb +25 -0
  8. data/app/controllers/solid_queue_monitor/queues_controller.rb +11 -0
  9. data/app/controllers/solid_queue_monitor/ready_jobs_controller.rb +14 -0
  10. data/app/controllers/solid_queue_monitor/recurring_jobs_controller.rb +14 -0
  11. data/app/controllers/solid_queue_monitor/scheduled_jobs_controller.rb +24 -0
  12. data/app/presenters/solid_queue_monitor/base_presenter.rb +57 -48
  13. data/app/presenters/solid_queue_monitor/failed_jobs_presenter.rb +257 -21
  14. data/app/presenters/solid_queue_monitor/in_progress_jobs_presenter.rb +71 -0
  15. data/app/presenters/solid_queue_monitor/jobs_presenter.rb +36 -2
  16. data/app/presenters/solid_queue_monitor/scheduled_jobs_presenter.rb +33 -10
  17. data/app/presenters/solid_queue_monitor/stats_presenter.rb +2 -2
  18. data/app/services/solid_queue_monitor/failed_job_service.rb +97 -0
  19. data/app/services/solid_queue_monitor/html_generator.rb +28 -2
  20. data/app/services/solid_queue_monitor/stats_calculator.rb +1 -0
  21. data/app/services/solid_queue_monitor/stylesheet_generator.rb +150 -1
  22. data/config/routes.rb +13 -7
  23. data/lib/solid_queue_monitor/version.rb +2 -2
  24. metadata +12 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d980b63b65c764dd6f0382f9072274f22a36f6906a56cdc888b081a3c3aeb5e
4
- data.tar.gz: 223799c8891120a76776d4f702315f76033af176274bbcb5c2c7d6b30aa1053e
3
+ metadata.gz: 264dd6a6542855e5209702c11fbb46c1c267b5c8d48d530887030f924bdb929f
4
+ data.tar.gz: 4d5a33c4152f29d0c3ba4c9fd6c59582e7af9694e85469a78cde9a9d173a4433
5
5
  SHA512:
6
- metadata.gz: 7b938b862ebd3b7d7e5c711f6b7dd5717ab70d4472641252f72d9bf1e96bb97596a65799e2adf17b27f2a119d07c3ee4d285d0925931b2c0e6ddc72ab57c4bd9
7
- data.tar.gz: 84ad9d12a2c7c37610c610938958c60585aeea01c4d3797ea7c9d019f37c0581ba4843e104fa89cdc59e50087e3a7c4bbcdfdd2e95d2bccd09e10f8d11d85722
6
+ metadata.gz: 704927588ba7329d988a7533ab70e6b444254abc64a15684fbeccce47221c929c39701abd8fbb02dbebf444c0c3a9521cbcc84260d06d3c6fdafca6e01ad2be5
7
+ data.tar.gz: 8b0a608d0ae89c54719dd5286e9de77cb4145d1f1c3dc7c3f8c1b46a521ae1b2e0ad37d35e7c2796a187122fc38f401b423a46109cc81328cff6805274338357
data/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # SolidQueueMonitor
2
2
 
3
- A lightweight, zero-dependency web interface for monitoring Solid Queue jobs in Rails applications.
3
+ [![Gem Version](https://badge.fury.io/rb/solid_queue_monitor.svg)](https://badge.fury.io/rb/solid_queue_monitor)
4
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
+
6
+ A lightweight, zero-dependency web interface for monitoring Solid Queue background jobs in Ruby on Rails applications. Perfect for Rails API-only applications and traditional Rails apps.
4
7
 
5
8
  ## Key Advantages
6
9
 
@@ -8,6 +11,7 @@ A lightweight, zero-dependency web interface for monitoring Solid Queue jobs in
8
11
  - **No External Dependencies**: No JavaScript frameworks, no CSS libraries, no additional gems required - just pure Rails.
9
12
  - **Self-contained UI**: All HTML, CSS, and JavaScript are generated server-side, making deployment simple and reliable.
10
13
  - **Minimal Footprint**: Adds minimal overhead to your application while providing powerful monitoring capabilities.
14
+ - **Rails 7 Compatible**: Fully compatible with Rails 7.1+ and the latest Solid Queue versions.
11
15
 
12
16
  ## Features
13
17
 
@@ -16,6 +20,7 @@ A lightweight, zero-dependency web interface for monitoring Solid Queue jobs in
16
20
  - **Job Management**: Execute scheduled jobs on demand
17
21
  - **Failed Job Inspection**: View detailed error information for failed jobs
18
22
  - **Queue Monitoring**: Track job distribution across different queues
23
+ - **Recurring Jobs**: Monitor and manage recurring background tasks
19
24
  - **Pagination**: Navigate through large job lists with ease
20
25
  - **Optional Authentication**: Secure your dashboard with HTTP Basic Authentication
21
26
  - **Responsive Design**: Works on desktop and mobile devices
@@ -27,16 +32,16 @@ A lightweight, zero-dependency web interface for monitoring Solid Queue jobs in
27
32
 
28
33
  ![Dashboard Overview](screenshots/dashboard.png)
29
34
 
30
- ### Recurring Jobs
35
+ ### Failed Jobs
31
36
 
32
- ![Recurring Jobs](screenshots/recurring_jobs.png)
37
+ ![Failed Jobs](screenshots/failed_jobs.png)
33
38
 
34
39
  ## Installation
35
40
 
36
41
  Add this line to your application's Gemfile:
37
42
 
38
43
  ```ruby
39
- gem 'solid_queue_monitor'
44
+ gem 'solid_queue_monitor', '~> 0.2.0'
40
45
  ```
41
46
 
42
47
  Then execute:
@@ -96,6 +101,7 @@ The dashboard provides several views:
96
101
  - **Overview**: Shows statistics and recent jobs
97
102
  - **Ready Jobs**: Jobs that are ready to be executed
98
103
  - **Scheduled Jobs**: Jobs scheduled for future execution
104
+ - **Recurring Jobs**: Jobs that run on a recurring schedule
99
105
  - **Failed Jobs**: Jobs that have failed with error details
100
106
  - **Queues**: Distribution of jobs across different queues
101
107
 
@@ -103,6 +109,20 @@ The dashboard provides several views:
103
109
 
104
110
  For API-only Rails applications, SolidQueueMonitor works out of the box without requiring you to enable the asset pipeline or webpacker. This makes it an ideal choice for monitoring background jobs in modern API-based architectures.
105
111
 
112
+ ## Use Cases
113
+
114
+ - **Production Monitoring**: Keep an eye on your background job processing in production environments
115
+ - **Debugging**: Quickly identify and troubleshoot failed jobs
116
+ - **Job Management**: Execute scheduled jobs on demand when needed
117
+ - **Performance Analysis**: Track job distribution and identify bottlenecks
118
+ - **DevOps Integration**: Easily integrate with your monitoring stack
119
+
120
+ ## Compatibility
121
+
122
+ - **Ruby**: 3.1.6 or higher
123
+ - **Rails**: 7.1 or higher
124
+ - **Solid Queue**: 0.1.0 or higher
125
+
106
126
  ## Contributing
107
127
 
108
128
  Contributions are welcome! Here's how you can contribute:
@@ -126,3 +146,9 @@ The gem is available as open source under the terms of the [MIT License](https:/
126
146
  ## Code of Conduct
127
147
 
128
148
  Everyone interacting in the SolidQueueMonitor project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/yourusername/solid_queue_monitor/blob/main/CODE_OF_CONDUCT.md).
149
+
150
+ ## Related Projects
151
+
152
+ - [Solid Queue](https://github.com/rails/solid_queue) - The official Rails background job framework
153
+ - [Rails](https://github.com/rails/rails) - The web application framework
154
+ - [ActiveJob](https://github.com/rails/rails/tree/main/activejob) - Rails job frameworkk
@@ -0,0 +1,194 @@
1
+ module SolidQueueMonitor
2
+ class BaseController < ActionController::Base
3
+ include ActionController::HttpAuthentication::Basic::ControllerMethods
4
+ include ActionController::Flash
5
+
6
+ before_action :authenticate, if: -> { SolidQueueMonitor::AuthenticationService.authentication_required? }
7
+ layout false
8
+ skip_before_action :verify_authenticity_token
9
+
10
+ # Define a helper method for setting flash messages
11
+ def set_flash_message(message, type)
12
+ session[:flash_message] = message
13
+ session[:flash_type] = type
14
+ end
15
+
16
+ private
17
+
18
+ def authenticate
19
+ authenticate_or_request_with_http_basic do |username, password|
20
+ SolidQueueMonitor::AuthenticationService.authenticate(username, password)
21
+ end
22
+ end
23
+
24
+ def paginate(relation)
25
+ PaginationService.new(relation, current_page, per_page).paginate
26
+ end
27
+
28
+ def render_page(title, content)
29
+ # Get flash message from session
30
+ message = session[:flash_message]
31
+ message_type = session[:flash_type]
32
+
33
+ # Clear the flash message from session after using it
34
+ session.delete(:flash_message)
35
+ session.delete(:flash_type)
36
+
37
+ html = SolidQueueMonitor::HtmlGenerator.new(
38
+ title: title,
39
+ content: content,
40
+ message: message,
41
+ message_type: message_type
42
+ ).generate
43
+
44
+ render html: html.html_safe
45
+ end
46
+
47
+ def current_page
48
+ (params[:page] || 1).to_i
49
+ end
50
+
51
+ def per_page
52
+ SolidQueueMonitor.jobs_per_page
53
+ end
54
+
55
+ # Preload job statuses to avoid N+1 queries
56
+ def preload_job_statuses(jobs)
57
+ return if jobs.empty?
58
+
59
+ # Get all job IDs
60
+ job_ids = jobs.map(&:id)
61
+
62
+ # Find all failed jobs in a single query
63
+ failed_job_ids = SolidQueue::FailedExecution.where(job_id: job_ids).pluck(:job_id)
64
+
65
+ # Find all scheduled jobs in a single query
66
+ scheduled_job_ids = SolidQueue::ScheduledExecution.where(job_id: job_ids).pluck(:job_id)
67
+
68
+ # Attach the status information to each job
69
+ jobs.each do |job|
70
+ job.instance_variable_set(:@failed, failed_job_ids.include?(job.id))
71
+ job.instance_variable_set(:@scheduled, scheduled_job_ids.include?(job.id))
72
+ end
73
+
74
+ # Define the method to check if a job is failed
75
+ SolidQueue::Job.class_eval do
76
+ def failed?
77
+ if instance_variable_defined?(:@failed)
78
+ @failed
79
+ else
80
+ SolidQueue::FailedExecution.exists?(job_id: id)
81
+ end
82
+ end
83
+
84
+ def scheduled?
85
+ if instance_variable_defined?(:@scheduled)
86
+ @scheduled
87
+ else
88
+ SolidQueue::ScheduledExecution.exists?(job_id: id)
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ def filter_jobs(relation)
95
+ relation = relation.where("class_name LIKE ?", "%#{params[:class_name]}%") if params[:class_name].present?
96
+ relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%") if params[:queue_name].present?
97
+
98
+ if params[:status].present?
99
+ case params[:status]
100
+ when 'completed'
101
+ relation = relation.where.not(finished_at: nil)
102
+ when 'failed'
103
+ failed_job_ids = SolidQueue::FailedExecution.pluck(:job_id)
104
+ relation = relation.where(id: failed_job_ids)
105
+ when 'scheduled'
106
+ scheduled_job_ids = SolidQueue::ScheduledExecution.pluck(:job_id)
107
+ relation = relation.where(id: scheduled_job_ids)
108
+ when 'pending'
109
+ # Pending jobs are those that are not completed, failed, or scheduled
110
+ failed_job_ids = SolidQueue::FailedExecution.pluck(:job_id)
111
+ scheduled_job_ids = SolidQueue::ScheduledExecution.pluck(:job_id)
112
+ relation = relation.where(finished_at: nil)
113
+ .where.not(id: failed_job_ids + scheduled_job_ids)
114
+ end
115
+ end
116
+
117
+ relation
118
+ end
119
+
120
+ def filter_ready_jobs(relation)
121
+ return relation unless params[:class_name].present? || params[:queue_name].present?
122
+
123
+ if params[:class_name].present?
124
+ job_ids = SolidQueue::Job.where("class_name LIKE ?", "%#{params[:class_name]}%").pluck(:id)
125
+ relation = relation.where(job_id: job_ids)
126
+ end
127
+
128
+ if params[:queue_name].present?
129
+ relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%")
130
+ end
131
+
132
+ relation
133
+ end
134
+
135
+ def filter_scheduled_jobs(relation)
136
+ return relation unless params[:class_name].present? || params[:queue_name].present?
137
+
138
+ if params[:class_name].present?
139
+ job_ids = SolidQueue::Job.where("class_name LIKE ?", "%#{params[:class_name]}%").pluck(:id)
140
+ relation = relation.where(job_id: job_ids)
141
+ end
142
+
143
+ if params[:queue_name].present?
144
+ relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%")
145
+ end
146
+
147
+ relation
148
+ end
149
+
150
+ def filter_recurring_jobs(relation)
151
+ return relation unless params[:class_name].present? || params[:queue_name].present?
152
+
153
+ if params[:class_name].present?
154
+ relation = relation.where("class_name LIKE ?", "%#{params[:class_name]}%")
155
+ end
156
+
157
+ if params[:queue_name].present?
158
+ relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%")
159
+ end
160
+
161
+ relation
162
+ end
163
+
164
+ def filter_failed_jobs(relation)
165
+ return relation unless params[:class_name].present? || params[:queue_name].present?
166
+
167
+ if params[:class_name].present?
168
+ job_ids = SolidQueue::Job.where("class_name LIKE ?", "%#{params[:class_name]}%").pluck(:id)
169
+ relation = relation.where(job_id: job_ids)
170
+ end
171
+
172
+ if params[:queue_name].present?
173
+ # Check if FailedExecution has queue_name column
174
+ if relation.column_names.include?('queue_name')
175
+ relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%")
176
+ else
177
+ # If not, filter by job's queue_name
178
+ job_ids = SolidQueue::Job.where("queue_name LIKE ?", "%#{params[:queue_name]}%").pluck(:id)
179
+ relation = relation.where(job_id: job_ids)
180
+ end
181
+ end
182
+
183
+ relation
184
+ end
185
+
186
+ def filter_params
187
+ {
188
+ class_name: params[:class_name],
189
+ queue_name: params[:queue_name],
190
+ status: params[:status]
191
+ }
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,62 @@
1
+ module SolidQueueMonitor
2
+ class FailedJobsController < BaseController
3
+ def index
4
+ base_query = SolidQueue::FailedExecution.includes(:job).order(created_at: :desc)
5
+ @failed_jobs = paginate(filter_failed_jobs(base_query))
6
+
7
+ render_page('Failed Jobs', SolidQueueMonitor::FailedJobsPresenter.new(@failed_jobs[:records],
8
+ current_page: @failed_jobs[:current_page],
9
+ total_pages: @failed_jobs[:total_pages],
10
+ filters: filter_params
11
+ ).render)
12
+ end
13
+
14
+ def retry
15
+ id = params[:id]
16
+ service = SolidQueueMonitor::FailedJobService.new
17
+
18
+ if service.retry_job(id)
19
+ set_flash_message("Job #{id} has been queued for retry.", 'success')
20
+ else
21
+ set_flash_message("Failed to retry job #{id}.", 'error')
22
+ end
23
+
24
+ redirect_to params[:redirect_to].present? ? params[:redirect_to] : failed_jobs_path
25
+ end
26
+
27
+ def discard
28
+ id = params[:id]
29
+ service = SolidQueueMonitor::FailedJobService.new
30
+
31
+ if service.discard_job(id)
32
+ set_flash_message("Job #{id} has been discarded.", 'success')
33
+ else
34
+ set_flash_message("Failed to discard job #{id}.", 'error')
35
+ end
36
+
37
+ redirect_to params[:redirect_to].present? ? params[:redirect_to] : failed_jobs_path
38
+ end
39
+
40
+ def retry_all
41
+ result = SolidQueueMonitor::FailedJobService.new.retry_all(params[:job_ids])
42
+
43
+ if result[:success]
44
+ set_flash_message(result[:message], 'success')
45
+ else
46
+ set_flash_message(result[:message], 'error')
47
+ end
48
+ redirect_to failed_jobs_path
49
+ end
50
+
51
+ def discard_all
52
+ result = SolidQueueMonitor::FailedJobService.new.discard_all(params[:job_ids])
53
+
54
+ if result[:success]
55
+ set_flash_message(result[:message], 'success')
56
+ else
57
+ set_flash_message(result[:message], 'error')
58
+ end
59
+ redirect_to failed_jobs_path
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,27 @@
1
+ module SolidQueueMonitor
2
+ class InProgressJobsController < BaseController
3
+ def index
4
+ base_query = SolidQueue::ClaimedExecution.includes(:job).order(created_at: :desc)
5
+ @in_progress_jobs = paginate(filter_in_progress_jobs(base_query))
6
+
7
+ render_page('In Progress Jobs', SolidQueueMonitor::InProgressJobsPresenter.new(@in_progress_jobs[:records],
8
+ current_page: @in_progress_jobs[:current_page],
9
+ total_pages: @in_progress_jobs[:total_pages],
10
+ filters: filter_params
11
+ ).render)
12
+ end
13
+
14
+ private
15
+
16
+ def filter_in_progress_jobs(relation)
17
+ return relation unless params[:class_name].present?
18
+
19
+ if params[:class_name].present?
20
+ job_ids = SolidQueue::Job.where("class_name LIKE ?", "%#{params[:class_name]}%").pluck(:id)
21
+ relation = relation.where(job_id: job_ids)
22
+ end
23
+
24
+ relation
25
+ end
26
+ end
27
+ end
@@ -1,10 +1,17 @@
1
1
  module SolidQueueMonitor
2
2
  class MonitorController < ActionController::Base
3
3
  include ActionController::HttpAuthentication::Basic::ControllerMethods
4
+ include ActionController::Flash
4
5
 
5
6
  before_action :authenticate, if: -> { SolidQueueMonitor::AuthenticationService.authentication_required? }
6
7
  layout false
7
- skip_before_action :verify_authenticity_token, only: [:execute_jobs]
8
+ skip_before_action :verify_authenticity_token, only: [:execute_jobs, :retry_failed_job, :discard_failed_job, :retry_failed_jobs, :discard_failed_jobs]
9
+
10
+ # Define a helper method for setting flash messages
11
+ def set_flash_message(message, type)
12
+ session[:flash_message] = message
13
+ session[:flash_type] = type
14
+ end
8
15
 
9
16
  def index
10
17
  @stats = SolidQueueMonitor::StatsCalculator.calculate
@@ -68,11 +75,57 @@ module SolidQueueMonitor
68
75
  def execute_jobs
69
76
  if params[:job_ids].present?
70
77
  SolidQueueMonitor::ExecuteJobService.new.execute_many(params[:job_ids])
71
- redirect_url = "#{scheduled_jobs_path}?message=Selected jobs moved to ready queue&message_type=success"
78
+ set_flash_message('Selected jobs moved to ready queue', 'success')
72
79
  else
73
- redirect_url = "#{scheduled_jobs_path}?message=No jobs selected&message_type=error"
80
+ set_flash_message('No jobs selected', 'error')
74
81
  end
75
- redirect_to redirect_url
82
+ redirect_to scheduled_jobs_path
83
+ end
84
+
85
+ def retry_failed_job
86
+ id = params[:id]
87
+ service = SolidQueueMonitor::FailedJobService.new
88
+
89
+ if service.retry_job(id)
90
+ set_flash_message("Job #{id} has been queued for retry.", 'success')
91
+ else
92
+ set_flash_message("Failed to retry job #{id}.", 'error')
93
+ end
94
+ redirect_to failed_jobs_path
95
+ end
96
+
97
+ def discard_failed_job
98
+ id = params[:id]
99
+ service = SolidQueueMonitor::FailedJobService.new
100
+
101
+ if service.discard_job(id)
102
+ set_flash_message("Job #{id} has been discarded.", 'success')
103
+ else
104
+ set_flash_message("Failed to discard job #{id}.", 'error')
105
+ end
106
+ redirect_to failed_jobs_path
107
+ end
108
+
109
+ def retry_failed_jobs
110
+ result = SolidQueueMonitor::FailedJobService.new.retry_all(params[:job_ids])
111
+
112
+ if result[:success]
113
+ set_flash_message(result[:message], 'success')
114
+ else
115
+ set_flash_message(result[:message], 'error')
116
+ end
117
+ redirect_to failed_jobs_path
118
+ end
119
+
120
+ def discard_failed_jobs
121
+ result = SolidQueueMonitor::FailedJobService.new.discard_all(params[:job_ids])
122
+
123
+ if result[:success]
124
+ set_flash_message(result[:message], 'success')
125
+ else
126
+ set_flash_message(result[:message], 'error')
127
+ end
128
+ redirect_to failed_jobs_path
76
129
  end
77
130
 
78
131
  private
@@ -88,11 +141,19 @@ module SolidQueueMonitor
88
141
  end
89
142
 
90
143
  def render_page(title, content)
144
+ # Get flash message from session
145
+ message = session[:flash_message]
146
+ message_type = session[:flash_type]
147
+
148
+ # Clear the flash message from session after using it
149
+ session.delete(:flash_message)
150
+ session.delete(:flash_type)
151
+
91
152
  html = SolidQueueMonitor::HtmlGenerator.new(
92
153
  title: title,
93
154
  content: content,
94
- message: params[:notice] || params[:alert],
95
- message_type: params[:notice] ? 'success' : 'error'
155
+ message: message,
156
+ message_type: message_type
96
157
  ).generate
97
158
 
98
159
  render html: html.html_safe
@@ -233,7 +294,14 @@ module SolidQueueMonitor
233
294
  end
234
295
 
235
296
  if params[:queue_name].present?
236
- relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%")
297
+ # Check if FailedExecution has queue_name column
298
+ if relation.column_names.include?('queue_name')
299
+ relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%")
300
+ else
301
+ # If not, filter by job's queue_name
302
+ job_ids = SolidQueue::Job.where("queue_name LIKE ?", "%#{params[:queue_name]}%").pluck(:id)
303
+ relation = relation.where(job_id: job_ids)
304
+ end
237
305
  end
238
306
 
239
307
  relation
@@ -0,0 +1,25 @@
1
+ module SolidQueueMonitor
2
+ class OverviewController < BaseController
3
+ def index
4
+ @stats = SolidQueueMonitor::StatsCalculator.calculate
5
+
6
+ recent_jobs_query = SolidQueue::Job.order(created_at: :desc).limit(100)
7
+ @recent_jobs = paginate(filter_jobs(recent_jobs_query))
8
+
9
+ preload_job_statuses(@recent_jobs[:records])
10
+
11
+ render_page('Overview', generate_overview_content)
12
+ end
13
+
14
+ private
15
+
16
+ def generate_overview_content
17
+ SolidQueueMonitor::StatsPresenter.new(@stats).render +
18
+ SolidQueueMonitor::JobsPresenter.new(@recent_jobs[:records],
19
+ current_page: @recent_jobs[:current_page],
20
+ total_pages: @recent_jobs[:total_pages],
21
+ filters: filter_params
22
+ ).render
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ module SolidQueueMonitor
2
+ class QueuesController < BaseController
3
+ def index
4
+ @queues = SolidQueue::Job.group(:queue_name)
5
+ .select('queue_name, COUNT(*) as job_count')
6
+ .order('job_count DESC')
7
+
8
+ render_page('Queues', SolidQueueMonitor::QueuesPresenter.new(@queues).render)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ module SolidQueueMonitor
2
+ class ReadyJobsController < BaseController
3
+ def index
4
+ base_query = SolidQueue::ReadyExecution.includes(:job).order(created_at: :desc)
5
+ @ready_jobs = paginate(filter_ready_jobs(base_query))
6
+
7
+ render_page('Ready Jobs', SolidQueueMonitor::ReadyJobsPresenter.new(@ready_jobs[:records],
8
+ current_page: @ready_jobs[:current_page],
9
+ total_pages: @ready_jobs[:total_pages],
10
+ filters: filter_params
11
+ ).render)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module SolidQueueMonitor
2
+ class RecurringJobsController < BaseController
3
+ def index
4
+ base_query = filter_recurring_jobs(SolidQueue::RecurringTask.order(:key))
5
+ @recurring_jobs = paginate(base_query)
6
+
7
+ render_page('Recurring Jobs', SolidQueueMonitor::RecurringJobsPresenter.new(@recurring_jobs[:records],
8
+ current_page: @recurring_jobs[:current_page],
9
+ total_pages: @recurring_jobs[:total_pages],
10
+ filters: filter_params
11
+ ).render)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,24 @@
1
+ module SolidQueueMonitor
2
+ class ScheduledJobsController < BaseController
3
+ def index
4
+ base_query = SolidQueue::ScheduledExecution.includes(:job).order(scheduled_at: :asc)
5
+ @scheduled_jobs = paginate(filter_scheduled_jobs(base_query))
6
+
7
+ render_page('Scheduled Jobs', SolidQueueMonitor::ScheduledJobsPresenter.new(@scheduled_jobs[:records],
8
+ current_page: @scheduled_jobs[:current_page],
9
+ total_pages: @scheduled_jobs[:total_pages],
10
+ filters: filter_params
11
+ ).render)
12
+ end
13
+
14
+ def create
15
+ if params[:job_ids].present?
16
+ SolidQueueMonitor::ExecuteJobService.new.execute_many(params[:job_ids])
17
+ set_flash_message('Selected jobs moved to ready queue', 'success')
18
+ else
19
+ set_flash_message('No jobs selected', 'error')
20
+ end
21
+ redirect_to scheduled_jobs_path
22
+ end
23
+ end
24
+ end