sidekiq_queue_manager 1.0.2 → 1.1.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/app/assets/javascripts/sidekiq_queue_manager/application.js +859 -12
- data/app/assets/stylesheets/sidekiq_queue_manager/application.css +467 -7
- data/app/controllers/sidekiq_queue_manager/dashboard_controller.rb +202 -0
- data/app/services/sidekiq_queue_manager/queue_service.rb +485 -0
- data/app/views/sidekiq_queue_manager/dashboard/index.html.erb +287 -19
- data/config/routes.rb +21 -0
- data/lib/sidekiq_queue_manager/version.rb +1 -1
- metadata +1 -1
@@ -236,10 +236,48 @@
|
|
236
236
|
|
237
237
|
.sqm-stats-grid {
|
238
238
|
display: grid;
|
239
|
-
grid-template-columns: repeat(
|
239
|
+
grid-template-columns: repeat(7, 1fr);
|
240
240
|
gap: 1rem;
|
241
241
|
}
|
242
242
|
|
243
|
+
@media (max-width: 1200px) {
|
244
|
+
.sqm-stats-grid {
|
245
|
+
grid-template-columns: repeat(4, 1fr);
|
246
|
+
}
|
247
|
+
}
|
248
|
+
|
249
|
+
@media (max-width: 900px) {
|
250
|
+
.sqm-stats-grid {
|
251
|
+
grid-template-columns: repeat(2, 1fr);
|
252
|
+
}
|
253
|
+
}
|
254
|
+
|
255
|
+
@media (max-width: 600px) {
|
256
|
+
.sqm-stats-grid {
|
257
|
+
grid-template-columns: 1fr;
|
258
|
+
}
|
259
|
+
|
260
|
+
.sqm-stat-card {
|
261
|
+
padding: 0.75rem;
|
262
|
+
text-align: center;
|
263
|
+
}
|
264
|
+
|
265
|
+
.sqm-stat-value {
|
266
|
+
font-size: 1.5rem !important;
|
267
|
+
}
|
268
|
+
|
269
|
+
.sqm-stat-value[data-length="8"],
|
270
|
+
.sqm-stat-value[data-length="9"],
|
271
|
+
.sqm-stat-value[data-length="10"],
|
272
|
+
.sqm-stat-value[data-length="11"],
|
273
|
+
.sqm-stat-value[data-length="12"],
|
274
|
+
.sqm-stat-value[data-length="13"],
|
275
|
+
.sqm-stat-value[data-length="14"],
|
276
|
+
.sqm-stat-value[data-length="long"] {
|
277
|
+
font-size: 1.25rem !important;
|
278
|
+
}
|
279
|
+
}
|
280
|
+
|
243
281
|
.sqm-stat-card {
|
244
282
|
background: var(--sqm-card);
|
245
283
|
border: 1px solid var(--sqm-border);
|
@@ -248,6 +286,7 @@
|
|
248
286
|
transition: var(--sqm-transition);
|
249
287
|
position: relative;
|
250
288
|
overflow: hidden;
|
289
|
+
min-width: 0; /* Allow flex shrinking */
|
251
290
|
}
|
252
291
|
|
253
292
|
.sqm-stat-card:hover {
|
@@ -268,7 +307,28 @@
|
|
268
307
|
font-weight: 700;
|
269
308
|
color: var(--sqm-foreground);
|
270
309
|
font-family: var(--sqm-font-mono);
|
271
|
-
line-height: 1;
|
310
|
+
line-height: 1.1;
|
311
|
+
word-break: break-all;
|
312
|
+
overflow-wrap: break-word;
|
313
|
+
min-width: 0;
|
314
|
+
}
|
315
|
+
|
316
|
+
/* Responsive font sizing for large numbers */
|
317
|
+
.sqm-stat-value[data-length="8"],
|
318
|
+
.sqm-stat-value[data-length="9"] {
|
319
|
+
font-size: 1.5rem;
|
320
|
+
}
|
321
|
+
|
322
|
+
.sqm-stat-value[data-length="10"],
|
323
|
+
.sqm-stat-value[data-length="11"] {
|
324
|
+
font-size: 1.25rem;
|
325
|
+
}
|
326
|
+
|
327
|
+
.sqm-stat-value[data-length="12"],
|
328
|
+
.sqm-stat-value[data-length="13"],
|
329
|
+
.sqm-stat-value[data-length="14"],
|
330
|
+
.sqm-stat-value[data-length="long"] {
|
331
|
+
font-size: 1rem;
|
272
332
|
}
|
273
333
|
|
274
334
|
/* ========================================
|
@@ -933,11 +993,6 @@
|
|
933
993
|
gap: 0.75rem;
|
934
994
|
}
|
935
995
|
|
936
|
-
.sqm-stats-grid {
|
937
|
-
grid-template-columns: 1fr;
|
938
|
-
gap: 0.75rem;
|
939
|
-
}
|
940
|
-
|
941
996
|
.sqm-controls {
|
942
997
|
flex-direction: column;
|
943
998
|
align-items: stretch;
|
@@ -1015,4 +1070,409 @@
|
|
1015
1070
|
transition-duration: 0.01ms !important;
|
1016
1071
|
scroll-behavior: auto !important;
|
1017
1072
|
}
|
1073
|
+
}
|
1074
|
+
|
1075
|
+
/* ========================================
|
1076
|
+
Tab Navigation System
|
1077
|
+
======================================== */
|
1078
|
+
|
1079
|
+
.sqm-tabs-nav {
|
1080
|
+
display: flex;
|
1081
|
+
align-items: center;
|
1082
|
+
gap: 0.125rem;
|
1083
|
+
margin-bottom: 1.5rem;
|
1084
|
+
padding: 0.25rem;
|
1085
|
+
background: var(--sqm-muted);
|
1086
|
+
border-radius: var(--sqm-radius);
|
1087
|
+
border: 1px solid var(--sqm-border);
|
1088
|
+
}
|
1089
|
+
|
1090
|
+
.sqm-tab {
|
1091
|
+
display: flex;
|
1092
|
+
align-items: center;
|
1093
|
+
gap: 0.5rem;
|
1094
|
+
padding: 0.75rem 1rem;
|
1095
|
+
font-size: 0.875rem;
|
1096
|
+
font-weight: 500;
|
1097
|
+
line-height: 1;
|
1098
|
+
color: var(--sqm-muted-foreground);
|
1099
|
+
background: transparent;
|
1100
|
+
border: none;
|
1101
|
+
border-radius: var(--sqm-radius-sm);
|
1102
|
+
cursor: pointer;
|
1103
|
+
transition: var(--sqm-transition);
|
1104
|
+
white-space: nowrap;
|
1105
|
+
min-height: 2.5rem;
|
1106
|
+
}
|
1107
|
+
|
1108
|
+
.sqm-tab:hover {
|
1109
|
+
color: var(--sqm-foreground);
|
1110
|
+
background: var(--sqm-accent);
|
1111
|
+
}
|
1112
|
+
|
1113
|
+
.sqm-tab:focus {
|
1114
|
+
outline: 2px solid var(--sqm-ring);
|
1115
|
+
outline-offset: 2px;
|
1116
|
+
}
|
1117
|
+
|
1118
|
+
.sqm-tab-active {
|
1119
|
+
color: var(--sqm-foreground);
|
1120
|
+
background: var(--sqm-background);
|
1121
|
+
border: 1px solid var(--sqm-border);
|
1122
|
+
box-shadow: var(--sqm-shadow-sm);
|
1123
|
+
}
|
1124
|
+
|
1125
|
+
.sqm-tab-active:hover {
|
1126
|
+
background: var(--sqm-background);
|
1127
|
+
}
|
1128
|
+
|
1129
|
+
.sqm-tab svg {
|
1130
|
+
flex-shrink: 0;
|
1131
|
+
width: 1rem;
|
1132
|
+
height: 1rem;
|
1133
|
+
}
|
1134
|
+
|
1135
|
+
.sqm-tab-count {
|
1136
|
+
display: inline-flex;
|
1137
|
+
align-items: center;
|
1138
|
+
justify-content: center;
|
1139
|
+
min-width: 1.25rem;
|
1140
|
+
height: 1.25rem;
|
1141
|
+
padding: 0 0.375rem;
|
1142
|
+
font-size: 0.75rem;
|
1143
|
+
font-weight: 600;
|
1144
|
+
line-height: 1;
|
1145
|
+
color: var(--sqm-muted-foreground);
|
1146
|
+
background: var(--sqm-muted);
|
1147
|
+
border-radius: 0.625rem;
|
1148
|
+
transition: var(--sqm-transition);
|
1149
|
+
}
|
1150
|
+
|
1151
|
+
.sqm-tab-active .sqm-tab-count {
|
1152
|
+
color: var(--sqm-primary-foreground);
|
1153
|
+
background: var(--sqm-primary);
|
1154
|
+
}
|
1155
|
+
|
1156
|
+
/* ========================================
|
1157
|
+
Tab Content System
|
1158
|
+
======================================== */
|
1159
|
+
|
1160
|
+
.sqm-tab-content {
|
1161
|
+
position: relative;
|
1162
|
+
}
|
1163
|
+
|
1164
|
+
.sqm-tab-panel {
|
1165
|
+
display: block;
|
1166
|
+
outline: none;
|
1167
|
+
}
|
1168
|
+
|
1169
|
+
.sqm-tab-panel.sqm-hidden {
|
1170
|
+
display: none;
|
1171
|
+
}
|
1172
|
+
|
1173
|
+
.sqm-tab-panel-active {
|
1174
|
+
display: block;
|
1175
|
+
}
|
1176
|
+
|
1177
|
+
/* ========================================
|
1178
|
+
Section Controls
|
1179
|
+
======================================== */
|
1180
|
+
|
1181
|
+
.sqm-section-controls {
|
1182
|
+
display: flex;
|
1183
|
+
align-items: center;
|
1184
|
+
justify-content: space-between;
|
1185
|
+
gap: 1rem;
|
1186
|
+
margin-bottom: 1.5rem;
|
1187
|
+
padding: 1rem;
|
1188
|
+
background: var(--sqm-card);
|
1189
|
+
border: 1px solid var(--sqm-border);
|
1190
|
+
border-radius: var(--sqm-radius);
|
1191
|
+
}
|
1192
|
+
|
1193
|
+
.sqm-controls-left,
|
1194
|
+
.sqm-controls-right {
|
1195
|
+
display: flex;
|
1196
|
+
align-items: center;
|
1197
|
+
gap: 0.75rem;
|
1198
|
+
}
|
1199
|
+
|
1200
|
+
.sqm-filter-input {
|
1201
|
+
width: 240px;
|
1202
|
+
height: 2.5rem;
|
1203
|
+
padding: 0 0.75rem;
|
1204
|
+
font-size: 0.875rem;
|
1205
|
+
color: var(--sqm-foreground);
|
1206
|
+
background: var(--sqm-background);
|
1207
|
+
border: 1px solid var(--sqm-border);
|
1208
|
+
border-radius: var(--sqm-radius);
|
1209
|
+
transition: var(--sqm-transition);
|
1210
|
+
}
|
1211
|
+
|
1212
|
+
.sqm-filter-input:focus {
|
1213
|
+
outline: none;
|
1214
|
+
border-color: var(--sqm-ring);
|
1215
|
+
box-shadow: 0 0 0 2px rgba(var(--sqm-ring), 0.2);
|
1216
|
+
}
|
1217
|
+
|
1218
|
+
.sqm-filter-input::placeholder {
|
1219
|
+
color: var(--sqm-muted-foreground);
|
1220
|
+
}
|
1221
|
+
|
1222
|
+
/* ========================================
|
1223
|
+
Pagination System
|
1224
|
+
======================================== */
|
1225
|
+
|
1226
|
+
.sqm-pagination {
|
1227
|
+
display: flex;
|
1228
|
+
align-items: center;
|
1229
|
+
justify-content: space-between;
|
1230
|
+
margin-top: 1.5rem;
|
1231
|
+
padding: 1rem;
|
1232
|
+
background: var(--sqm-card);
|
1233
|
+
border: 1px solid var(--sqm-border);
|
1234
|
+
border-radius: var(--sqm-radius);
|
1235
|
+
}
|
1236
|
+
|
1237
|
+
.sqm-pagination-info {
|
1238
|
+
font-size: 0.875rem;
|
1239
|
+
color: var(--sqm-muted-foreground);
|
1240
|
+
}
|
1241
|
+
|
1242
|
+
.sqm-pagination-controls {
|
1243
|
+
display: flex;
|
1244
|
+
align-items: center;
|
1245
|
+
gap: 0.5rem;
|
1246
|
+
}
|
1247
|
+
|
1248
|
+
.sqm-pagination-btn {
|
1249
|
+
display: flex;
|
1250
|
+
align-items: center;
|
1251
|
+
justify-content: center;
|
1252
|
+
width: 2.5rem;
|
1253
|
+
height: 2.5rem;
|
1254
|
+
font-size: 0.875rem;
|
1255
|
+
font-weight: 500;
|
1256
|
+
color: var(--sqm-foreground);
|
1257
|
+
background: var(--sqm-background);
|
1258
|
+
border: 1px solid var(--sqm-border);
|
1259
|
+
border-radius: var(--sqm-radius);
|
1260
|
+
cursor: pointer;
|
1261
|
+
transition: var(--sqm-transition);
|
1262
|
+
}
|
1263
|
+
|
1264
|
+
.sqm-pagination-btn:hover:not(:disabled) {
|
1265
|
+
background: var(--sqm-accent);
|
1266
|
+
}
|
1267
|
+
|
1268
|
+
.sqm-pagination-btn:disabled {
|
1269
|
+
color: var(--sqm-muted-foreground);
|
1270
|
+
cursor: not-allowed;
|
1271
|
+
opacity: 0.5;
|
1272
|
+
}
|
1273
|
+
|
1274
|
+
.sqm-pagination-btn svg {
|
1275
|
+
width: 1rem;
|
1276
|
+
height: 1rem;
|
1277
|
+
}
|
1278
|
+
|
1279
|
+
.sqm-pagination-current {
|
1280
|
+
background: var(--sqm-primary);
|
1281
|
+
color: var(--sqm-primary-foreground);
|
1282
|
+
border-color: var(--sqm-primary);
|
1283
|
+
}
|
1284
|
+
|
1285
|
+
.sqm-pagination-current:hover {
|
1286
|
+
background: var(--sqm-primary);
|
1287
|
+
}
|
1288
|
+
|
1289
|
+
/* ========================================
|
1290
|
+
Enhanced Table Styles for New Tabs
|
1291
|
+
======================================== */
|
1292
|
+
|
1293
|
+
.sqm-scheduled-section .sqm-table,
|
1294
|
+
.sqm-retries-section .sqm-table,
|
1295
|
+
.sqm-dead-section .sqm-table {
|
1296
|
+
margin-top: 0;
|
1297
|
+
}
|
1298
|
+
|
1299
|
+
.sqm-table .sqm-error-preview {
|
1300
|
+
max-width: 200px;
|
1301
|
+
font-family: var(--sqm-font-mono);
|
1302
|
+
font-size: 0.75rem;
|
1303
|
+
color: var(--sqm-destructive);
|
1304
|
+
white-space: nowrap;
|
1305
|
+
overflow: hidden;
|
1306
|
+
text-overflow: ellipsis;
|
1307
|
+
}
|
1308
|
+
|
1309
|
+
.sqm-table .sqm-time-relative {
|
1310
|
+
font-size: 0.75rem;
|
1311
|
+
color: var(--sqm-muted-foreground);
|
1312
|
+
white-space: nowrap;
|
1313
|
+
}
|
1314
|
+
|
1315
|
+
.sqm-table .sqm-job-args {
|
1316
|
+
max-width: 150px;
|
1317
|
+
font-family: var(--sqm-font-mono);
|
1318
|
+
font-size: 0.75rem;
|
1319
|
+
color: var(--sqm-muted-foreground);
|
1320
|
+
white-space: nowrap;
|
1321
|
+
overflow: hidden;
|
1322
|
+
text-overflow: ellipsis;
|
1323
|
+
}
|
1324
|
+
|
1325
|
+
.sqm-table .sqm-retry-count {
|
1326
|
+
display: inline-flex;
|
1327
|
+
align-items: center;
|
1328
|
+
gap: 0.25rem;
|
1329
|
+
font-size: 0.75rem;
|
1330
|
+
font-weight: 500;
|
1331
|
+
}
|
1332
|
+
|
1333
|
+
.sqm-table .sqm-retry-progress {
|
1334
|
+
display: inline-block;
|
1335
|
+
width: 60px;
|
1336
|
+
height: 4px;
|
1337
|
+
background: var(--sqm-muted);
|
1338
|
+
border-radius: 2px;
|
1339
|
+
overflow: hidden;
|
1340
|
+
}
|
1341
|
+
|
1342
|
+
.sqm-table .sqm-retry-progress-bar {
|
1343
|
+
height: 100%;
|
1344
|
+
background: var(--sqm-warning);
|
1345
|
+
transition: width 0.3s ease;
|
1346
|
+
}
|
1347
|
+
|
1348
|
+
/* ========================================
|
1349
|
+
Job Status Indicators
|
1350
|
+
======================================== */
|
1351
|
+
|
1352
|
+
.sqm-job-status {
|
1353
|
+
display: inline-flex;
|
1354
|
+
align-items: center;
|
1355
|
+
gap: 0.375rem;
|
1356
|
+
padding: 0.25rem 0.5rem;
|
1357
|
+
font-size: 0.75rem;
|
1358
|
+
font-weight: 500;
|
1359
|
+
border-radius: var(--sqm-radius-sm);
|
1360
|
+
}
|
1361
|
+
|
1362
|
+
.sqm-job-status-scheduled {
|
1363
|
+
color: var(--sqm-info-foreground);
|
1364
|
+
background: var(--sqm-info);
|
1365
|
+
}
|
1366
|
+
|
1367
|
+
.sqm-job-status-retrying {
|
1368
|
+
color: var(--sqm-warning-foreground);
|
1369
|
+
background: var(--sqm-warning);
|
1370
|
+
}
|
1371
|
+
|
1372
|
+
.sqm-job-status-dead {
|
1373
|
+
color: var(--sqm-destructive-foreground);
|
1374
|
+
background: var(--sqm-destructive);
|
1375
|
+
}
|
1376
|
+
|
1377
|
+
.sqm-job-status-dot {
|
1378
|
+
width: 0.375rem;
|
1379
|
+
height: 0.375rem;
|
1380
|
+
border-radius: 50%;
|
1381
|
+
background: currentColor;
|
1382
|
+
}
|
1383
|
+
|
1384
|
+
/* ========================================
|
1385
|
+
Action Button Variants for Job Management
|
1386
|
+
======================================== */
|
1387
|
+
|
1388
|
+
.sqm-btn-enqueue {
|
1389
|
+
color: var(--sqm-info-foreground);
|
1390
|
+
background: var(--sqm-info);
|
1391
|
+
border-color: var(--sqm-info);
|
1392
|
+
}
|
1393
|
+
|
1394
|
+
.sqm-btn-enqueue:hover {
|
1395
|
+
background: hsl(199.1 89.1% 42%);
|
1396
|
+
}
|
1397
|
+
|
1398
|
+
.sqm-btn-resurrect {
|
1399
|
+
color: var(--sqm-success-foreground);
|
1400
|
+
background: var(--sqm-success);
|
1401
|
+
border-color: var(--sqm-success);
|
1402
|
+
}
|
1403
|
+
|
1404
|
+
.sqm-btn-resurrect:hover {
|
1405
|
+
background: hsl(142.1 76.2% 32%);
|
1406
|
+
}
|
1407
|
+
|
1408
|
+
.sqm-btn-kill {
|
1409
|
+
color: var(--sqm-destructive-foreground);
|
1410
|
+
background: var(--sqm-destructive);
|
1411
|
+
border-color: var(--sqm-destructive);
|
1412
|
+
}
|
1413
|
+
|
1414
|
+
.sqm-btn-kill:hover {
|
1415
|
+
background: hsl(0 84.2% 55%);
|
1416
|
+
}
|
1417
|
+
|
1418
|
+
/* ========================================
|
1419
|
+
Responsive Design for Tabs
|
1420
|
+
======================================== */
|
1421
|
+
|
1422
|
+
@media (max-width: 768px) {
|
1423
|
+
.sqm-tabs-nav {
|
1424
|
+
flex-wrap: wrap;
|
1425
|
+
gap: 0.25rem;
|
1426
|
+
}
|
1427
|
+
|
1428
|
+
.sqm-tab {
|
1429
|
+
flex: 1;
|
1430
|
+
min-width: 0;
|
1431
|
+
justify-content: center;
|
1432
|
+
padding: 0.625rem 0.75rem;
|
1433
|
+
}
|
1434
|
+
|
1435
|
+
.sqm-tab span:not(.sqm-tab-count) {
|
1436
|
+
display: none;
|
1437
|
+
}
|
1438
|
+
|
1439
|
+
.sqm-section-controls {
|
1440
|
+
flex-direction: column;
|
1441
|
+
align-items: stretch;
|
1442
|
+
gap: 1rem;
|
1443
|
+
}
|
1444
|
+
|
1445
|
+
.sqm-controls-left,
|
1446
|
+
.sqm-controls-right {
|
1447
|
+
justify-content: center;
|
1448
|
+
}
|
1449
|
+
|
1450
|
+
.sqm-filter-input {
|
1451
|
+
width: 100%;
|
1452
|
+
max-width: 300px;
|
1453
|
+
}
|
1454
|
+
|
1455
|
+
.sqm-pagination {
|
1456
|
+
flex-direction: column;
|
1457
|
+
gap: 1rem;
|
1458
|
+
text-align: center;
|
1459
|
+
}
|
1460
|
+
}
|
1461
|
+
|
1462
|
+
@media (max-width: 480px) {
|
1463
|
+
.sqm-tab-count {
|
1464
|
+
min-width: 1rem;
|
1465
|
+
height: 1rem;
|
1466
|
+
font-size: 0.625rem;
|
1467
|
+
}
|
1468
|
+
|
1469
|
+
.sqm-section-controls {
|
1470
|
+
padding: 0.75rem;
|
1471
|
+
}
|
1472
|
+
|
1473
|
+
.sqm-controls-left,
|
1474
|
+
.sqm-controls-right {
|
1475
|
+
flex-wrap: wrap;
|
1476
|
+
gap: 0.5rem;
|
1477
|
+
}
|
1018
1478
|
}
|
@@ -19,6 +19,10 @@ module SidekiqQueueManager
|
|
19
19
|
set_limit remove_limit set_process_limit remove_process_limit
|
20
20
|
block unblock clear delete_queue
|
21
21
|
]
|
22
|
+
before_action :set_job_id, only: %i[
|
23
|
+
delete_scheduled_job enqueue_scheduled_job retry_job_now delete_retry_job
|
24
|
+
kill_retry_job resurrect_dead_job delete_dead_job
|
25
|
+
]
|
22
26
|
|
23
27
|
# Comprehensive error handling for all controller actions
|
24
28
|
rescue_from StandardError, with: :handle_api_error
|
@@ -73,6 +77,193 @@ module SidekiqQueueManager
|
|
73
77
|
end
|
74
78
|
end
|
75
79
|
|
80
|
+
# ========================================
|
81
|
+
# Scheduled Jobs Management
|
82
|
+
# ========================================
|
83
|
+
|
84
|
+
# Get scheduled jobs with pagination and filtering
|
85
|
+
# GET /scheduled
|
86
|
+
def scheduled_jobs
|
87
|
+
page = params[:page] || 1
|
88
|
+
per_page = params[:per_page] || 25
|
89
|
+
filter = params[:filter]
|
90
|
+
|
91
|
+
result = QueueService.scheduled_jobs(page: page, per_page: per_page, filter: filter)
|
92
|
+
render json: api_response(
|
93
|
+
success: result[:success],
|
94
|
+
message: result[:message],
|
95
|
+
data: result
|
96
|
+
), status: result[:success] ? :ok : :unprocessable_entity
|
97
|
+
end
|
98
|
+
|
99
|
+
# Delete a scheduled job
|
100
|
+
# DELETE /scheduled/:id
|
101
|
+
def delete_scheduled_job
|
102
|
+
result = QueueService.delete_scheduled_job(@job_id)
|
103
|
+
render json: api_response(
|
104
|
+
success: result[:success],
|
105
|
+
message: result[:message]
|
106
|
+
), status: result[:success] ? :ok : :unprocessable_entity
|
107
|
+
end
|
108
|
+
|
109
|
+
# Enqueue a scheduled job immediately
|
110
|
+
# POST /scheduled/:id/enqueue
|
111
|
+
def enqueue_scheduled_job
|
112
|
+
result = QueueService.enqueue_scheduled_job(@job_id)
|
113
|
+
render json: api_response(
|
114
|
+
success: result[:success],
|
115
|
+
message: result[:message]
|
116
|
+
), status: result[:success] ? :ok : :unprocessable_entity
|
117
|
+
end
|
118
|
+
|
119
|
+
# Clear all scheduled jobs
|
120
|
+
# POST /scheduled/clear
|
121
|
+
def clear_scheduled_jobs
|
122
|
+
filter = params[:filter]
|
123
|
+
result = QueueService.clear_scheduled_jobs(filter: filter)
|
124
|
+
render json: api_response(
|
125
|
+
success: result[:success],
|
126
|
+
message: result[:message],
|
127
|
+
data: result
|
128
|
+
), status: result[:success] ? :ok : :unprocessable_entity
|
129
|
+
end
|
130
|
+
|
131
|
+
# ========================================
|
132
|
+
# Retry Jobs Management
|
133
|
+
# ========================================
|
134
|
+
|
135
|
+
# Get retry jobs with pagination and filtering
|
136
|
+
# GET /retries
|
137
|
+
def retry_jobs
|
138
|
+
page = params[:page] || 1
|
139
|
+
per_page = params[:per_page] || 25
|
140
|
+
filter = params[:filter]
|
141
|
+
|
142
|
+
result = QueueService.retry_jobs(page: page, per_page: per_page, filter: filter)
|
143
|
+
render json: api_response(
|
144
|
+
success: result[:success],
|
145
|
+
message: result[:message],
|
146
|
+
data: result
|
147
|
+
), status: result[:success] ? :ok : :unprocessable_entity
|
148
|
+
end
|
149
|
+
|
150
|
+
# Retry a job immediately
|
151
|
+
# POST /retries/:id/retry
|
152
|
+
def retry_job_now
|
153
|
+
result = QueueService.retry_job_now(@job_id)
|
154
|
+
render json: api_response(
|
155
|
+
success: result[:success],
|
156
|
+
message: result[:message]
|
157
|
+
), status: result[:success] ? :ok : :unprocessable_entity
|
158
|
+
end
|
159
|
+
|
160
|
+
# Delete a retry job
|
161
|
+
# DELETE /retries/:id
|
162
|
+
def delete_retry_job
|
163
|
+
result = QueueService.delete_retry_job(@job_id)
|
164
|
+
render json: api_response(
|
165
|
+
success: result[:success],
|
166
|
+
message: result[:message]
|
167
|
+
), status: result[:success] ? :ok : :unprocessable_entity
|
168
|
+
end
|
169
|
+
|
170
|
+
# Kill a retry job (move to dead queue)
|
171
|
+
# POST /retries/:id/kill
|
172
|
+
def kill_retry_job
|
173
|
+
result = QueueService.kill_retry_job(@job_id)
|
174
|
+
render json: api_response(
|
175
|
+
success: result[:success],
|
176
|
+
message: result[:message]
|
177
|
+
), status: result[:success] ? :ok : :unprocessable_entity
|
178
|
+
end
|
179
|
+
|
180
|
+
# Clear all retry jobs
|
181
|
+
# POST /retries/clear
|
182
|
+
def clear_retry_jobs
|
183
|
+
filter = params[:filter]
|
184
|
+
result = QueueService.clear_retry_jobs(filter: filter)
|
185
|
+
render json: api_response(
|
186
|
+
success: result[:success],
|
187
|
+
message: result[:message],
|
188
|
+
data: result
|
189
|
+
), status: result[:success] ? :ok : :unprocessable_entity
|
190
|
+
end
|
191
|
+
|
192
|
+
# Retry all jobs in retry queue
|
193
|
+
# POST /retries/retry_all
|
194
|
+
def retry_all_jobs
|
195
|
+
filter = params[:filter]
|
196
|
+
result = QueueService.retry_all_jobs(filter: filter)
|
197
|
+
render json: api_response(
|
198
|
+
success: result[:success],
|
199
|
+
message: result[:message],
|
200
|
+
data: result
|
201
|
+
), status: result[:success] ? :ok : :unprocessable_entity
|
202
|
+
end
|
203
|
+
|
204
|
+
# ========================================
|
205
|
+
# Dead Jobs Management
|
206
|
+
# ========================================
|
207
|
+
|
208
|
+
# Get dead jobs with pagination and filtering
|
209
|
+
# GET /dead
|
210
|
+
def dead_jobs
|
211
|
+
page = params[:page] || 1
|
212
|
+
per_page = params[:per_page] || 25
|
213
|
+
filter = params[:filter]
|
214
|
+
|
215
|
+
result = QueueService.dead_jobs(page: page, per_page: per_page, filter: filter)
|
216
|
+
render json: api_response(
|
217
|
+
success: result[:success],
|
218
|
+
message: result[:message],
|
219
|
+
data: result
|
220
|
+
), status: result[:success] ? :ok : :unprocessable_entity
|
221
|
+
end
|
222
|
+
|
223
|
+
# Resurrect a dead job
|
224
|
+
# POST /dead/:id/resurrect
|
225
|
+
def resurrect_dead_job
|
226
|
+
result = QueueService.resurrect_dead_job(@job_id)
|
227
|
+
render json: api_response(
|
228
|
+
success: result[:success],
|
229
|
+
message: result[:message]
|
230
|
+
), status: result[:success] ? :ok : :unprocessable_entity
|
231
|
+
end
|
232
|
+
|
233
|
+
# Delete a dead job permanently
|
234
|
+
# DELETE /dead/:id
|
235
|
+
def delete_dead_job
|
236
|
+
result = QueueService.delete_dead_job(@job_id)
|
237
|
+
render json: api_response(
|
238
|
+
success: result[:success],
|
239
|
+
message: result[:message]
|
240
|
+
), status: result[:success] ? :ok : :unprocessable_entity
|
241
|
+
end
|
242
|
+
|
243
|
+
# Clear all dead jobs
|
244
|
+
# POST /dead/clear
|
245
|
+
def clear_dead_jobs
|
246
|
+
filter = params[:filter]
|
247
|
+
result = QueueService.clear_dead_jobs(filter: filter)
|
248
|
+
render json: api_response(
|
249
|
+
success: result[:success],
|
250
|
+
message: result[:message],
|
251
|
+
data: result
|
252
|
+
), status: result[:success] ? :ok : :unprocessable_entity
|
253
|
+
end
|
254
|
+
|
255
|
+
# Resurrect all dead jobs
|
256
|
+
# POST /dead/resurrect_all
|
257
|
+
def resurrect_all_dead_jobs
|
258
|
+
filter = params[:filter]
|
259
|
+
result = QueueService.resurrect_all_dead_jobs(filter: filter)
|
260
|
+
render json: api_response(
|
261
|
+
success: result[:success],
|
262
|
+
message: result[:message],
|
263
|
+
data: result
|
264
|
+
), status: result[:success] ? :ok : :unprocessable_entity
|
265
|
+
end
|
266
|
+
|
76
267
|
# ========================================
|
77
268
|
# Queue Control Actions (JSON API)
|
78
269
|
# ========================================
|
@@ -293,6 +484,17 @@ module SidekiqQueueManager
|
|
293
484
|
nil
|
294
485
|
end
|
295
486
|
|
487
|
+
def set_job_id
|
488
|
+
@job_id = params[:id] || params[:job_id]
|
489
|
+
|
490
|
+
if @job_id.blank?
|
491
|
+
render json: api_error_response('Job ID is required'), status: :bad_request
|
492
|
+
return
|
493
|
+
end
|
494
|
+
|
495
|
+
@job_id
|
496
|
+
end
|
497
|
+
|
296
498
|
def available_queue_names
|
297
499
|
@available_queue_names ||= SidekiqQueueManager::QueueService.send(:available_queues)
|
298
500
|
end
|