solid_queue_monitor 0.3.2 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 76559272501afef3ca73a5f8739a0d6bea2c7c3dc5fc4a3153ed9890f5a8f0b5
4
- data.tar.gz: efcfd9438981bc2f35a8925c6d2b08120ec490441e8f66959202c5d78dff7990
3
+ metadata.gz: 2597291a879f3f6720e783c6aae3fa203434a67652ba24c5e65a6fdd87081120
4
+ data.tar.gz: d01e77ac3c412bf2fcdab1c4f1a02aa072786f7d88ec42798758615159ddc67c
5
5
  SHA512:
6
- metadata.gz: b6d8a0d27f8770f9c7ebf838e2f7e78ac72afbdf900ff5718c98861bfb25ab151250a5f36be58edcdcaa029f82d68118e0f5aa102d12c6054289e428f83069d7
7
- data.tar.gz: 0750abe0b179438a53c8519fad9219247850de0fb661f57dd9800eb120446c4f5cea3d128ffd8b310842c0f5334c22c6f2c3db800ff46a2ffa66564b1071893d
6
+ metadata.gz: 9f95324749998dd5c7a6e0598a25edd14f82bbc32610499dcf688592729f4cbf805bf3ac8da8ad3f776c0dc3231ebaa6fa710fac5537806553cc3cc50f3a7d66
7
+ data.tar.gz: be751aaa21b617d1ba1f09eb473a9bbd83eeccfbe79c918040fe2df420c76258b012fad5c26eefa1aaaafe50e62fdf6d566b08469042006a501d9f354cd49658
data/README.md CHANGED
@@ -21,10 +21,12 @@ A lightweight, zero-dependency web interface for monitoring Solid Queue backgrou
21
21
  - **Scheduled Jobs**: See upcoming jobs scheduled for future execution with ability to execute immediately or reject permanently
22
22
  - **Recurring Jobs**: Manage periodic jobs that run on a schedule
23
23
  - **Failed Jobs**: Track and debug failed jobs, with the ability to retry or discard them
24
- - **Queue Management**: View and filter jobs by queue
24
+ - **Queue Management**: View and filter jobs by queue with pause/resume controls
25
+ - **Pause/Resume Queues**: Temporarily stop processing jobs on specific queues for incident response
25
26
  - **Advanced Job Filtering**: Filter jobs by class name, queue, status, and job arguments
26
27
  - **Quick Actions**: Retry or discard failed jobs, execute or reject scheduled jobs directly from any view
27
28
  - **Performance Optimized**: Designed for high-volume applications with smart pagination
29
+ - **Auto-refresh**: Real-time monitoring with configurable auto-refresh interval and toggle
28
30
  - **Optional Authentication**: Secure your dashboard with HTTP Basic Authentication
29
31
  - **Responsive Design**: Works on desktop and mobile devices
30
32
  - **Zero Dependencies**: No additional JavaScript libraries or frameworks required
@@ -44,7 +46,7 @@ A lightweight, zero-dependency web interface for monitoring Solid Queue backgrou
44
46
  Add this line to your application's Gemfile:
45
47
 
46
48
  ```ruby
47
- gem 'solid_queue_monitor', '~> 0.3.2'
49
+ gem 'solid_queue_monitor', '~> 0.4.0'
48
50
  ```
49
51
 
50
52
  Then execute:
@@ -83,6 +85,13 @@ SolidQueueMonitor.setup do |config|
83
85
 
84
86
  # Number of jobs to display per page
85
87
  config.jobs_per_page = 25
88
+
89
+ # Auto-refresh settings
90
+ # Enable or disable auto-refresh globally (users can still toggle it in the UI)
91
+ config.auto_refresh_enabled = true
92
+
93
+ # Auto-refresh interval in seconds (default: 30)
94
+ config.auto_refresh_interval = 30
86
95
  end
87
96
  ```
88
97
 
@@ -10,8 +10,17 @@ module SolidQueueMonitor
10
10
  skip_before_action :verify_authenticity_token
11
11
 
12
12
  def set_flash_message(message, type)
13
- session[:flash_message] = message
14
- session[:flash_type] = type
13
+ # Store in instance variable for access in views
14
+ @flash_message = message
15
+ @flash_type = type
16
+
17
+ # Try to use Rails flash if available
18
+ begin
19
+ flash[:notice] = message if type == :success
20
+ flash[:alert] = message if type == :error
21
+ rescue StandardError
22
+ # Flash not available (e.g., no session middleware)
23
+ end
15
24
  end
16
25
 
17
26
  private
@@ -7,13 +7,21 @@ module SolidQueueMonitor
7
7
  end
8
8
 
9
9
  def render_page(title, content)
10
- # Get flash message from session
11
- message = session[:flash_message]
12
- message_type = session[:flash_type]
13
-
14
- # Clear the flash message from session after using it
15
- session.delete(:flash_message)
16
- session.delete(:flash_type)
10
+ # Get flash message from instance variable (set by set_flash_message) or session
11
+ message = @flash_message
12
+ message_type = @flash_type
13
+
14
+ # Try to get from session as fallback, but don't fail if session unavailable
15
+ begin
16
+ message ||= session[:flash_message]
17
+ message_type ||= session[:flash_type]
18
+
19
+ # Clear the flash message from session after using it
20
+ session.delete(:flash_message) if message
21
+ session.delete(:flash_type) if message_type
22
+ rescue StandardError
23
+ # Session not available (e.g., no session middleware in tests)
24
+ end
17
25
 
18
26
  html = SolidQueueMonitor::HtmlGenerator.new(
19
27
  title: title,
@@ -6,8 +6,25 @@ module SolidQueueMonitor
6
6
  @queues = SolidQueue::Job.group(:queue_name)
7
7
  .select('queue_name, COUNT(*) as job_count')
8
8
  .order('job_count DESC')
9
+ @paused_queues = QueuePauseService.paused_queues
9
10
 
10
- render_page('Queues', SolidQueueMonitor::QueuesPresenter.new(@queues).render)
11
+ render_page('Queues', SolidQueueMonitor::QueuesPresenter.new(@queues, @paused_queues).render)
12
+ end
13
+
14
+ def pause
15
+ queue_name = params[:queue_name]
16
+ result = QueuePauseService.new(queue_name).pause
17
+
18
+ set_flash_message(result[:message], result[:success] ? 'success' : 'error')
19
+ redirect_to queues_path
20
+ end
21
+
22
+ def resume
23
+ queue_name = params[:queue_name]
24
+ result = QueuePauseService.new(queue_name).resume
25
+
26
+ set_flash_message(result[:message], result[:success] ? 'success' : 'error')
27
+ redirect_to queues_path
11
28
  end
12
29
  end
13
30
  end
@@ -2,8 +2,9 @@
2
2
 
3
3
  module SolidQueueMonitor
4
4
  class QueuesPresenter < BasePresenter
5
- def initialize(records)
5
+ def initialize(records, paused_queues = [])
6
6
  @records = records
7
+ @paused_queues = paused_queues
7
8
  end
8
9
 
9
10
  def render
@@ -19,10 +20,12 @@ module SolidQueueMonitor
19
20
  <thead>
20
21
  <tr>
21
22
  <th>Queue Name</th>
23
+ <th>Status</th>
22
24
  <th>Total Jobs</th>
23
25
  <th>Ready Jobs</th>
24
26
  <th>Scheduled Jobs</th>
25
27
  <th>Failed Jobs</th>
28
+ <th>Actions</th>
26
29
  </tr>
27
30
  </thead>
28
31
  <tbody>
@@ -34,17 +37,53 @@ module SolidQueueMonitor
34
37
  end
35
38
 
36
39
  def generate_row(queue)
40
+ queue_name = queue.queue_name || 'default'
41
+ paused = @paused_queues.include?(queue_name)
42
+
37
43
  <<-HTML
38
- <tr>
39
- <td>#{queue.queue_name || 'default'}</td>
44
+ <tr class="#{paused ? 'queue-paused' : ''}">
45
+ <td>#{queue_name}</td>
46
+ <td>#{status_badge(paused)}</td>
40
47
  <td>#{queue.job_count}</td>
41
- <td>#{ready_jobs_count(queue.queue_name)}</td>
42
- <td>#{scheduled_jobs_count(queue.queue_name)}</td>
43
- <td>#{failed_jobs_count(queue.queue_name)}</td>
48
+ <td>#{ready_jobs_count(queue_name)}</td>
49
+ <td>#{scheduled_jobs_count(queue_name)}</td>
50
+ <td>#{failed_jobs_count(queue_name)}</td>
51
+ <td class="actions-cell">#{action_button(queue_name, paused)}</td>
44
52
  </tr>
45
53
  HTML
46
54
  end
47
55
 
56
+ def status_badge(paused)
57
+ if paused
58
+ '<span class="status-badge status-paused">Paused</span>'
59
+ else
60
+ '<span class="status-badge status-active">Active</span>'
61
+ end
62
+ end
63
+
64
+ def action_button(queue_name, paused)
65
+ if paused
66
+ <<-HTML
67
+ <form action="#{resume_queue_path}" method="post" class="inline-form">
68
+ <input type="hidden" name="queue_name" value="#{queue_name}">
69
+ <button type="submit" class="action-button resume-button" title="Resume queue processing">
70
+ Resume
71
+ </button>
72
+ </form>
73
+ HTML
74
+ else
75
+ <<-HTML
76
+ <form action="#{pause_queue_path}" method="post" class="inline-form"
77
+ onsubmit="return confirm('Are you sure you want to pause the #{queue_name} queue? Workers will stop processing jobs from this queue.');">
78
+ <input type="hidden" name="queue_name" value="#{queue_name}">
79
+ <button type="submit" class="action-button pause-button" title="Pause queue processing">
80
+ Pause
81
+ </button>
82
+ </form>
83
+ HTML
84
+ end
85
+ end
86
+
48
87
  def ready_jobs_count(queue_name)
49
88
  SolidQueue::ReadyExecution.where(queue_name: queue_name).count
50
89
  end
@@ -50,6 +50,7 @@ module SolidQueueMonitor
50
50
  </div>
51
51
  #{generate_footer}
52
52
  </div>
53
+ #{generate_auto_refresh_script}
53
54
  HTML
54
55
  end
55
56
 
@@ -88,7 +89,10 @@ module SolidQueueMonitor
88
89
  def generate_header
89
90
  <<-HTML
90
91
  <header>
91
- <h1>Solid Queue Monitor</h1>
92
+ <div class="header-top">
93
+ <h1>Solid Queue Monitor</h1>
94
+ #{generate_auto_refresh_controls}
95
+ </div>
92
96
  <nav class="navigation">
93
97
  <a href="#{root_path}" class="nav-link">Overview</a>
94
98
  <a href="#{ready_jobs_path}" class="nav-link">Ready Jobs</a>
@@ -110,6 +114,104 @@ module SolidQueueMonitor
110
114
  HTML
111
115
  end
112
116
 
117
+ def generate_auto_refresh_controls
118
+ return '' unless SolidQueueMonitor.auto_refresh_enabled
119
+
120
+ interval = SolidQueueMonitor.auto_refresh_interval
121
+ <<-HTML
122
+ <div class="auto-refresh-container" title="Auto-refresh every #{interval}s" data-tooltip="Auto-refresh: Dashboard updates automatically every #{interval} seconds. Toggle to enable/disable.">
123
+ <span class="auto-refresh-indicator" id="auto-refresh-indicator"></span>
124
+ <span class="auto-refresh-countdown" id="auto-refresh-countdown">#{interval}s</span>
125
+ <label class="auto-refresh-switch" title="Toggle auto-refresh">
126
+ <input type="checkbox" id="auto-refresh-toggle" checked>
127
+ <span class="switch-slider"></span>
128
+ </label>
129
+ <button class="refresh-now-btn" id="refresh-now-btn" title="Refresh now">
130
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
131
+ <path d="M21 2v6h-6M3 12a9 9 0 0 1 15-6.7L21 8M3 22v-6h6M21 12a9 9 0 0 1-15 6.7L3 16"/>
132
+ </svg>
133
+ </button>
134
+ </div>
135
+ HTML
136
+ end
137
+
138
+ def generate_auto_refresh_script
139
+ return '' unless SolidQueueMonitor.auto_refresh_enabled
140
+
141
+ "<script>#{auto_refresh_javascript}</script>"
142
+ end
143
+
144
+ def auto_refresh_javascript
145
+ interval = SolidQueueMonitor.auto_refresh_interval
146
+ <<-JS
147
+ (function() {
148
+ var REFRESH_INTERVAL = #{interval};
149
+ var countdown = REFRESH_INTERVAL;
150
+ var timerId = null;
151
+ var isEnabled = localStorage.getItem('sqm_auto_refresh') !== 'false';
152
+ #{auto_refresh_dom_elements}
153
+ #{auto_refresh_functions}
154
+ #{auto_refresh_event_listeners}
155
+ #{auto_refresh_init}
156
+ })();
157
+ JS
158
+ end
159
+
160
+ def auto_refresh_dom_elements
161
+ <<-JS
162
+ var toggle = document.getElementById('auto-refresh-toggle');
163
+ var indicator = document.getElementById('auto-refresh-indicator');
164
+ var countdownEl = document.getElementById('auto-refresh-countdown');
165
+ var refreshBtn = document.getElementById('refresh-now-btn');
166
+ JS
167
+ end
168
+
169
+ def auto_refresh_functions
170
+ <<-JS
171
+ function updateUI() {
172
+ if (toggle) toggle.checked = isEnabled;
173
+ if (indicator) indicator.classList.toggle('active', isEnabled);
174
+ if (countdownEl) {
175
+ countdownEl.textContent = countdown + 's';
176
+ countdownEl.style.opacity = isEnabled ? '1' : '0.4';
177
+ }
178
+ }
179
+ function tick() {
180
+ countdown--;
181
+ if (countdown <= 0) { refresh(); } else { updateUI(); }
182
+ }
183
+ function startTimer() {
184
+ stopTimer();
185
+ countdown = REFRESH_INTERVAL;
186
+ updateUI();
187
+ timerId = setInterval(tick, 1000);
188
+ }
189
+ function stopTimer() {
190
+ if (timerId) { clearInterval(timerId); timerId = null; }
191
+ }
192
+ function refresh() { window.location.reload(); }
193
+ function setEnabled(enabled) {
194
+ isEnabled = enabled;
195
+ localStorage.setItem('sqm_auto_refresh', enabled ? 'true' : 'false');
196
+ if (enabled) { startTimer(); } else { stopTimer(); countdown = REFRESH_INTERVAL; updateUI(); }
197
+ }
198
+ JS
199
+ end
200
+
201
+ def auto_refresh_event_listeners
202
+ <<-JS
203
+ if (toggle) { toggle.addEventListener('change', function() { setEnabled(this.checked); }); }
204
+ if (refreshBtn) { refreshBtn.addEventListener('click', function() { refresh(); }); }
205
+ JS
206
+ end
207
+
208
+ def auto_refresh_init
209
+ <<-JS
210
+ updateUI();
211
+ if (isEnabled) { startTimer(); }
212
+ JS
213
+ end
214
+
113
215
  def default_url_options
114
216
  { only_path: true }
115
217
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidQueueMonitor
4
+ class QueuePauseService
5
+ delegate :paused?, to: :@queue
6
+
7
+ def initialize(queue_name)
8
+ @queue_name = queue_name
9
+ @queue = SolidQueue::Queue.new(queue_name)
10
+ end
11
+
12
+ def pause
13
+ return { success: false, message: "Queue '#{@queue_name}' is already paused" } if paused?
14
+
15
+ @queue.pause
16
+ { success: true, message: "Queue '#{@queue_name}' has been paused" }
17
+ rescue StandardError => e
18
+ { success: false, message: "Failed to pause queue: #{e.message}" }
19
+ end
20
+
21
+ def resume
22
+ return { success: false, message: "Queue '#{@queue_name}' is not paused" } unless paused?
23
+
24
+ @queue.resume
25
+ { success: true, message: "Queue '#{@queue_name}' has been resumed" }
26
+ rescue StandardError => e
27
+ { success: false, message: "Failed to resume queue: #{e.message}" }
28
+ end
29
+
30
+ def self.paused_queues
31
+ SolidQueue::Pause.pluck(:queue_name)
32
+ end
33
+ end
34
+ end
@@ -182,6 +182,30 @@ module SolidQueueMonitor
182
182
  .solid_queue_monitor .status-failed { background: #fee2e2; color: #991b1b; }
183
183
  .solid_queue_monitor .status-scheduled { background: #dbeafe; color: #1e40af; }
184
184
  .solid_queue_monitor .status-pending { background: #f3f4f6; color: #374151; }
185
+ .solid_queue_monitor .status-active { background: #d1fae5; color: #065f46; }
186
+ .solid_queue_monitor .status-paused { background: #fef3c7; color: #92400e; }
187
+
188
+ .solid_queue_monitor .queue-paused {
189
+ background-color: #fffbeb;
190
+ }
191
+
192
+ .solid_queue_monitor .pause-button {
193
+ background: #f59e0b;
194
+ color: white;
195
+ }
196
+
197
+ .solid_queue_monitor .pause-button:hover {
198
+ background: #d97706;
199
+ }
200
+
201
+ .solid_queue_monitor .resume-button {
202
+ background: #10b981;
203
+ color: white;
204
+ }
205
+
206
+ .solid_queue_monitor .resume-button:hover {
207
+ background: #059669;
208
+ }
185
209
 
186
210
  .solid_queue_monitor .execute-btn {
187
211
  background: var(--primary-color);
@@ -585,6 +609,183 @@ module SolidQueueMonitor
585
609
  .solid_queue_monitor .execute-button:hover {
586
610
  background: #2563eb;
587
611
  }
612
+
613
+ /* Header top row with title and auto-refresh */
614
+ .solid_queue_monitor .header-top {
615
+ display: flex;
616
+ justify-content: space-between;
617
+ align-items: center;
618
+ margin-bottom: 0.5rem;
619
+ }
620
+
621
+ /* Auto-refresh styles - compact design */
622
+ .solid_queue_monitor .auto-refresh-container {
623
+ position: relative;
624
+ display: flex;
625
+ align-items: center;
626
+ gap: 0.5rem;
627
+ padding: 0.375rem 0.625rem;
628
+ background: white;
629
+ border-radius: 2rem;
630
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
631
+ font-size: 0.75rem;
632
+ color: #6b7280;
633
+ cursor: default;
634
+ }
635
+
636
+ /* Tooltip styles */
637
+ .solid_queue_monitor .auto-refresh-container::after {
638
+ content: attr(data-tooltip);
639
+ position: absolute;
640
+ top: calc(100% + 8px);
641
+ right: 0;
642
+ background: #1f2937;
643
+ color: white;
644
+ padding: 0.5rem 0.75rem;
645
+ border-radius: 0.375rem;
646
+ font-size: 0.75rem;
647
+ line-height: 1.4;
648
+ white-space: nowrap;
649
+ max-width: 280px;
650
+ white-space: normal;
651
+ text-align: left;
652
+ opacity: 0;
653
+ visibility: hidden;
654
+ transition: opacity 0.2s, visibility 0.2s;
655
+ z-index: 1000;
656
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
657
+ pointer-events: none;
658
+ }
659
+
660
+ /* Tooltip arrow */
661
+ .solid_queue_monitor .auto-refresh-container::before {
662
+ content: "";
663
+ position: absolute;
664
+ top: calc(100% + 2px);
665
+ right: 16px;
666
+ border: 6px solid transparent;
667
+ border-bottom-color: #1f2937;
668
+ opacity: 0;
669
+ visibility: hidden;
670
+ transition: opacity 0.2s, visibility 0.2s;
671
+ z-index: 1001;
672
+ pointer-events: none;
673
+ }
674
+
675
+ .solid_queue_monitor .auto-refresh-container:hover::after,
676
+ .solid_queue_monitor .auto-refresh-container:hover::before {
677
+ opacity: 1;
678
+ visibility: visible;
679
+ }
680
+
681
+ .solid_queue_monitor .auto-refresh-indicator {
682
+ width: 6px;
683
+ height: 6px;
684
+ border-radius: 50%;
685
+ background: #d1d5db;
686
+ flex-shrink: 0;
687
+ }
688
+
689
+ .solid_queue_monitor .auto-refresh-indicator.active {
690
+ background: var(--success-color);
691
+ animation: pulse 2s infinite;
692
+ }
693
+
694
+ @keyframes pulse {
695
+ 0%, 100% { opacity: 1; }
696
+ 50% { opacity: 0.5; }
697
+ }
698
+
699
+ .solid_queue_monitor .auto-refresh-countdown {
700
+ font-variant-numeric: tabular-nums;
701
+ font-weight: 500;
702
+ min-width: 1.75rem;
703
+ color: var(--text-color);
704
+ transition: opacity 0.2s;
705
+ }
706
+
707
+ /* Toggle switch */
708
+ .solid_queue_monitor .auto-refresh-switch {
709
+ position: relative;
710
+ display: inline-block;
711
+ width: 32px;
712
+ height: 18px;
713
+ flex-shrink: 0;
714
+ }
715
+
716
+ .solid_queue_monitor .auto-refresh-switch input {
717
+ opacity: 0;
718
+ width: 0;
719
+ height: 0;
720
+ }
721
+
722
+ .solid_queue_monitor .switch-slider {
723
+ position: absolute;
724
+ cursor: pointer;
725
+ top: 0;
726
+ left: 0;
727
+ right: 0;
728
+ bottom: 0;
729
+ background-color: #d1d5db;
730
+ transition: 0.2s;
731
+ border-radius: 18px;
732
+ }
733
+
734
+ .solid_queue_monitor .switch-slider:before {
735
+ position: absolute;
736
+ content: "";
737
+ height: 14px;
738
+ width: 14px;
739
+ left: 2px;
740
+ bottom: 2px;
741
+ background-color: white;
742
+ transition: 0.2s;
743
+ border-radius: 50%;
744
+ box-shadow: 0 1px 2px rgba(0,0,0,0.2);
745
+ }
746
+
747
+ .solid_queue_monitor .auto-refresh-switch input:checked + .switch-slider {
748
+ background-color: var(--success-color);
749
+ }
750
+
751
+ .solid_queue_monitor .auto-refresh-switch input:checked + .switch-slider:before {
752
+ transform: translateX(14px);
753
+ }
754
+
755
+ .solid_queue_monitor .refresh-now-btn {
756
+ display: flex;
757
+ align-items: center;
758
+ justify-content: center;
759
+ background: transparent;
760
+ border: none;
761
+ padding: 0.25rem;
762
+ border-radius: 0.25rem;
763
+ cursor: pointer;
764
+ color: #9ca3af;
765
+ transition: all 0.2s;
766
+ }
767
+
768
+ .solid_queue_monitor .refresh-now-btn:hover {
769
+ color: var(--primary-color);
770
+ background: rgba(59, 130, 246, 0.1);
771
+ }
772
+
773
+ @media (max-width: 768px) {
774
+ .solid_queue_monitor .header-top {
775
+ flex-direction: column;
776
+ gap: 0.75rem;
777
+ }
778
+
779
+ .solid_queue_monitor .auto-refresh-container {
780
+ align-self: center;
781
+ }
782
+
783
+ /* Hide tooltip on mobile - use native title instead */
784
+ .solid_queue_monitor .auto-refresh-container::after,
785
+ .solid_queue_monitor .auto-refresh-container::before {
786
+ display: none;
787
+ }
788
+ }
588
789
  CSS
589
790
  end
590
791
  end
@@ -0,0 +1,3 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: ":memory:"
@@ -4,4 +4,6 @@ SolidQueueMonitor.setup do |config|
4
4
  config.username = 'admin' # Change this in your application
5
5
  config.password = 'password' # Change this in your application
6
6
  config.jobs_per_page = 25
7
+ config.auto_refresh_enabled = true # Enable/disable auto-refresh globally
8
+ config.auto_refresh_interval = 30 # Auto-refresh interval in seconds
7
9
  end
data/config/routes.rb CHANGED
@@ -1,7 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Guard against multiple loads of routes file in test environment
3
4
  SolidQueueMonitor::Engine.routes.draw do
4
- root to: 'overview#index', as: :root
5
+ return if SolidQueueMonitor::Engine.routes.routes.any? { |r| r.name == 'root' }
6
+
7
+ root to: 'overview#index'
5
8
 
6
9
  resources :ready_jobs, only: [:index]
7
10
  resources :scheduled_jobs, only: [:index]
@@ -17,4 +20,7 @@ SolidQueueMonitor::Engine.routes.draw do
17
20
  post 'discard_failed_job/:id', to: 'failed_jobs#discard', as: :discard_failed_job
18
21
  post 'retry_failed_jobs', to: 'failed_jobs#retry_all', as: :retry_failed_jobs
19
22
  post 'discard_failed_jobs', to: 'failed_jobs#discard_all', as: :discard_failed_jobs
23
+
24
+ post 'pause_queue', to: 'queues#pause', as: :pause_queue
25
+ post 'resume_queue', to: 'queues#resume', as: :resume_queue
20
26
  end
@@ -13,4 +13,11 @@ SolidQueueMonitor.setup do |config|
13
13
 
14
14
  # Number of jobs to display per page
15
15
  # config.jobs_per_page = 25
16
+
17
+ # Auto-refresh settings
18
+ # Enable or disable auto-refresh globally (users can still toggle it in the UI)
19
+ # config.auto_refresh_enabled = true
20
+
21
+ # Auto-refresh interval in seconds (default: 30)
22
+ # config.auto_refresh_interval = 30
16
23
  end
@@ -9,6 +9,11 @@ module SolidQueueMonitor
9
9
  # Optional: Add eager loading for production
10
10
  config.eager_load_paths << root.join('app', 'services')
11
11
 
12
+ # Ensure session middleware is available
13
+ initializer 'solid_queue_monitor.middleware' do |app|
14
+ app.config.session_store :cookie_store, key: '_solid_queue_monitor_session' unless app.config.session_store
15
+ end
16
+
12
17
  initializer 'solid_queue_monitor.assets' do |app|
13
18
  # Optional: Add assets if needed
14
19
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidQueueMonitor
4
- VERSION = '0.3.2'
4
+ VERSION = '0.5.0'
5
5
  end
@@ -6,13 +6,16 @@ require_relative 'solid_queue_monitor/engine'
6
6
  module SolidQueueMonitor
7
7
  class Error < StandardError; end
8
8
  class << self
9
- attr_accessor :username, :password, :jobs_per_page, :authentication_enabled
9
+ attr_accessor :username, :password, :jobs_per_page, :authentication_enabled,
10
+ :auto_refresh_enabled, :auto_refresh_interval
10
11
  end
11
12
 
12
13
  @username = 'admin'
13
14
  @password = 'password'
14
15
  @jobs_per_page = 25
15
16
  @authentication_enabled = false
17
+ @auto_refresh_enabled = true
18
+ @auto_refresh_interval = 30 # seconds
16
19
 
17
20
  def self.setup
18
21
  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: 0.3.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vishal Sadriya
@@ -71,10 +71,12 @@ files:
71
71
  - app/services/solid_queue_monitor/failed_job_service.rb
72
72
  - app/services/solid_queue_monitor/html_generator.rb
73
73
  - app/services/solid_queue_monitor/pagination_service.rb
74
+ - app/services/solid_queue_monitor/queue_pause_service.rb
74
75
  - app/services/solid_queue_monitor/reject_job_service.rb
75
76
  - app/services/solid_queue_monitor/stats_calculator.rb
76
77
  - app/services/solid_queue_monitor/status_calculator.rb
77
78
  - app/services/solid_queue_monitor/stylesheet_generator.rb
79
+ - config/database.yml
78
80
  - config/initializers/solid_queue_monitor.rb
79
81
  - config/routes.rb
80
82
  - lib/generators/solid_queue_monitor/install_generator.rb