agendash 1.0.0 → 3.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 (72) hide show
  1. package/Dockerfile +3 -2
  2. package/History.md +80 -58
  3. package/LICENSE +1 -1
  4. package/README.md +173 -49
  5. package/all-jobs.png +0 -0
  6. package/app.js +12 -7
  7. package/bin/agendash-standalone-fastify.js +61 -0
  8. package/bin/agendash-standalone-hapi.js +60 -0
  9. package/bin/agendash-standalone-koa.js +56 -0
  10. package/bin/agendash-standalone.js +47 -15
  11. package/create-job.png +0 -0
  12. package/entrypoint.sh +1 -1
  13. package/lib/controllers/agendash.js +340 -0
  14. package/lib/middlewares/README.md +12 -5
  15. package/lib/middlewares/express.js +58 -34
  16. package/lib/middlewares/fastify.js +75 -0
  17. package/lib/middlewares/hapi.js +65 -88
  18. package/lib/middlewares/koa.js +71 -0
  19. package/mobile-ui-sm.png +0 -0
  20. package/mobile-ui-xs.png +0 -0
  21. package/package.json +45 -36
  22. package/public/android-chrome-192x192.png +0 -0
  23. package/public/android-chrome-512x512.png +0 -0
  24. package/public/app/assets/ArgensdashV2Logo.svg +18 -0
  25. package/public/app/css/styles.css +221 -0
  26. package/public/app/js/confirmDelete.js +40 -0
  27. package/public/app/js/confirmDeleteMulti.js +45 -0
  28. package/public/app/js/confirmRequeue.js +40 -0
  29. package/public/app/js/confirmRequeueMulti.js +45 -0
  30. package/public/app/js/confirms.js +8 -0
  31. package/public/app/js/jobdetail.js +48 -0
  32. package/public/app/js/joblist.js +239 -0
  33. package/public/app/js/main.js +275 -0
  34. package/public/app/js/newJob.js +89 -0
  35. package/public/app/js/sidebar.js +121 -0
  36. package/public/app/js/topbar.js +95 -0
  37. package/public/apple-touch-icon.png +0 -0
  38. package/public/favicon-16x16.png +0 -0
  39. package/public/favicon-32x32.png +0 -0
  40. package/public/favicon.ico +0 -0
  41. package/public/index.html +40 -164
  42. package/public/site.webmanifest +1 -0
  43. package/search.png +0 -0
  44. package/.editorconfig +0 -7
  45. package/.travis.yml +0 -24
  46. package/job-details.png +0 -0
  47. package/lib/agendash.js +0 -295
  48. package/public/css/bootstrap-theme.css +0 -587
  49. package/public/css/bootstrap-theme.css.map +0 -1
  50. package/public/css/bootstrap-theme.min.css +0 -6
  51. package/public/css/bootstrap-theme.min.css.map +0 -1
  52. package/public/css/bootstrap.css +0 -6760
  53. package/public/css/bootstrap.css.map +0 -1
  54. package/public/css/bootstrap.min.css +0 -6
  55. package/public/css/bootstrap.min.css.map +0 -1
  56. package/public/css/dashboard.css +0 -94
  57. package/public/fonts/glyphicons-halflings-regular.eot +0 -0
  58. package/public/fonts/glyphicons-halflings-regular.svg +0 -288
  59. package/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  60. package/public/fonts/glyphicons-halflings-regular.woff +0 -0
  61. package/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
  62. package/public/js/backbone-min.js +0 -2
  63. package/public/js/backbone-min.map +0 -1
  64. package/public/js/bootstrap.js +0 -2363
  65. package/public/js/bootstrap.min.js +0 -7
  66. package/public/js/jquery-2.2.0.min.js +0 -4
  67. package/public/js/lodash.min.js +0 -120
  68. package/public/js/main.js +0 -414
  69. package/public/js/moment.min.js +0 -7
  70. package/test/test-express.js +0 -98
  71. package/test/test-hapi.js +0 -104
  72. package/yarn.lock +0 -4412
package/public/index.html CHANGED
@@ -7,173 +7,49 @@
7
7
 
8
8
  <title>Agendash</title>
9
9
 
10
- <link href="css/bootstrap.min.css" rel="stylesheet">
11
- <link href="css/dashboard.css" rel="stylesheet">
10
+ <link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
11
+ <link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
12
+ <link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">
13
+ <link rel="manifest" href="site.webmanifest">
14
+
15
+ <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
16
+ integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
17
+ <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
18
+ integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
19
+ <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
20
+ integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
21
+ <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
22
+ integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
23
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;1,300;1,400;1,500;1,900&display=swap" rel="stylesheet">
24
+ <link href="https://fonts.googleapis.com/css2?family=Days+One&display=swap" rel="stylesheet">
25
+ <link href="https://fonts.googleapis.com/icon?family=Material+Icons"
26
+ rel="stylesheet">
27
+ <link href="https://cdn.jsdelivr.net/npm/prismjs@1.20.0/themes/prism.css" rel="stylesheet" />
28
+ <link rel="stylesheet" href="https://unpkg.com/vue-prism-editor@0.6.1/dist/VuePrismEditor.css">
29
+ <link href="app/css/styles.css" rel="stylesheet">
12
30
  </head>
13
-
14
31
  <body>
15
- <div id="app">
16
- <div id="sidebar">
17
- <div class="sidebar-header">
18
- <h1 class="page-title"></h1>
19
- <div class="form-group">
20
- <label for="overviewFilter">Filter by name</label>
21
- <input type="text" class="overview-filter form-control">
22
- </div>
23
- <div class="form-group">
24
- <label for="overviewFilter">Refresh interval (seconds)</label>
25
- <input type="number" value="2" class="refresh-interval form-control">
26
- </div>
27
- </div>
28
- <div id="job-overview-list"></div>
29
- </div>
30
- <div id="main-pane">
31
- <div id="list-pane">
32
- <div class="page-header">
33
- <h2 id="active-title">
34
- <span class="active-job"></span>
35
- <small class="active-state"></small>
36
- </h2>
37
- <ul id="select-jobs" class="nav nav-pills">
38
- <li role="presentation"><a role="button" data-action="schedule-job">Schedule job</a></li>
39
- <li role="presentation"><a role="button" data-action="select-all">Select all</a></li>
40
- <li role="presentation"><a role="button" data-action="select-none">Select none</a></li>
41
- </ul>
42
- <div class="clearfix"></div>
43
- </div>
44
- <div class="table-responsive">
45
- <table class="table">
46
- <thead>
47
- <tr>
48
- <th>Status</th>
49
- <th>Name</th>
50
- <th>Last run started</th>
51
- <th>Next run starts</th>
52
- <th>Last finished</th>
53
- <th>Locked</th>
54
- </tr>
55
- </thead>
56
- <tbody id="job-list"></tbody>
57
- </table>
58
- </div>
59
- </div>
60
- <div id="details-pane">
61
- <h2 class="sub-header"><span class="number-selected">0</span> <small>selected</small></h2>
62
- <button type="button" class="btn btn-danger btn-xs" data-action="requeue-jobs">Requeue selected</button>
63
- <button type="button" class="btn btn-danger btn-xs pull-right" data-action="delete-jobs">Delete selected</button>
64
- <hr />
65
- <div id="details-list"></div>
66
- </div>
67
- <div id="create-job-pane">
68
- <h2 class="sub-header">Create Job</h2>
69
- <hr />
70
- <div class="form-group">
71
- <label>Job name</label>
72
- <input type="text" class="job-name form-control">
73
- </div>
74
- <div class="form-group">
75
- <label>Schedule</label>
76
- <input type="text" class="job-schedule form-control">
77
- </div>
78
- <div class="form-group">
79
- <label>Repeat every</label>
80
- <input type="text" class="job-repeat-every form-control">
81
- </div>
82
- <div class="form-group">
83
- <label>Job data (json)</label>
84
- <textarea class="job-data form-control">{}</textarea>
85
- </div>
86
- <div class="form-group">
87
- <span class="btn btn-default btn-success" data-action="save">Save</span>
88
- <span class="btn btn-default btn-warning" data-action="cancel">Cancel</span>
89
- </div>
90
- </div>
91
- </div>
32
+ <div id="agendash-root">
33
+ <app></app>
92
34
  </div>
93
-
94
- <script type="text/template" id="overview-item-template">
95
- <ul class="nav nav-sidebar">
96
- <li data-state="">
97
- <a class="text-muted" href="#">
98
- <strong><%- displayName %></strong>
99
- <span class="label label-default pull-right"><%- total %></span>
100
- <div class="progress">
101
- <div class="progress-bar progress-bar-info" style="flex-grow: <%- Math.log2(1 + scheduled) %>"></div>
102
- <div class="progress-bar progress-bar-primary" style="flex-grow: <%- Math.log2(1 + queued) %>"></div>
103
- <div class="progress-bar progress-bar-warning" style="flex-grow: <%- Math.log2(1 + running) %>"></div>
104
- <div class="progress-bar progress-bar-success" style="flex-grow: <%- Math.log2(1 + completed) %>"></div>
105
- <div class="progress-bar progress-bar-danger" style="flex-grow: <%- Math.log2(1 + failed) %>"></div>
106
- </div>
107
- </a>
108
- </li>
109
- <ul class="nav nav-sidebar">
110
- <li data-state="scheduled"><a class="text-info" href="#">Scheduled<span class="label label-info pull-right"><%- scheduled %></span></a></li>
111
- <li data-state="queued"><a class="text-primary" href="#">Queued<span class="label label-primary pull-right"><%- queued %></span></a></li>
112
- <li data-state="running"><a class="text-warning" href="#">Running<span class="label label-warning pull-right"><%- running %></span></a></li>
113
- <li data-state="completed"><a class="text-success" href="#">Completed<span class="label label-success pull-right"><%- completed %></span></a></li>
114
- <li data-state="failed"><a class="text-danger" href="#">Failed<span class="label label-danger pull-right"><%- failed %></span></a></li>
115
- <li data-state="repeating"><a class="text-info" href="#">Repeating<span class="label label-info pull-right"><%- repeating %></span></a></li>
116
- </ul>
117
- </ul>
35
+ <script src="https://cdn.jsdelivr.net/combine/npm/prismjs@1.20.0,npm/vue-prism-editor@0.6.1,npm/prismjs@1.20.0/plugins/autoloader/prism-autoloader.min.js"></script>
36
+ <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
37
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
38
+ <script src="https://cdn.jsdelivr.net/npm/moment@2.24.0/moment.min.js"></script>
39
+ <script src="app/js/confirms.js"></script>
40
+ <script src="app/js/confirmDelete.js"></script>
41
+ <script src="app/js/confirmDeleteMulti.js"></script>
42
+ <script src="app/js/confirmRequeue.js"></script>
43
+ <script src="app/js/confirmRequeueMulti.js"></script>
44
+ <script src="app/js/newJob.js"></script>
45
+ <script src="app/js/jobdetail.js"></script>
46
+ <script src="app/js/joblist.js"></script>
47
+ <script src="app/js/sidebar.js"></script>
48
+ <script src="app/js/topbar.js"></script>
49
+ <script src="app/js/main.js"></script>
50
+ <script>
51
+ Vue.component('prism-editor', VuePrismEditor)
52
+ new Vue({ el: '#agendash-root' })
118
53
  </script>
119
-
120
- <script type="text/template" id="job-item-template">
121
- <td>
122
- <% if (repeating) { %><span class="label label-info"><i class="glyphicon glyphicon-repeat"></i> <%- job.repeatInterval %></span><% } %>
123
- <% if (scheduled) { %><span class="label label-info">Scheduled</span><% } %>
124
- <% if (queued) { %><span class="label label-primary">Queued</span><% } %>
125
- <% if (running) { %><span class="label label-warning">Running</span><% } %>
126
- <% if (completed) { %><span class="label label-success">Completed</span><% } %>
127
- <% if (failed) { %><span class="label label-danger">Failed</span><% } %>
128
- </td>
129
- <td>
130
- <%- job.name %>
131
- </td>
132
- <td><% if (job.lastRunAt) { %><time datetime="<%- moment(job.lastRunAt).toISOString() %>"><%- moment(job.lastRunAt).fromNow() %></time><% } %></td>
133
- <td><% if (job.nextRunAt) { %><time datetime="<%- moment(job.nextRunAt).toISOString() %>"><%- moment(job.nextRunAt).fromNow() %></time><% } %></td>
134
- <td><% if (job.lastFinishedAt) { %><time datetime="<%- moment(job.lastFinishedAt).toISOString() %>"><%- moment(job.lastFinishedAt).fromNow() %></time><% } %></td>
135
- <td><% if (job.lockedAt) { %><time datetime="<%- moment(job.lockedAt).toISOString() %>"><%- moment(job.lockedAt).fromNow() %></time><% } %></td>
136
- </script>
137
-
138
- <script type="text/template" id="job-item-details-template">
139
- <div class="panel panel-default">
140
- <div class="panel-heading">
141
- <button type="button" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button>
142
- <h3 class="panel-title"><%- job.name %></h3>
143
- <% if (repeating) { %><span class="label label-info"><i class="glyphicon glyphicon-repeat"></i> <%- job.repeatInterval %></span><% } %>
144
- <% if (scheduled) { %><span class="label label-info">Scheduled</span><% } %>
145
- <% if (queued) { %><span class="label label-primary">Queued</span><% } %>
146
- <% if (running) { %><span class="label label-warning">Running</span><% } %>
147
- <% if (completed) { %><span class="label label-success">Completed</span><% } %>
148
- <% if (failed) { %><span class="label label-danger">Failed</span><% } %>
149
- </div>
150
- <div class="panel-body">
151
- <% if (job.lastRunAt) { %><p>Last run <time datetime="<%- moment(job.lastRunAt).toISOString() %>"><%- moment(job.lastRunAt).fromNow() %></time></p><% } %>
152
- <% if (job.nextRunAt) { %><p>Next run <time datetime="<%- moment(job.nextRunAt).toISOString() %>"><%- moment(job.nextRunAt).fromNow() %></time></p><% } %>
153
- <% if (job.lastFinishedAt) { %><p>Last finished <time datetime="<%- moment(job.lastFinishedAt).toISOString() %>"><%- moment(job.lastFinishedAt).fromNow() %></time></p><% } %>
154
- <% if (job.lockedAt) { %><p>Locked <time datetime="<%- moment(job.lockedAt).toISOString() %>"><%- moment(job.lockedAt).fromNow() %></time></p><% } %>
155
- <% if (job.repeatInterval) { %><p>Repeat each <%- job.repeatInterval %></p><% } %>
156
-
157
- <strong>Job data</strong>
158
- <pre><%- JSON.stringify(job.data, null, 2) %></pre>
159
- <% if (failed) { %>
160
- <strong>Failure reason</strong>
161
- <pre><%- JSON.stringify(job.failReason || '', null, 2) %></pre>
162
- <% } %>
163
- </div>
164
- <div class="panel-footer clearfix">
165
- <button type="button" class="btn btn-danger btn-sm" data-action="requeue">Requeue</button>
166
- <button type="button" class="btn btn-danger btn-sm pull-right" data-action="delete">Delete permanently</button>
167
- </div>
168
- </div>
169
- </script>
170
-
171
- <script src="js/jquery-2.2.0.min.js"></script>
172
- <script src="js/lodash.min.js"></script>
173
- <script src="js/bootstrap.min.js"></script>
174
- <script src="js/backbone-min.js"></script>
175
- <script src="js/moment.min.js"></script>
176
- <script src="js/main.js"></script>
177
-
178
54
  </body>
179
55
  </html>
@@ -0,0 +1 @@
1
+ {"name":"Agendash","short_name":"Agendash","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
package/search.png ADDED
Binary file
package/.editorconfig DELETED
@@ -1,7 +0,0 @@
1
- [*]
2
- charset=utf-8
3
- end_of_line=lf
4
- trim_trailing_whitespace=true
5
- insert_final_newline=true
6
- indent_style=space
7
- indent_size=2
package/.travis.yml DELETED
@@ -1,24 +0,0 @@
1
- language: node_js
2
- node_js:
3
- - 8
4
- - 9
5
- - 10
6
- matrix:
7
- fast_finish: true
8
- services:
9
- - mongodb
10
- sudo: false
11
- addons:
12
- apt:
13
- sources:
14
- - mongodb-upstart
15
- packages:
16
- - mongodb-org-server
17
- - mongodb-org-shell
18
- before_script:
19
- - mongo --version
20
- - mongod --version
21
- - 'if [ "$AGENDA_VERSION" ]; then yarn add agenda@$AGENDA_VERSION; fi'
22
- - 'until nc -z 127.0.0.1 27017 ; do echo Waiting for MongoDB; sleep 1; done'
23
- env:
24
- - AGENDA_VERSION=^2.0.0
package/job-details.png DELETED
Binary file
package/lib/agendash.js DELETED
@@ -1,295 +0,0 @@
1
- 'use strict';
2
- const async = require('async');
3
- const semver = require('semver');
4
-
5
- module.exports = function(agenda, options) {
6
- options = options || {};
7
-
8
- agenda.on('ready', () => {
9
- const collection = agenda._collection.collection || agenda._collection;
10
- collection.createIndexes([
11
- {key: {nextRunAt: -1, lastRunAt: -1, lastFinishedAt: -1}},
12
- {key: {name: 1, nextRunAt: -1, lastRunAt: -1, lastFinishedAt: -1}}
13
- ], err => {
14
- if (err) {
15
- console.warn('Agendash indexes might not exist. Performance may decrease.');
16
- }
17
- });
18
- agenda._mdb.admin().serverInfo((err, serverInfo) => {
19
- if (err) {
20
- throw err;
21
- }
22
- if (!semver.satisfies(serverInfo.version, '>=2.6.0')) {
23
- console.warn('Agendash requires mongodb version >=2.6.0.');
24
- }
25
- });
26
- });
27
-
28
- const getJobs = (job, state, callback) => {
29
- const preMatch = {};
30
- if (job) {
31
- preMatch.name = job;
32
- }
33
-
34
- const postMatch = {};
35
- if (state) {
36
- postMatch[state] = true;
37
- }
38
-
39
- const limit = 200; // @TODO: UI param
40
- const skip = 0; // @TODO: UI param
41
-
42
- const collection = agenda._collection.collection || agenda._collection;
43
- collection.aggregate([
44
- {$match: preMatch},
45
- {$sort: {
46
- nextRunAt: -1,
47
- lastRunAt: -1,
48
- lastFinishedAt: -1
49
- }},
50
- {$project: {
51
- _id: 0,
52
- job: '$$ROOT',
53
- nextRunAt: {$ifNull: ['$nextRunAt', 0]},
54
- lockedAt: {$ifNull: ['$lockedAt', 0]},
55
- lastRunAt: {$ifNull: ['$lastRunAt', 0]},
56
- lastFinishedAt: {$ifNull: ['$lastFinishedAt', 0]},
57
- failedAt: {$ifNull: ['$failedAt', 0]},
58
- repeatInterval: {$ifNull: ['$repeatInterval', 0]}
59
- }},
60
- {$project: {
61
- job: '$job',
62
- _id: '$job._id',
63
- running: {$and: [
64
- '$lastRunAt',
65
- {$gt: ['$lastRunAt', '$lastFinishedAt']}
66
- ]},
67
- scheduled: {$and: [
68
- '$nextRunAt',
69
- {$gte: ['$nextRunAt', new Date()]}
70
- ]},
71
- queued: {$and: [
72
- '$nextRunAt',
73
- {$gte: [new Date(), '$nextRunAt']},
74
- {$gte: ['$nextRunAt', '$lastFinishedAt']}
75
- ]},
76
- completed: {$and: [
77
- '$lastFinishedAt',
78
- {$gt: ['$lastFinishedAt', '$failedAt']}
79
- ]},
80
- failed: {$and: [
81
- '$lastFinishedAt',
82
- '$failedAt',
83
- {$eq: ['$lastFinishedAt', '$failedAt']}
84
- ]},
85
- repeating: {$and: [
86
- '$repeatInterval',
87
- {$ne: ['$repeatInterval', null]}
88
- ]}
89
- }},
90
- {$match: postMatch},
91
- {$limit: limit},
92
- {$skip: skip}
93
- ]).toArray((err, results) => {
94
- if (err) {
95
- return callback(err);
96
- }
97
- callback(null, results);
98
- });
99
- };
100
-
101
- const getOverview = callback => {
102
- const collection = agenda._collection.collection || agenda._collection;
103
- collection.aggregate([
104
- {$project: {
105
- _id: 0,
106
- name: '$name',
107
- type: '$type',
108
- priority: '$priority',
109
- repeatInterval: '$repeatInterval',
110
- repeatTimezone: '$repeatTimezone',
111
- nextRunAt: {$ifNull: ['$nextRunAt', 0]},
112
- lockedAt: {$ifNull: ['$lockedAt', 0]},
113
- lastRunAt: {$ifNull: ['$lastRunAt', 0]},
114
- lastFinishedAt: {$ifNull: ['$lastFinishedAt', 0]},
115
- failedAt: {$ifNull: ['$failedAt', 0]}
116
- }},
117
- {$project: {
118
- name: '$name',
119
- type: '$type',
120
- priority: '$priority',
121
- repeatInterval: '$repeatInterval',
122
- repeatTimezone: '$repeatTimezone',
123
- running: {$cond: [{$and: [
124
- '$lastRunAt',
125
- {$gt: ['$lastRunAt', '$lastFinishedAt']}
126
- ]}, 1, 0]},
127
- scheduled: {$cond: [{$and: [
128
- '$nextRunAt',
129
- {$gte: ['$nextRunAt', new Date()]}
130
- ]}, 1, 0]},
131
- queued: {$cond: [{$and: [
132
- '$nextRunAt',
133
- {$gte: [new Date(), '$nextRunAt']},
134
- {$gte: ['$nextRunAt', '$lastFinishedAt']}
135
- ]}, 1, 0]},
136
- completed: {$cond: [{$and: [
137
- '$lastFinishedAt',
138
- {$gt: ['$lastFinishedAt', '$failedAt']}
139
- ]}, 1, 0]},
140
- failed: {$cond: [{$and: [
141
- '$lastFinishedAt',
142
- '$failedAt',
143
- {$eq: ['$lastFinishedAt', '$failedAt']}
144
- ]}, 1, 0]},
145
- repeating: {$cond: [{$and: [
146
- '$repeatInterval',
147
- {$ne: ['$repeatInterval', null]}
148
- ]}, 1, 0]}
149
- }},
150
- {$group: {
151
- _id: '$name',
152
- displayName: {$first: '$name'},
153
- meta: {$addToSet: {
154
- type: '$type',
155
- priority: '$priority',
156
- repeatInterval: '$repeatInterval',
157
- repeatTimezone: '$repeatTimezone'
158
- }},
159
- total: {$sum: 1},
160
- running: {$sum: '$running'},
161
- scheduled: {$sum: '$scheduled'},
162
- queued: {$sum: '$queued'},
163
- completed: {$sum: '$completed'},
164
- failed: {$sum: '$failed'},
165
- repeating: {$sum: '$repeating'}
166
- }}
167
- ]).toArray((err, results) => {
168
- if (err) {
169
- return callback(err);
170
- }
171
- const totals = {displayName: 'All Jobs'};
172
- const states = ['total', 'running', 'scheduled', 'queued', 'completed', 'failed', 'repeating'];
173
- states.forEach(state => {
174
- totals[state] = 0;
175
- });
176
- results.forEach(job => {
177
- states.forEach(state => {
178
- totals[state] += job[state];
179
- });
180
- });
181
- results.unshift(totals);
182
- callback(null, results);
183
- });
184
- };
185
-
186
- const api = function(job, state, callback) {
187
- if (!agenda) {
188
- return callback('Agenda instance is not ready');
189
- }
190
- async.parallel({
191
- overview: getOverview,
192
- jobs: getJobs.bind(this, job, state)
193
- },
194
- (err, apiResponse) => {
195
- if (err) {
196
- return callback(err.message);
197
- }
198
- apiResponse.title = options.title || 'Agendash';
199
- apiResponse.currentRequest = {
200
- title: options.title || 'Agendash',
201
- job: job || 'All Jobs',
202
- state
203
- };
204
- callback(null, apiResponse);
205
- });
206
- };
207
-
208
- const requeueJobs = (jobIds, callback) => {
209
- if (!agenda) {
210
- return callback('Agenda instance is not ready');
211
- }
212
- try {
213
- const collection = agenda._collection.collection || agenda._collection;
214
- collection
215
- .find({_id: {$in: jobIds.map(jobId => collection.s.pkFactory(jobId))}})
216
- .toArray((err, jobs) => {
217
- if (err || jobs.length === 0) {
218
- return callback('Jobs not found');
219
- }
220
- async.series(jobs.map(job => done => {
221
- const newJob = agenda.create(job.name, job.data);
222
- newJob.save()
223
- .then(() => {
224
- done(null, newJob);
225
- })
226
- .catch(err => {
227
- done(err);
228
- });
229
- }), (err, results) => {
230
- callback(err, results);
231
- });
232
- });
233
- } catch (err) {
234
- callback(err.message);
235
- }
236
- };
237
-
238
- const deleteJobs = (jobIds, callback) => {
239
- if (!agenda) {
240
- return callback('Agenda instance is not ready');
241
- }
242
- try {
243
- const collection = agenda._collection.collection || agenda._collection;
244
- agenda.cancel({_id: {$in: jobIds.map(jobId => collection.s.pkFactory(jobId))}})
245
- .then(deleted => {
246
- if (deleted) {
247
- callback();
248
- } else {
249
- callback('Jobs not deleted');
250
- }
251
- })
252
- .catch(err => {
253
- callback(err);
254
- });
255
- } catch (err) {
256
- callback(err.message);
257
- }
258
- };
259
-
260
- const createJob = (jobName, jobSchedule, jobRepeatEvery, jobData, callback) => {
261
- if (!agenda) {
262
- return callback('Agenda instance is not ready');
263
- }
264
- try {
265
- // @TODO: Need to validate user input.
266
- const job = agenda.create(jobName, jobData);
267
- if (jobSchedule && jobRepeatEvery) {
268
- job.repeatAt(jobSchedule);
269
- job.repeatEvery(jobRepeatEvery);
270
- } else if (jobSchedule) {
271
- job.schedule(jobSchedule);
272
- } else if (jobRepeatEvery) {
273
- job.repeatEvery(jobRepeatEvery);
274
- } else {
275
- return callback('Jobs not created');
276
- }
277
- job.save()
278
- .then(() => {
279
- callback();
280
- })
281
- .catch(() => {
282
- return callback('Jobs not created');
283
- });
284
- } catch (err) {
285
- callback(err.message);
286
- }
287
- };
288
-
289
- return {
290
- api,
291
- requeueJobs,
292
- deleteJobs,
293
- createJob
294
- };
295
- };