solid_queue_monitor 0.1.0 → 0.1.2

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.
@@ -2,7 +2,7 @@ module SolidQueueMonitor
2
2
  class StylesheetGenerator
3
3
  def generate
4
4
  <<-CSS
5
- :root {
5
+ .solid_queue_monitor {
6
6
  --primary-color: #3b82f6;
7
7
  --success-color: #10b981;
8
8
  --error-color: #ef4444;
@@ -11,33 +11,33 @@ module SolidQueueMonitor
11
11
  --background-color: #f9fafb;
12
12
  }
13
13
 
14
- * { box-sizing: border-box; margin: 0; padding: 0; }
14
+ .solid_queue_monitor * { box-sizing: border-box; margin: 0; padding: 0; }
15
15
 
16
- body {
16
+ .solid_queue_monitor {
17
17
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
18
18
  line-height: 1.5;
19
19
  color: var(--text-color);
20
20
  background: var(--background-color);
21
21
  }
22
22
 
23
- .container {
23
+ .solid_queue_monitor .container {
24
24
  max-width: 1200px;
25
25
  margin: 0 auto;
26
26
  padding: 2rem;
27
27
  }
28
28
 
29
- header {
29
+ .solid_queue_monitor header {
30
30
  margin-bottom: 2rem;
31
31
  text-align: center;
32
32
  }
33
33
 
34
- h1 {
34
+ .solid_queue_monitor h1 {
35
35
  font-size: 2rem;
36
36
  font-weight: 600;
37
37
  margin-bottom: 0.5rem;
38
38
  }
39
39
 
40
- .navigation {
40
+ .solid_queue_monitor .navigation {
41
41
  display: flex;
42
42
  flex-wrap: wrap;
43
43
  justify-content: center;
@@ -45,7 +45,7 @@ module SolidQueueMonitor
45
45
  padding: 0.5rem;
46
46
  }
47
47
 
48
- .nav-link {
48
+ .solid_queue_monitor .nav-link {
49
49
  text-decoration: none;
50
50
  color: var(--text-color);
51
51
  padding: 0.5rem 1rem;
@@ -55,28 +55,28 @@ module SolidQueueMonitor
55
55
  transition: all 0.2s;
56
56
  }
57
57
 
58
- .nav-link:hover {
58
+ .solid_queue_monitor .nav-link:hover {
59
59
  background: var(--primary-color);
60
60
  color: white;
61
61
  }
62
62
 
63
- .section-wrapper {
63
+ .solid_queue_monitor .section-wrapper {
64
64
  margin-top: 2rem;
65
65
  }
66
66
 
67
67
 
68
- .section h2 {
68
+ .solid_queue_monitor .section h2 {
69
69
  padding: 1rem;
70
70
  border-bottom: 1px solid var(--border-color);
71
71
  font-size: 1.25rem;
72
72
  background: var(--background-color);
73
73
  }
74
74
 
75
- .stats-container {
75
+ .solid_queue_monitor .stats-container {
76
76
  margin-bottom: 2rem;
77
77
  }
78
78
 
79
- .stats {
79
+ .solid_queue_monitor .stats {
80
80
  display: flex;
81
81
  flex-direction: row;
82
82
  flex-wrap: wrap;
@@ -84,7 +84,7 @@ module SolidQueueMonitor
84
84
  margin: 0 -0.5rem;
85
85
  }
86
86
 
87
- .stat-card {
87
+ .solid_queue_monitor .stat-card {
88
88
  flex: 1 1 0;
89
89
  min-width: 150px;
90
90
  background: white;
@@ -94,7 +94,7 @@ module SolidQueueMonitor
94
94
  text-align: center;
95
95
  }
96
96
 
97
- .stat-card h3 {
97
+ .solid_queue_monitor .stat-card h3 {
98
98
  color: #6b7280;
99
99
  font-size: 0.875rem;
100
100
  text-transform: uppercase;
@@ -102,38 +102,39 @@ module SolidQueueMonitor
102
102
  margin-bottom: 0.5rem;
103
103
  }
104
104
 
105
- .stat-card p {
105
+ .solid_queue_monitor .stat-card p {
106
106
  font-size: 1.5rem;
107
107
  font-weight: 600;
108
108
  color: var(--primary-color);
109
109
  }
110
110
 
111
- .section h2 {
111
+ .solid_queue_monitor .section h2 {
112
112
  padding: 1rem;
113
113
  border-bottom: 1px solid var(--border-color);
114
114
  font-size: 1.25rem;
115
115
  }
116
116
 
117
- .table-container {
117
+ .solid_queue_monitor .table-container {
118
118
  width: 100%;
119
119
  overflow-x: auto;
120
120
  -webkit-overflow-scrolling: touch;
121
121
  }
122
122
 
123
- table {
123
+ .solid_queue_monitor table {
124
124
  width: 100%;
125
125
  min-width: 800px; /* Ensures table doesn't get too squeezed */
126
126
  border-collapse: collapse;
127
127
  white-space: nowrap;
128
128
  }
129
129
 
130
- th, td {
130
+ .solid_queue_monitor th,
131
+ .solid_queue_monitor td {
131
132
  padding: 0.75rem 1rem;
132
133
  text-align: left;
133
134
  border-bottom: 1px solid var(--border-color);
134
135
  }
135
136
 
136
- th {
137
+ .solid_queue_monitor th {
137
138
  background: var(--background-color);
138
139
  font-weight: 500;
139
140
  font-size: 0.875rem;
@@ -141,7 +142,7 @@ module SolidQueueMonitor
141
142
  letter-spacing: 0.05em;
142
143
  }
143
144
 
144
- .status-badge {
145
+ .solid_queue_monitor .status-badge {
145
146
  display: inline-block;
146
147
  padding: 0.25rem 0.5rem;
147
148
  border-radius: 9999px;
@@ -149,38 +150,38 @@ module SolidQueueMonitor
149
150
  font-weight: 500;
150
151
  }
151
152
 
152
- .table-actions {
153
- display: flex;
154
- justify-content: space-between;
155
- align-items: center;
156
- padding: 1rem;
157
- border-top: 1px solid var(--border-color);
158
- }
159
-
160
- .select-all {
161
- display: flex;
162
- align-items: center;
163
- gap: 0.5rem;
164
- cursor: pointer;
165
- }
166
-
167
- .execute-btn:disabled {
168
- opacity: 0.5;
169
- cursor: not-allowed;
170
- }
171
-
172
- input[type="checkbox"] {
173
- width: 1rem;
174
- height: 1rem;
175
- cursor: pointer;
176
- }
177
-
178
- .status-completed { background: #d1fae5; color: #065f46; }
179
- .status-failed { background: #fee2e2; color: #991b1b; }
180
- .status-scheduled { background: #dbeafe; color: #1e40af; }
181
- .status-pending { background: #f3f4f6; color: #374151; }
182
-
183
- .execute-btn {
153
+ .solid_queue_monitor .table-actions {
154
+ display: flex;
155
+ justify-content: space-between;
156
+ align-items: center;
157
+ padding: 1rem;
158
+ border-top: 1px solid var(--border-color);
159
+ }
160
+
161
+ .solid_queue_monitor .select-all {
162
+ display: flex;
163
+ align-items: center;
164
+ gap: 0.5rem;
165
+ cursor: pointer;
166
+ }
167
+
168
+ .solid_queue_monitor .execute-btn:disabled {
169
+ opacity: 0.5;
170
+ cursor: not-allowed;
171
+ }
172
+
173
+ .solid_queue_monitor input[type="checkbox"] {
174
+ width: 1rem;
175
+ height: 1rem;
176
+ cursor: pointer;
177
+ }
178
+
179
+ .solid_queue_monitor .status-completed { background: #d1fae5; color: #065f46; }
180
+ .solid_queue_monitor .status-failed { background: #fee2e2; color: #991b1b; }
181
+ .solid_queue_monitor .status-scheduled { background: #dbeafe; color: #1e40af; }
182
+ .solid_queue_monitor .status-pending { background: #f3f4f6; color: #374151; }
183
+
184
+ .solid_queue_monitor .execute-btn {
184
185
  background: var(--primary-color);
185
186
  color: white;
186
187
  border: none;
@@ -191,33 +192,34 @@ input[type="checkbox"] {
191
192
  transition: background-color 0.2s;
192
193
  }
193
194
 
194
- .execute-btn:hover {
195
+ .solid_queue_monitor .execute-btn:hover {
195
196
  background: #2563eb;
196
197
  }
197
198
 
198
- .message {
199
+ .solid_queue_monitor .message {
199
200
  padding: 1rem;
200
201
  margin-bottom: 1rem;
201
202
  border-radius: 0.375rem;
203
+ transition: opacity 0.5s ease-in-out;
202
204
  }
203
205
 
204
- .message-success {
206
+ .solid_queue_monitor .message-success {
205
207
  background: #d1fae5;
206
208
  color: #065f46;
207
209
  }
208
210
 
209
- .message-error {
211
+ .solid_queue_monitor .message-error {
210
212
  background: #fee2e2;
211
213
  color: #991b1b;
212
214
  }
213
215
 
214
- footer {
216
+ .solid_queue_monitor footer {
215
217
  text-align: center;
216
218
  padding: 2rem 0;
217
219
  color: #6b7280;
218
220
  }
219
221
 
220
- .pagination {
222
+ .solid_queue_monitor .pagination {
221
223
  display: flex;
222
224
  justify-content: center;
223
225
  gap: 0.5rem;
@@ -225,12 +227,12 @@ input[type="checkbox"] {
225
227
  padding: 1rem;
226
228
  }
227
229
 
228
- .pagination-nav {
230
+ .solid_queue_monitor .pagination-nav {
229
231
  padding: 0.5rem 1rem;
230
232
  font-size: 0.875rem;
231
233
  }
232
234
 
233
- .pagination-gap {
235
+ .solid_queue_monitor .pagination-gap {
234
236
  display: inline-flex;
235
237
  align-items: center;
236
238
  justify-content: center;
@@ -240,14 +242,14 @@ input[type="checkbox"] {
240
242
  color: var(--text-color);
241
243
  }
242
244
 
243
- .pagination-link.disabled {
245
+ .solid_queue_monitor .pagination-link.disabled {
244
246
  opacity: 0.5;
245
247
  cursor: not-allowed;
246
248
  pointer-events: none;
247
249
  }
248
250
 
249
- .pagination-link,
250
- .pagination-current {
251
+ .solid_queue_monitor .pagination-link,
252
+ .solid_queue_monitor .pagination-current {
251
253
  display: inline-flex;
252
254
  align-items: center;
253
255
  justify-content: center;
@@ -260,84 +262,122 @@ input[type="checkbox"] {
260
262
  transition: all 0.2s;
261
263
  }
262
264
 
263
- .pagination-link {
265
+ .solid_queue_monitor .pagination-link {
264
266
  background: white;
265
267
  color: var(--text-color);
266
268
  border: 1px solid var(--border-color);
267
269
  }
268
270
 
269
- .pagination-link:hover {
271
+ .solid_queue_monitor .pagination-link:hover {
270
272
  background: var(--primary-color);
271
273
  color: white;
272
274
  border-color: var(--primary-color);
273
275
  }
274
276
 
275
- .pagination-current {
277
+ .solid_queue_monitor .pagination-current {
276
278
  background: var(--primary-color);
277
279
  color: white;
278
280
  font-weight: 500;
279
281
  }
280
282
 
281
283
  @media (max-width: 768px) {
282
- .container {
284
+ .solid_queue_monitor .container {
283
285
  padding: 0.5rem;
284
286
  }
285
287
 
286
- .stats {
288
+ .solid_queue_monitor .stats {
287
289
  margin: 0;
288
290
  }
289
291
 
290
- .stat-card {
292
+ .solid_queue_monitor .stat-card {
291
293
  flex: 1 1 calc(33.333% - 1rem);
292
294
  min-width: 120px;
293
295
  }
294
296
 
295
- .section {
297
+ .solid_queue_monitor .section {
296
298
  margin: 0.5rem 0;
297
299
  border-radius: 0.375rem;
298
300
  }
299
301
 
300
- .table-container {
302
+ .solid_queue_monitor .table-container {
301
303
  width: 100%;
302
304
  overflow-x: auto;
303
305
  }
304
306
  }
305
307
 
306
308
  @media (max-width: 480px) {
307
- .stat-card {
309
+ .solid_queue_monitor .stat-card {
308
310
  flex: 1 1 calc(50% - 1rem);
309
311
  }
310
312
 
311
- .nav-link {
313
+ .solid_queue_monitor .nav-link {
312
314
  width: 100%;
313
315
  text-align: center;
314
316
  }
315
- .pagination-nav {
317
+ .solid_queue_monitor .pagination-nav {
316
318
  display: none;
317
319
  }
318
320
  }
319
321
 
320
- .filter-form-container {
322
+ .solid_queue_monitor .filter-and-actions-container {
323
+ display: flex;
324
+ justify-content: space-between;
325
+ align-items: flex-start;
326
+ gap: 1rem;
327
+ margin-bottom: 1rem;
328
+ }
329
+
330
+ .solid_queue_monitor .filter-form-container {
321
331
  background: white;
322
332
  padding: 1rem;
323
333
  border-radius: 0.5rem;
324
- margin-bottom: 1rem;
325
334
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
335
+ flex: 3;
336
+ }
337
+
338
+ .solid_queue_monitor .bulk-actions-container {
339
+ display: flex;
340
+ flex-direction: row;
341
+ gap: 0.75rem;
342
+ padding: 1rem;
343
+ background: white;
344
+ border-radius: 0.5rem;
345
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
346
+ flex: 2;
347
+ align-items: center;
348
+ justify-content: center;
349
+ }
350
+
351
+ .solid_queue_monitor .large-button {
352
+ padding: 0.75rem 1.25rem;
353
+ font-size: 0.9rem;
354
+ text-align: center;
355
+ flex: 1;
326
356
  }
327
357
 
328
- .filter-form {
358
+ @media (max-width: 992px) {
359
+ .solid_queue_monitor .filter-and-actions-container {
360
+ flex-direction: column;
361
+ }
362
+
363
+ .solid_queue_monitor .bulk-actions-container {
364
+ width: 100%;
365
+ }
366
+ }
367
+
368
+ .solid_queue_monitor .filter-form {
329
369
  display: flex;
330
370
  flex-wrap: wrap;
331
371
  gap: 1rem;
332
372
  align-items: flex-end;
333
373
  }
334
374
 
335
- .filter-group {
375
+ .solid_queue_monitor .filter-group {
336
376
  flex: 1;
337
377
  min-width: 200px;
338
378
  }
339
379
 
340
- .filter-group label {
380
+ .solid_queue_monitor .filter-group label {
341
381
  display: block;
342
382
  margin-bottom: 0.5rem;
343
383
  font-size: 0.875rem;
@@ -345,8 +385,8 @@ input[type="checkbox"] {
345
385
  color: #4b5563;
346
386
  }
347
387
 
348
- .filter-group input,
349
- .filter-group select {
388
+ .solid_queue_monitor .filter-group input,
389
+ .solid_queue_monitor .filter-group select {
350
390
  width: 100%;
351
391
  padding: 0.5rem;
352
392
  border: 1px solid #d1d5db;
@@ -354,12 +394,12 @@ input[type="checkbox"] {
354
394
  font-size: 0.875rem;
355
395
  }
356
396
 
357
- .filter-actions {
397
+ .solid_queue_monitor .filter-actions {
358
398
  display: flex;
359
399
  gap: 0.5rem;
360
400
  }
361
401
 
362
- .filter-button {
402
+ .solid_queue_monitor .filter-button {
363
403
  background: var(--primary-color);
364
404
  color: white;
365
405
  border: none;
@@ -370,11 +410,11 @@ input[type="checkbox"] {
370
410
  transition: background-color 0.2s;
371
411
  }
372
412
 
373
- .filter-button:hover {
413
+ .solid_queue_monitor .filter-button:hover {
374
414
  background: #2563eb;
375
415
  }
376
416
 
377
- .reset-button {
417
+ .solid_queue_monitor .reset-button {
378
418
  background: #f3f4f6;
379
419
  color: #4b5563;
380
420
  border: 1px solid #d1d5db;
@@ -386,9 +426,119 @@ input[type="checkbox"] {
386
426
  transition: background-color 0.2s;
387
427
  }
388
428
 
389
- .reset-button:hover {
429
+ .solid_queue_monitor .reset-button:hover {
390
430
  background: #e5e7eb;
391
431
  }
432
+
433
+ /* Action buttons for retry/discard */
434
+ .solid_queue_monitor .action-button {
435
+ padding: 0.5rem 1rem;
436
+ border-radius: 0.375rem;
437
+ font-size: 0.75rem;
438
+ font-weight: 500;
439
+ cursor: pointer;
440
+ transition: background-color 0.2s;
441
+ border: none;
442
+ text-decoration: none;
443
+ }
444
+
445
+ .solid_queue_monitor .retry-button {
446
+ background: #3b82f6;
447
+ color: white;
448
+ }
449
+
450
+ .solid_queue_monitor .retry-button:hover {
451
+ background: #2563eb;
452
+ }
453
+
454
+ .solid_queue_monitor .discard-button {
455
+ background: #ef4444;
456
+ color: white;
457
+ }
458
+
459
+ .solid_queue_monitor .discard-button:hover {
460
+ background: #dc2626;
461
+ }
462
+
463
+ .solid_queue_monitor .action-button:disabled {
464
+ opacity: 0.5;
465
+ cursor: not-allowed;
466
+ }
467
+
468
+ .solid_queue_monitor .inline-form {
469
+ display: inline-block;
470
+ margin-right: 0.5rem;
471
+ }
472
+
473
+ .solid_queue_monitor .actions-cell {
474
+ white-space: nowrap;
475
+ }
476
+
477
+ .solid_queue_monitor .bulk-actions {
478
+ display: flex;
479
+ gap: 0.5rem;
480
+ }
481
+
482
+ .solid_queue_monitor .error-message {
483
+ color: #dc2626;
484
+ font-weight: 500;
485
+ margin-bottom: 0.25rem;
486
+ }
487
+
488
+ .solid_queue_monitor .error-backtrace {
489
+ font-size: 0.75rem;
490
+ white-space: pre-wrap;
491
+ max-height: 200px;
492
+ overflow-y: auto;
493
+ background: #f3f4f6;
494
+ padding: 0.5rem;
495
+ border-radius: 0.25rem;
496
+ margin-top: 0.5rem;
497
+ }
498
+
499
+ .solid_queue_monitor details {
500
+ margin-top: 0.25rem;
501
+ }
502
+
503
+ .solid_queue_monitor summary {
504
+ cursor: pointer;
505
+ color: #6b7280;
506
+ font-size: 0.75rem;
507
+ }
508
+
509
+ .solid_queue_monitor summary:hover {
510
+ color: #4b5563;
511
+ }
512
+
513
+ .solid_queue_monitor .job-checkbox,
514
+ .solid_queue_monitor .select-all-checkbox {
515
+ width: 1rem;
516
+ height: 1rem;
517
+ }
518
+
519
+ .solid_queue_monitor .bulk-actions-bar {
520
+ display: flex;
521
+ gap: 0.75rem;
522
+ margin: 1rem 0;
523
+ background: white;
524
+ padding: 0.75rem;
525
+ border-radius: 0.5rem;
526
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
527
+ }
528
+
529
+ .solid_queue_monitor .bulk-actions-bar .action-button {
530
+ padding: 0.6rem 1rem;
531
+ font-size: 0.875rem;
532
+ }
533
+
534
+ .solid_queue_monitor .execute-button {
535
+ background: var(--primary-color);
536
+ color: white;
537
+ }
538
+
539
+ .solid_queue_monitor .execute-button:hover {
540
+ background: #2563eb;
541
+ }
392
542
  CSS
393
543
  end
394
544
  end
data/config/routes.rb CHANGED
@@ -1,11 +1,17 @@
1
1
  SolidQueueMonitor::Engine.routes.draw do
2
2
  root to: 'monitor#index'
3
3
 
4
- get 'ready_jobs', to: 'monitor#ready_jobs'
5
- get 'scheduled_jobs', to: 'monitor#scheduled_jobs'
6
- get 'failed_jobs', to: 'monitor#failed_jobs'
7
- get 'recurring_jobs', to: 'monitor#recurring_jobs'
8
- get 'queues', to: 'monitor#queues'
4
+ get 'ready_jobs', to: 'monitor#ready_jobs', as: 'ready_jobs'
5
+ get 'scheduled_jobs', to: 'monitor#scheduled_jobs', as: 'scheduled_jobs'
6
+ get 'failed_jobs', to: 'monitor#failed_jobs', as: 'failed_jobs'
7
+ get 'recurring_jobs', to: 'monitor#recurring_jobs', as: 'recurring_jobs'
8
+ get 'queues', to: 'monitor#queues', as: 'queues'
9
9
 
10
- post 'execute_jobs', to: 'monitor#execute_jobs'
10
+ post 'execute_jobs', to: 'monitor#execute_jobs', as: 'execute_jobs'
11
+
12
+ # Failed job actions
13
+ post 'retry_failed_job/:id', to: 'monitor#retry_failed_job', as: 'retry_failed_job'
14
+ post 'discard_failed_job/:id', to: 'monitor#discard_failed_job', as: 'discard_failed_job'
15
+ post 'retry_failed_jobs', to: 'monitor#retry_failed_jobs', as: 'retry_failed_jobs'
16
+ post 'discard_failed_jobs', to: 'monitor#discard_failed_jobs', as: 'discard_failed_jobs'
11
17
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidQueueMonitor
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.2"
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid_queue_monitor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vishal Sadriya
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-03-15 00:00:00.000000000 Z
10
+ date: 2025-03-18 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rails
@@ -198,6 +198,7 @@ files:
198
198
  - app/presenters/solid_queue_monitor/stats_presenter.rb
199
199
  - app/services/solid_queue_monitor/authentication_service.rb
200
200
  - app/services/solid_queue_monitor/execute_job_service.rb
201
+ - app/services/solid_queue_monitor/failed_job_service.rb
201
202
  - app/services/solid_queue_monitor/html_generator.rb
202
203
  - app/services/solid_queue_monitor/pagination_service.rb
203
204
  - app/services/solid_queue_monitor/stats_calculator.rb