worker-que 1.0.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.
- package/DASHBOARD-QUICKSTART.md +278 -0
- package/DASHBOARD.md +556 -0
- package/LICENSE +21 -0
- package/README.md +414 -0
- package/SSL-QUICK-REFERENCE.md +225 -0
- package/SSL.md +516 -0
- package/dist/client.d.ts +11 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +64 -0
- package/dist/client.js.map +1 -0
- package/dist/dashboard/index.d.ts +34 -0
- package/dist/dashboard/index.d.ts.map +1 -0
- package/dist/dashboard/index.js +164 -0
- package/dist/dashboard/index.js.map +1 -0
- package/dist/dashboard/service.d.ts +66 -0
- package/dist/dashboard/service.d.ts.map +1 -0
- package/dist/dashboard/service.js +201 -0
- package/dist/dashboard/service.js.map +1 -0
- package/dist/dashboard/views.d.ts +3 -0
- package/dist/dashboard/views.d.ts.map +1 -0
- package/dist/dashboard/views.js +786 -0
- package/dist/dashboard/views.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/job.d.ts +19 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/job.js +36 -0
- package/dist/job.js.map +1 -0
- package/dist/sql.d.ts +8 -0
- package/dist/sql.d.ts.map +1 -0
- package/dist/sql.js +57 -0
- package/dist/sql.js.map +1 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +6 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +31 -0
- package/dist/utils.js.map +1 -0
- package/dist/worker.d.ts +19 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +99 -0
- package/dist/worker.js.map +1 -0
- package/migrations/schema.sql +26 -0
- package/package.json +105 -0
package/DASHBOARD.md
ADDED
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
# Dashboard Documentation
|
|
2
|
+
|
|
3
|
+
A beautiful, real-time web dashboard for monitoring and managing your Que job queue.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 📊 **Real-time Statistics** - Monitor total, ready, scheduled, and failed jobs
|
|
8
|
+
- 📈 **Visual Analytics** - Charts showing job distribution by queue and class
|
|
9
|
+
- 🔍 **Advanced Filtering** - Filter jobs by status, queue, and job class
|
|
10
|
+
- 🔄 **Job Management** - Retry failed jobs or delete jobs directly from the UI
|
|
11
|
+
- 🎨 **Modern UI** - Beautiful, responsive design that works on all devices
|
|
12
|
+
- 🔐 **Authentication** - Built-in support for custom authentication
|
|
13
|
+
- ⚡ **Auto-refresh** - Configurable real-time updates
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
### Installation
|
|
18
|
+
|
|
19
|
+
The dashboard requires Express.js:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install express
|
|
23
|
+
npm install --save-dev @types/express # If using TypeScript
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Basic Setup
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import express from 'express';
|
|
30
|
+
import { Pool } from 'pg';
|
|
31
|
+
import { createDashboard } from 'que-ts/dashboard';
|
|
32
|
+
|
|
33
|
+
const app = express();
|
|
34
|
+
const pool = new Pool({
|
|
35
|
+
host: 'localhost',
|
|
36
|
+
port: 5432,
|
|
37
|
+
database: 'myapp',
|
|
38
|
+
user: 'postgres',
|
|
39
|
+
password: 'password',
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Mount dashboard at /admin/queue
|
|
43
|
+
app.use('/admin/queue', createDashboard(pool));
|
|
44
|
+
|
|
45
|
+
app.listen(3000, () => {
|
|
46
|
+
console.log('Dashboard: http://localhost:3000/admin/queue');
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
That's it! Visit http://localhost:3000/admin/queue to see your dashboard.
|
|
51
|
+
|
|
52
|
+
## Configuration Options
|
|
53
|
+
|
|
54
|
+
### DashboardOptions
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
interface DashboardOptions {
|
|
58
|
+
// Dashboard title (shown in header)
|
|
59
|
+
title?: string; // Default: 'Que Dashboard'
|
|
60
|
+
|
|
61
|
+
// Base path for API routes
|
|
62
|
+
basePath?: string; // Default: '/que'
|
|
63
|
+
|
|
64
|
+
// Auto-refresh interval in milliseconds
|
|
65
|
+
refreshInterval?: number; // Default: 5000 (5 seconds)
|
|
66
|
+
|
|
67
|
+
// Maximum number of recent failures to show
|
|
68
|
+
maxRecentFailures?: number; // Default: 50
|
|
69
|
+
|
|
70
|
+
// Authentication function
|
|
71
|
+
auth?: (req, res, next) => boolean | Promise<boolean>;
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Examples
|
|
76
|
+
|
|
77
|
+
#### Custom Title and Refresh Rate
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
app.use('/queue', createDashboard(pool, {
|
|
81
|
+
title: 'Production Job Queue',
|
|
82
|
+
refreshInterval: 2000, // Refresh every 2 seconds
|
|
83
|
+
}));
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### With Different Base Path
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
app.use('/jobs', createDashboard(pool, {
|
|
90
|
+
basePath: '/jobs', // Important: must match mount path
|
|
91
|
+
}));
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Authentication
|
|
95
|
+
|
|
96
|
+
### Basic Authentication
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
app.use('/admin/queue', createDashboard(pool, {
|
|
100
|
+
auth: (req, res, next) => {
|
|
101
|
+
const apiKey = req.headers['x-api-key'];
|
|
102
|
+
return apiKey === 'your-secret-api-key';
|
|
103
|
+
}
|
|
104
|
+
}));
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Session-based Authentication
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import session from 'express-session';
|
|
111
|
+
|
|
112
|
+
app.use(session({
|
|
113
|
+
secret: 'your-session-secret',
|
|
114
|
+
resave: false,
|
|
115
|
+
saveUninitialized: false,
|
|
116
|
+
}));
|
|
117
|
+
|
|
118
|
+
app.use('/admin/queue', createDashboard(pool, {
|
|
119
|
+
auth: (req, res, next) => {
|
|
120
|
+
// Only allow authenticated admins
|
|
121
|
+
return req.session?.user?.role === 'admin';
|
|
122
|
+
}
|
|
123
|
+
}));
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Async Authentication (Database Check)
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
app.use('/admin/queue', createDashboard(pool, {
|
|
130
|
+
auth: async (req, res, next) => {
|
|
131
|
+
const token = req.headers['authorization']?.split(' ')[1];
|
|
132
|
+
|
|
133
|
+
if (!token) return false;
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const user = await verifyToken(token);
|
|
137
|
+
return user.hasPermission('view-queue');
|
|
138
|
+
} catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}));
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### JWT Authentication
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import jwt from 'jsonwebtoken';
|
|
149
|
+
|
|
150
|
+
app.use('/admin/queue', createDashboard(pool, {
|
|
151
|
+
auth: (req, res, next) => {
|
|
152
|
+
try {
|
|
153
|
+
const token = req.cookies.auth_token;
|
|
154
|
+
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
|
155
|
+
return decoded.role === 'admin';
|
|
156
|
+
} catch {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}));
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## API Routes
|
|
164
|
+
|
|
165
|
+
The dashboard exposes several API routes that you can use programmatically:
|
|
166
|
+
|
|
167
|
+
### GET /api/stats
|
|
168
|
+
|
|
169
|
+
Get queue statistics.
|
|
170
|
+
|
|
171
|
+
**Response:**
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"total": 156,
|
|
175
|
+
"scheduled": 23,
|
|
176
|
+
"ready": 120,
|
|
177
|
+
"failed": 13,
|
|
178
|
+
"errorRate": 8.33,
|
|
179
|
+
"avgErrorCount": 2.5,
|
|
180
|
+
"oldestJob": "2024-01-15T10:30:00Z",
|
|
181
|
+
"newestJob": "2024-01-15T15:45:00Z",
|
|
182
|
+
"totalByQueue": [
|
|
183
|
+
{ "queue": "default", "count": 100 },
|
|
184
|
+
{ "queue": "critical", "count": 56 }
|
|
185
|
+
],
|
|
186
|
+
"totalByClass": [
|
|
187
|
+
{ "jobClass": "SendEmail", "count": 80 },
|
|
188
|
+
{ "jobClass": "ProcessPayment", "count": 76 }
|
|
189
|
+
],
|
|
190
|
+
"recentFailures": [...]
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### GET /api/jobs
|
|
195
|
+
|
|
196
|
+
Get paginated list of jobs with optional filters.
|
|
197
|
+
|
|
198
|
+
**Query Parameters:**
|
|
199
|
+
- `status`: Filter by status (`all`, `ready`, `scheduled`, `failed`)
|
|
200
|
+
- `queue`: Filter by queue name
|
|
201
|
+
- `jobClass`: Filter by job class
|
|
202
|
+
- `limit`: Number of results per page (default: 50)
|
|
203
|
+
- `offset`: Pagination offset (default: 0)
|
|
204
|
+
|
|
205
|
+
**Example:**
|
|
206
|
+
```
|
|
207
|
+
GET /api/jobs?status=failed&queue=critical&limit=20&offset=0
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Response:**
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"jobs": [
|
|
214
|
+
{
|
|
215
|
+
"id": 123,
|
|
216
|
+
"queue": "critical",
|
|
217
|
+
"priority": 10,
|
|
218
|
+
"runAt": "2024-01-15T14:30:00Z",
|
|
219
|
+
"jobClass": "ProcessPayment",
|
|
220
|
+
"args": [{ "amount": 100, "userId": 456 }],
|
|
221
|
+
"errorCount": 3,
|
|
222
|
+
"lastError": "Payment gateway timeout"
|
|
223
|
+
}
|
|
224
|
+
],
|
|
225
|
+
"total": 156
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### GET /api/jobs/:id
|
|
230
|
+
|
|
231
|
+
Get details of a specific job.
|
|
232
|
+
|
|
233
|
+
**Response:**
|
|
234
|
+
```json
|
|
235
|
+
{
|
|
236
|
+
"id": 123,
|
|
237
|
+
"queue": "default",
|
|
238
|
+
"priority": 100,
|
|
239
|
+
"runAt": "2024-01-15T14:30:00Z",
|
|
240
|
+
"jobClass": "SendEmail",
|
|
241
|
+
"args": [{ "to": "user@example.com" }],
|
|
242
|
+
"errorCount": 0
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### DELETE /api/jobs/:id
|
|
247
|
+
|
|
248
|
+
Delete a job from the queue.
|
|
249
|
+
|
|
250
|
+
**Response:**
|
|
251
|
+
```json
|
|
252
|
+
{
|
|
253
|
+
"success": true,
|
|
254
|
+
"message": "Job deleted"
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### POST /api/jobs/:id/retry
|
|
259
|
+
|
|
260
|
+
Retry a failed job (resets error count and schedules immediately).
|
|
261
|
+
|
|
262
|
+
**Response:**
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"success": true,
|
|
266
|
+
"message": "Job queued for retry"
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### GET /api/queues
|
|
271
|
+
|
|
272
|
+
Get list of all queue names.
|
|
273
|
+
|
|
274
|
+
**Response:**
|
|
275
|
+
```json
|
|
276
|
+
["(default)", "critical", "background", "email"]
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### GET /api/job-classes
|
|
280
|
+
|
|
281
|
+
Get list of all job class names.
|
|
282
|
+
|
|
283
|
+
**Response:**
|
|
284
|
+
```json
|
|
285
|
+
["SendEmail", "ProcessPayment", "GenerateReport", "SendReminder"]
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Programmatic Usage
|
|
289
|
+
|
|
290
|
+
You can also use the dashboard service directly in your code:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import { Pool } from 'pg';
|
|
294
|
+
import { DashboardService } from 'que-ts/dashboard';
|
|
295
|
+
|
|
296
|
+
const pool = new Pool({ /* config */ });
|
|
297
|
+
const dashboard = new DashboardService(pool);
|
|
298
|
+
|
|
299
|
+
// Get statistics
|
|
300
|
+
const stats = await dashboard.getStats();
|
|
301
|
+
console.log(`Total jobs: ${stats.total}`);
|
|
302
|
+
console.log(`Failed jobs: ${stats.failed}`);
|
|
303
|
+
|
|
304
|
+
// Get jobs with filters
|
|
305
|
+
const { jobs, total } = await dashboard.getJobs({
|
|
306
|
+
status: 'failed',
|
|
307
|
+
queue: 'critical',
|
|
308
|
+
limit: 10,
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// Retry a failed job
|
|
312
|
+
await dashboard.retryJob(123);
|
|
313
|
+
|
|
314
|
+
// Delete a job
|
|
315
|
+
await dashboard.deleteJob(456);
|
|
316
|
+
|
|
317
|
+
// Get available queues
|
|
318
|
+
const queues = await dashboard.getQueues();
|
|
319
|
+
|
|
320
|
+
// Get job classes
|
|
321
|
+
const jobClasses = await dashboard.getJobClasses();
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Integration Examples
|
|
325
|
+
|
|
326
|
+
### With Existing Express App
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
import express from 'express';
|
|
330
|
+
import { Pool } from 'pg';
|
|
331
|
+
import { createDashboard } from 'que-ts/dashboard';
|
|
332
|
+
|
|
333
|
+
const app = express();
|
|
334
|
+
const pool = new Pool({ /* config */ });
|
|
335
|
+
|
|
336
|
+
// Your existing routes
|
|
337
|
+
app.get('/', (req, res) => {
|
|
338
|
+
res.send('Home page');
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
app.get('/api/users', (req, res) => {
|
|
342
|
+
// Your API
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Add dashboard
|
|
346
|
+
app.use('/admin/queue', createDashboard(pool, {
|
|
347
|
+
title: 'My App Queue',
|
|
348
|
+
auth: (req) => req.session?.isAdmin,
|
|
349
|
+
}));
|
|
350
|
+
|
|
351
|
+
app.listen(3000);
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### With Multiple Workers
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
import express from 'express';
|
|
358
|
+
import { Pool } from 'pg';
|
|
359
|
+
import { createDashboard } from 'que-ts/dashboard';
|
|
360
|
+
import { Worker } from 'que-ts';
|
|
361
|
+
|
|
362
|
+
const app = express();
|
|
363
|
+
const pool = new Pool({ /* config */ });
|
|
364
|
+
|
|
365
|
+
// Create dashboard
|
|
366
|
+
app.use('/queue', createDashboard(pool));
|
|
367
|
+
|
|
368
|
+
// Start multiple workers
|
|
369
|
+
const criticalWorker = new Worker(
|
|
370
|
+
{ /* db config */ },
|
|
371
|
+
{ queue: 'critical', interval: 100 }
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
const backgroundWorker = new Worker(
|
|
375
|
+
{ /* db config */ },
|
|
376
|
+
{ queue: 'background', interval: 5000 }
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
// Register handlers and start workers
|
|
380
|
+
criticalWorker.register('ProcessPayment', handlePayment);
|
|
381
|
+
backgroundWorker.register('GenerateReport', handleReport);
|
|
382
|
+
|
|
383
|
+
criticalWorker.work();
|
|
384
|
+
backgroundWorker.work();
|
|
385
|
+
|
|
386
|
+
app.listen(3000);
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Behind a Proxy (nginx, etc.)
|
|
390
|
+
|
|
391
|
+
If running behind a proxy, make sure to set the correct `basePath`:
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
// nginx config:
|
|
395
|
+
// location /app/queue {
|
|
396
|
+
// proxy_pass http://localhost:3000/queue;
|
|
397
|
+
// }
|
|
398
|
+
|
|
399
|
+
app.use('/queue', createDashboard(pool, {
|
|
400
|
+
basePath: '/queue', // Not /app/queue - that's handled by nginx
|
|
401
|
+
}));
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
## Deployment Considerations
|
|
405
|
+
|
|
406
|
+
### Production Checklist
|
|
407
|
+
|
|
408
|
+
- [ ] **Enable Authentication** - Never deploy without auth in production
|
|
409
|
+
- [ ] **Use HTTPS** - Protect sensitive job data
|
|
410
|
+
- [ ] **Restrict Access** - Use firewall rules or network policies
|
|
411
|
+
- [ ] **Monitor Performance** - Dashboard queries can impact database
|
|
412
|
+
- [ ] **Set Reasonable Refresh Interval** - Don't overwhelm your database
|
|
413
|
+
- [ ] **Use Connection Pooling** - Reuse database connections
|
|
414
|
+
- [ ] **Enable Logging** - Track dashboard access and actions
|
|
415
|
+
|
|
416
|
+
### Environment Variables
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
import { createDashboard } from 'que-ts/dashboard';
|
|
420
|
+
|
|
421
|
+
const pool = new Pool({
|
|
422
|
+
connectionString: process.env.DATABASE_URL,
|
|
423
|
+
ssl: process.env.NODE_ENV === 'production',
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
app.use(process.env.DASHBOARD_PATH || '/admin/queue',
|
|
427
|
+
createDashboard(pool, {
|
|
428
|
+
title: process.env.APP_NAME || 'Queue Dashboard',
|
|
429
|
+
refreshInterval: parseInt(process.env.DASHBOARD_REFRESH_MS || '5000'),
|
|
430
|
+
auth: (req) => {
|
|
431
|
+
const apiKey = req.headers['x-api-key'];
|
|
432
|
+
return apiKey === process.env.DASHBOARD_API_KEY;
|
|
433
|
+
},
|
|
434
|
+
})
|
|
435
|
+
);
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### Docker Example
|
|
439
|
+
|
|
440
|
+
```dockerfile
|
|
441
|
+
FROM node:18-alpine
|
|
442
|
+
|
|
443
|
+
WORKDIR /app
|
|
444
|
+
COPY package*.json ./
|
|
445
|
+
RUN npm ci --only=production
|
|
446
|
+
|
|
447
|
+
COPY . .
|
|
448
|
+
|
|
449
|
+
ENV NODE_ENV=production
|
|
450
|
+
ENV DASHBOARD_PATH=/queue
|
|
451
|
+
ENV DASHBOARD_REFRESH_MS=5000
|
|
452
|
+
|
|
453
|
+
EXPOSE 3000
|
|
454
|
+
|
|
455
|
+
CMD ["node", "dist/server.js"]
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Troubleshooting
|
|
459
|
+
|
|
460
|
+
### Dashboard shows "Failed to load jobs"
|
|
461
|
+
|
|
462
|
+
**Check:**
|
|
463
|
+
1. Database connection is working
|
|
464
|
+
2. `que_jobs` table exists
|
|
465
|
+
3. User has SELECT permissions on `que_jobs`
|
|
466
|
+
|
|
467
|
+
### Authentication not working
|
|
468
|
+
|
|
469
|
+
**Check:**
|
|
470
|
+
1. Auth function returns `true` or `false` (not undefined)
|
|
471
|
+
2. For async auth, ensure function is declared `async`
|
|
472
|
+
3. Check browser console for 403 errors
|
|
473
|
+
|
|
474
|
+
### Slow dashboard performance
|
|
475
|
+
|
|
476
|
+
**Solutions:**
|
|
477
|
+
1. Increase `refreshInterval` to reduce polling frequency
|
|
478
|
+
2. Add database indexes:
|
|
479
|
+
```sql
|
|
480
|
+
CREATE INDEX idx_que_jobs_run_at ON que_jobs(run_at);
|
|
481
|
+
CREATE INDEX idx_que_jobs_error_count ON que_jobs(error_count);
|
|
482
|
+
CREATE INDEX idx_que_jobs_queue ON que_jobs(queue);
|
|
483
|
+
CREATE INDEX idx_que_jobs_job_class ON que_jobs(job_class);
|
|
484
|
+
```
|
|
485
|
+
3. Reduce `maxRecentFailures` option
|
|
486
|
+
4. Use database connection pooling
|
|
487
|
+
|
|
488
|
+
### Dashboard not refreshing
|
|
489
|
+
|
|
490
|
+
**Check:**
|
|
491
|
+
1. JavaScript is enabled in browser
|
|
492
|
+
2. No console errors in browser dev tools
|
|
493
|
+
3. API endpoints are accessible (check network tab)
|
|
494
|
+
4. CORS is configured if dashboard is on different domain
|
|
495
|
+
|
|
496
|
+
## Screenshots
|
|
497
|
+
|
|
498
|
+
### Main Dashboard
|
|
499
|
+

|
|
500
|
+
|
|
501
|
+
Shows:
|
|
502
|
+
- Real-time job statistics
|
|
503
|
+
- Ready, scheduled, and failed job counts
|
|
504
|
+
- Visual charts for queue and class distribution
|
|
505
|
+
|
|
506
|
+
### Jobs List
|
|
507
|
+

|
|
508
|
+
|
|
509
|
+
Features:
|
|
510
|
+
- Filter by status, queue, and job class
|
|
511
|
+
- Pagination for large job lists
|
|
512
|
+
- Quick actions to retry or delete jobs
|
|
513
|
+
|
|
514
|
+
### Recent Failures
|
|
515
|
+

|
|
516
|
+
|
|
517
|
+
Displays:
|
|
518
|
+
- Jobs that have failed
|
|
519
|
+
- Error messages
|
|
520
|
+
- Retry and delete actions
|
|
521
|
+
|
|
522
|
+
## Customization
|
|
523
|
+
|
|
524
|
+
### Custom Styles
|
|
525
|
+
|
|
526
|
+
The dashboard uses inline styles for portability. To customize:
|
|
527
|
+
|
|
528
|
+
1. Create your own view template:
|
|
529
|
+
```typescript
|
|
530
|
+
import { getDashboardHTML } from 'que-ts/dashboard/views';
|
|
531
|
+
|
|
532
|
+
// Extend or modify the HTML
|
|
533
|
+
const customHTML = getDashboardHTML(options)
|
|
534
|
+
.replace('background: #667eea', 'background: #your-color');
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
2. Or mount your own routes:
|
|
538
|
+
```typescript
|
|
539
|
+
import { DashboardService } from 'que-ts/dashboard';
|
|
540
|
+
|
|
541
|
+
const service = new DashboardService(pool);
|
|
542
|
+
|
|
543
|
+
app.get('/custom-dashboard', (req, res) => {
|
|
544
|
+
// Your custom HTML using service.getStats()
|
|
545
|
+
});
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
## Support
|
|
549
|
+
|
|
550
|
+
- 📖 Full API documentation: [API.md](../API.md)
|
|
551
|
+
- 🐛 Report issues: [GitHub Issues](https://github.com/Duke-Engineering/que-ts/issues)
|
|
552
|
+
- 💬 Discussions: [GitHub Discussions](https://github.com/Duke-Engineering/que-ts/discussions)
|
|
553
|
+
|
|
554
|
+
## License
|
|
555
|
+
|
|
556
|
+
MIT
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 [Your Name]
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|