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.
Files changed (48) hide show
  1. package/DASHBOARD-QUICKSTART.md +278 -0
  2. package/DASHBOARD.md +556 -0
  3. package/LICENSE +21 -0
  4. package/README.md +414 -0
  5. package/SSL-QUICK-REFERENCE.md +225 -0
  6. package/SSL.md +516 -0
  7. package/dist/client.d.ts +11 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +64 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/dashboard/index.d.ts +34 -0
  12. package/dist/dashboard/index.d.ts.map +1 -0
  13. package/dist/dashboard/index.js +164 -0
  14. package/dist/dashboard/index.js.map +1 -0
  15. package/dist/dashboard/service.d.ts +66 -0
  16. package/dist/dashboard/service.d.ts.map +1 -0
  17. package/dist/dashboard/service.js +201 -0
  18. package/dist/dashboard/service.js.map +1 -0
  19. package/dist/dashboard/views.d.ts +3 -0
  20. package/dist/dashboard/views.d.ts.map +1 -0
  21. package/dist/dashboard/views.js +786 -0
  22. package/dist/dashboard/views.js.map +1 -0
  23. package/dist/index.d.ts +6 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +29 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/job.d.ts +19 -0
  28. package/dist/job.d.ts.map +1 -0
  29. package/dist/job.js +36 -0
  30. package/dist/job.js.map +1 -0
  31. package/dist/sql.d.ts +8 -0
  32. package/dist/sql.d.ts.map +1 -0
  33. package/dist/sql.js +57 -0
  34. package/dist/sql.js.map +1 -0
  35. package/dist/types.d.ts +90 -0
  36. package/dist/types.d.ts.map +1 -0
  37. package/dist/types.js +3 -0
  38. package/dist/types.js.map +1 -0
  39. package/dist/utils.d.ts +6 -0
  40. package/dist/utils.d.ts.map +1 -0
  41. package/dist/utils.js +31 -0
  42. package/dist/utils.js.map +1 -0
  43. package/dist/worker.d.ts +19 -0
  44. package/dist/worker.d.ts.map +1 -0
  45. package/dist/worker.js +99 -0
  46. package/dist/worker.js.map +1 -0
  47. package/migrations/schema.sql +26 -0
  48. 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
+ ![Dashboard Overview](docs/images/dashboard-overview.png)
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
+ ![Jobs List](docs/images/jobs-list.png)
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
+ ![Recent Failures](docs/images/failures.png)
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.