sidekiq_queue_manager 1.0.0 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: decb676a394e280ac75b0a45c17541551d2191ad74205c57bf8cf5b333eb4341
4
- data.tar.gz: ef24c3fd05b309c5df6eb852791238d8a458393f002d371c526b8fd3dc2512ed
3
+ metadata.gz: 643e7129093c9ce8ed3d1c44b0066249f339ba8e05619644e35500b481f8fff6
4
+ data.tar.gz: fb0ca16a5c9df55ad9e384dd1c36c04adef2e88fe97dcd218896cde084f96cde
5
5
  SHA512:
6
- metadata.gz: 553576ce338f3b59688ad806a5d227f021d2c2a0b84a775246724e1ba4e1c089a4f66ac796e7a155ce2da0da13b95bacd941287d31315c273b9e0a54f483f448
7
- data.tar.gz: 100b3bdbf614358e6099721185ef05ed395620172fe743030cd1d64698c161afa72a75aac413cb6af450b0abef3dbbac48e71677909aa4031138c7c15a6ff032
6
+ metadata.gz: 878fe78f4d684a661a143de01fd6d16624ae0dbe7ab1b38d9fac4d96673e16a3615aa5f947142c8237ca6bf6841d10bfb25fb9d4e2b013835af1a57169300d79
7
+ data.tar.gz: ec5751c4bf9ca6be1b9b310438a22aefde78bf7c5c566cf8478cc5c9760dbd1384e12f26dd24c7b9cdfcbb60bfaccfa1f26849dd0e6f147f96becf416254fbd8
@@ -755,6 +755,9 @@ class SidekiqQueueManagerUI {
755
755
  <button class="sqm-actions-menu-item sqm-actions-danger" data-action="clear" data-queue="${queueName}">
756
756
  🗑️ Clear All Jobs
757
757
  </button>
758
+ <button class="sqm-actions-menu-item sqm-actions-danger" data-action="delete_queue" data-queue="${queueName}">
759
+ ❌ Delete Queue
760
+ </button>
758
761
  </div>
759
762
  </div>
760
763
  `;
@@ -847,6 +850,8 @@ class SidekiqQueueManagerUI {
847
850
  return this.unblockQueue(queueName);
848
851
  case 'clear':
849
852
  return this.clearQueue(queueName);
853
+ case 'delete_queue':
854
+ return this.deleteQueue(queueName);
850
855
  default:
851
856
  throw new Error(`Unknown action: ${action}`);
852
857
  }
@@ -1391,6 +1396,52 @@ class SidekiqQueueManagerUI {
1391
1396
  }
1392
1397
  }
1393
1398
 
1399
+ async deleteQueue(queueName) {
1400
+ const confirmed = await this.showCustomConfirm(
1401
+ 'Delete Queue Completely',
1402
+ `Are you sure you want to PERMANENTLY DELETE the "${queueName}" queue?`,
1403
+ 'danger',
1404
+ 'This action will remove the queue completely from Sidekiq, along with all its jobs. This cannot be undone.'
1405
+ );
1406
+
1407
+ if (!confirmed) return;
1408
+
1409
+ // Triple confirmation for queue deletion (more destructive than clearing)
1410
+ const doubleConfirmed = await this.showCustomConfirm(
1411
+ 'Final Warning',
1412
+ `This will PERMANENTLY DELETE the entire "${queueName}" queue!`,
1413
+ 'danger',
1414
+ 'The queue will be completely removed from Sidekiq and cannot be recovered.'
1415
+ );
1416
+
1417
+ if (!doubleConfirmed) return;
1418
+
1419
+ const confirmation = await this.showCustomPrompt(
1420
+ 'Type Queue Name to Confirm',
1421
+ `Type "${queueName}" exactly to confirm queue deletion:`,
1422
+ queueName,
1423
+ 'This is your last chance to cancel this destructive operation.'
1424
+ );
1425
+
1426
+ if (confirmation !== queueName) {
1427
+ this.showNotification('Queue deletion cancelled', 'info');
1428
+ return;
1429
+ }
1430
+
1431
+ try {
1432
+ const response = await this.apiCall(`/queues/${queueName}`, { method: 'DELETE' });
1433
+
1434
+ if (response.success) {
1435
+ this.showNotification(`Queue "${queueName}" deleted permanently`, 'success');
1436
+ await this.refreshQueues();
1437
+ } else {
1438
+ throw new Error(response.message || 'Failed to delete queue');
1439
+ }
1440
+ } catch (error) {
1441
+ this.showNotification(`Failed to delete queue: ${error.message}`, 'error');
1442
+ }
1443
+ }
1444
+
1394
1445
  async pauseAllQueues() {
1395
1446
  if (!await this.showCustomConfirm('Pause All Queues', 'Are you sure you want to pause all non-critical queues?')) {
1396
1447
  return;
@@ -13,6 +13,9 @@ module SidekiqQueueManager
13
13
  # Engine-specific layout
14
14
  layout 'sidekiq_queue_manager/application'
15
15
 
16
+ # Make mount path available to views
17
+ helper_method :engine_mount_path
18
+
16
19
  # Authentication and security using Ruby's declarative approach
17
20
  before_action :authenticate_access
18
21
  before_action :set_security_headers
@@ -164,7 +167,7 @@ module SidekiqQueueManager
164
167
  end
165
168
 
166
169
  def direct_serving_strategy
167
- mount_path = sidekiq_dashboard.root_path.chomp('/')
170
+ mount_path = engine_mount_path
168
171
 
169
172
  {
170
173
  css_path: "#{mount_path}/assets/sidekiq_queue_manager/application.css",
@@ -174,6 +177,16 @@ module SidekiqQueueManager
174
177
  }
175
178
  end
176
179
 
180
+ # Get the mount path from the engine's routes
181
+ def engine_mount_path
182
+ # Extract mount path from the current request path
183
+ if request.path =~ %r{^(/[^/]+)}
184
+ Regexp.last_match(1)
185
+ else
186
+ '/sidekiq_manager' # Default fallback
187
+ end
188
+ end
189
+
177
190
  # Common helper for accessing main application methods
178
191
  def main_app
179
192
  @main_app ||= Rails.application.routes.url_helpers
@@ -17,7 +17,7 @@ module SidekiqQueueManager
17
17
  before_action :set_queue_name, only: %i[
18
18
  queue_status pause_queue resume_queue jobs delete_job
19
19
  set_limit remove_limit set_process_limit remove_process_limit
20
- block unblock clear
20
+ block unblock clear delete_queue
21
21
  ]
22
22
 
23
23
  # Comprehensive error handling for all controller actions
@@ -250,6 +250,17 @@ module SidekiqQueueManager
250
250
  ), status: result[:success] ? :ok : :unprocessable_entity
251
251
  end
252
252
 
253
+ # Delete a queue completely
254
+ # DELETE /queues/:name
255
+ def delete_queue
256
+ result = QueueService.delete_queue(@queue_name)
257
+ render json: api_response(
258
+ success: result[:success],
259
+ message: result[:message],
260
+ data: result[:data]
261
+ ), status: result[:success] ? :ok : :unprocessable_entity
262
+ end
263
+
253
264
  private
254
265
 
255
266
  # ========================================
@@ -202,7 +202,16 @@ module SidekiqQueueManager
202
202
 
203
203
  queue = Sidekiq::Queue[queue_name]
204
204
  operation = method_name.split('_')[0] # 'set', 'remove', 'block', 'unblock'
205
- attribute = method_name.match(/_(queue_|process_)?(.+)$/)[2] # 'limit', 'process_limit'
205
+
206
+ # Extract the correct attribute name based on the method
207
+ attribute = case method_name
208
+ when /set_queue_limit|remove_queue_limit/
209
+ 'limit' # queue.limit for queue limits
210
+ when /set_process_limit|remove_process_limit/
211
+ 'process_limit' # queue.process_limit for process limits
212
+ else
213
+ method_name.split('_', 2)[1] # fallback for block/unblock operations
214
+ end
206
215
 
207
216
  perform_queue_operation(queue, queue_name, operation, attribute, *args)
208
217
  rescue StandardError => e
@@ -224,18 +233,42 @@ module SidekiqQueueManager
224
233
  handle_service_error(e, "clear queue '#{queue_name}'")
225
234
  end
226
235
 
236
+ def delete_queue(queue_name)
237
+ return failure_response('Invalid queue name') unless valid_queue?(queue_name)
238
+ return failure_response('Cannot delete critical queue') if critical_queue?(queue_name)
239
+
240
+ queue = Sidekiq::Queue[queue_name]
241
+ jobs_count = queue.size
242
+
243
+ # Clear all jobs first
244
+ queue.clear
245
+
246
+ # Complete cleanup of queue from Redis
247
+ Sidekiq.redis do |conn|
248
+ # Remove queue from the queues set
249
+ conn.srem('queues', queue_name)
250
+
251
+ # Delete the actual queue key from Redis
252
+ conn.del("queue:#{queue_name}")
253
+
254
+ # Clean up any related keys (like paused queue markers)
255
+ conn.del("queue:#{queue_name}:paused")
256
+ end
257
+
258
+ log_operation("Queue '#{queue_name}' deleted completely - #{jobs_count} jobs removed")
259
+ success_response("Queue '#{queue_name}' deleted successfully", jobs_cleared: jobs_count)
260
+ rescue StandardError => e
261
+ handle_service_error(e, "delete queue '#{queue_name}'")
262
+ end
263
+
227
264
  private
228
265
 
229
266
  # ========================================
230
267
  # Queue Information Helpers
231
268
  # ========================================
232
269
 
233
- def available_queues
234
- @available_queues ||= discover_queues
235
- end
236
-
237
270
  # Ruby's functional approach to queue discovery
238
- def discover_queues
271
+ def available_queues
239
272
  [
240
273
  # Method 1: Sidekiq's built-in discovery
241
274
  -> { Sidekiq::Queue.all.map(&:name) },
@@ -54,7 +54,7 @@
54
54
  <!-- Configuration for JavaScript -->
55
55
  <script type="text/javascript">
56
56
  window.SidekiqQueueManagerConfig = {
57
- mountPath: '<%= sidekiq_dashboard.root_path %>',
57
+ mountPath: '<%= engine_mount_path %>',
58
58
  refreshInterval: <%= SidekiqQueueManager.configuration.refresh_interval %>,
59
59
  theme: '<%= SidekiqQueueManager.configuration.theme %>',
60
60
  criticalQueues: <%= SidekiqQueueManager.configuration.critical_queues.to_json.html_safe %>,
data/config/routes.rb CHANGED
@@ -31,6 +31,7 @@ SidekiqQueueManager::Engine.routes.draw do
31
31
  get '/queues/:name/jobs', to: 'dashboard#jobs', as: :queue_jobs
32
32
  delete '/queues/:name/delete_job', to: 'dashboard#delete_job', as: :delete_queue_job
33
33
  post '/queues/:name/clear', to: 'dashboard#clear', as: :clear_queue
34
+ delete '/queues/:name', to: 'dashboard#delete_queue', as: :delete_queue
34
35
 
35
36
  # Queue limits and configuration
36
37
  post '/queues/:name/set_limit', to: 'dashboard#set_limit', as: :set_queue_limit
@@ -8,21 +8,28 @@ module SidekiqQueueManager
8
8
  end
9
9
 
10
10
  def call(env)
11
- # Only log requests to our gem's paths
12
- if env['PATH_INFO'].start_with?('/sidekiq_dashboard')
13
- start_time = Time.current
14
-
15
- status, headers, body = @app.call(env)
11
+ start_time = Time.current
12
+ status, headers, body = @app.call(env)
16
13
 
14
+ # Only log if this request was handled by our engine
15
+ if handled_by_sidekiq_queue_manager?(env)
17
16
  end_time = Time.current
18
17
  duration = (end_time - start_time) * 1000 # milliseconds
18
+ path_info = env['PATH_INFO'] || ''
19
19
 
20
- Rails.logger.info "[SidekiqQueueManager] #{env['REQUEST_METHOD']} #{env['PATH_INFO']} - #{status} (#{duration.round(2)}ms)"
21
-
22
- [status, headers, body]
23
- else
24
- @app.call(env)
20
+ Rails.logger.info "[SidekiqQueueManager] #{env['REQUEST_METHOD']} #{path_info} - #{status} (#{duration.round(2)}ms)"
25
21
  end
22
+
23
+ [status, headers, body]
24
+ end
25
+
26
+ private
27
+
28
+ # Check if the request was handled by our engine controllers
29
+ def handled_by_sidekiq_queue_manager?(env)
30
+ # Check if the controller class is from our engine
31
+ controller_class = env['action_controller.instance']&.class&.name
32
+ controller_class&.start_with?('SidekiqQueueManager::')
26
33
  end
27
34
  end
28
35
  end
@@ -7,6 +7,6 @@ module SidekiqQueueManager
7
7
  # - MAJOR: Incompatible API changes
8
8
  # - MINOR: Add functionality in backward compatible manner
9
9
  # - PATCH: Backward compatible bug fixes
10
- VERSION = '1.0.0'
10
+ VERSION = '1.0.1'
11
11
  end
12
12
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq_queue_manager
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jamal Awad