agendash 2.0.0 → 3.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.
Files changed (39) hide show
  1. package/README.md +59 -22
  2. package/app.js +9 -5
  3. package/bin/agendash-standalone-fastify.js +61 -0
  4. package/bin/agendash-standalone-hapi.js +36 -18
  5. package/bin/agendash-standalone-koa.js +30 -14
  6. package/bin/agendash-standalone.js +44 -19
  7. package/lib/controllers/agendash.js +228 -110
  8. package/lib/csp.js +28 -0
  9. package/lib/middlewares/README.md +3 -1
  10. package/lib/middlewares/express.js +43 -17
  11. package/lib/middlewares/fastify.js +77 -0
  12. package/lib/middlewares/hapi.js +58 -33
  13. package/lib/middlewares/koa.js +29 -20
  14. package/package.json +37 -35
  15. package/public/app/js/confirmDelete.js +14 -13
  16. package/public/app/js/confirmDeleteMulti.js +14 -13
  17. package/public/app/js/confirmRequeue.js +14 -13
  18. package/public/app/js/confirmRequeueMulti.js +15 -14
  19. package/public/app/js/confirms.js +4 -4
  20. package/public/app/js/jobdetail.js +9 -9
  21. package/public/app/js/joblist.js +50 -43
  22. package/public/app/js/main.js +109 -72
  23. package/public/app/js/newJob.js +37 -23
  24. package/public/app/js/sidebar.js +51 -26
  25. package/public/app/js/topbar.js +15 -5
  26. package/.editorconfig +0 -7
  27. package/.idea/agendash.iml +0 -12
  28. package/.idea/inspectionProfiles/Project_Default.xml +0 -6
  29. package/.idea/jsLibraryMappings.xml +0 -6
  30. package/.idea/misc.xml +0 -92
  31. package/.idea/modules.xml +0 -8
  32. package/.idea/vcs.xml +0 -6
  33. package/.travis.yml +0 -27
  34. package/.vscode/launch.json +0 -15
  35. package/History.md +0 -106
  36. package/renovate.json +0 -5
  37. package/test/test-express.js +0 -82
  38. package/test/test-hapi.js +0 -106
  39. package/test/test-koa.js +0 -85
@@ -1,20 +1,23 @@
1
- 'use strict';
2
- const semver = require('semver');
3
- const {ObjectID} = require('mongodb'); // We rely on the Agenda's "mongodb", thus our package.json lists "*" as required version
1
+ "use strict";
2
+ const semver = require("semver");
3
+ const { ObjectId } = require("mongodb"); // We rely on the Agenda's "mongodb", thus our package.json lists "*" as required version
4
4
 
5
- module.exports = function(agenda, options) {
5
+ module.exports = function (agenda, options) {
6
6
  options = options || {};
7
7
 
8
- agenda.on('ready', () => {
8
+ agenda.on("ready", () => {
9
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
- ], error => {
14
- if (error) {
15
- // Ignoring for now
10
+ collection.createIndexes(
11
+ [
12
+ { key: { nextRunAt: -1, lastRunAt: -1, lastFinishedAt: -1 } },
13
+ { key: { name: 1, nextRunAt: -1, lastRunAt: -1, lastFinishedAt: -1 } },
14
+ ],
15
+ (error) => {
16
+ if (error) {
17
+ // Ignoring for now
18
+ }
16
19
  }
17
- });
20
+ );
18
21
 
19
22
  // Mongoose internals changed at some point. This will fix crash for older versions.
20
23
  const mdb = agenda._mdb.admin ? agenda._mdb : agenda._mdb.db;
@@ -24,14 +27,14 @@ module.exports = function(agenda, options) {
24
27
  throw error;
25
28
  }
26
29
 
27
- if (!semver.satisfies(serverInfo.version, '>=2.6.0')) {
28
- throw new Error('MongoDB version not supported');
30
+ if (!semver.satisfies(semver.coerce(serverInfo.version), ">=3.6.0")) {
31
+ throw new Error("MongoDB version not supported");
29
32
  }
30
33
  });
31
34
  });
32
35
 
33
36
  // Options = {query = '', property = '', isObjectId = false, limit, skip}
34
- const getJobs = function(job, state, options) {
37
+ const getJobs = function (job, state, options) {
35
38
  const preMatch = {};
36
39
  if (job) {
37
40
  preMatch.name = job;
@@ -39,11 +42,11 @@ module.exports = function(agenda, options) {
39
42
 
40
43
  if (options.query && options.property) {
41
44
  if (options.isObjectId) {
42
- preMatch[options.property] = ObjectID.createFromHexString(options.query);
45
+ preMatch[options.property] = ObjectId(options.query);
43
46
  } else if (/^\d+$/.test(options.query)) {
44
47
  preMatch[options.property] = Number.parseInt(options.query, 10);
45
48
  } else {
46
- preMatch[options.property] = {$regex: options.query, $options: 'i'};
49
+ preMatch[options.property] = { $regex: options.query, $options: "i" };
47
50
  }
48
51
  }
49
52
 
@@ -53,82 +56,185 @@ module.exports = function(agenda, options) {
53
56
  }
54
57
 
55
58
  const collection = agenda._collection.collection || agenda._collection;
56
- return collection.aggregate([
57
- {$match: preMatch},
58
- {$sort: {
59
- nextRunAt: -1,
60
- lastRunAt: -1,
61
- lastFinishedAt: -1
62
- }},
63
- {$project: {
64
- job: '$$ROOT',
65
- _id: '$$ROOT._id',
66
- running: {$and: [
67
- '$lastRunAt',
68
- {$gt: ['$lastRunAt', '$lastFinishedAt']}
69
- ]},
70
- scheduled: {$and: [
71
- '$nextRunAt',
72
- {$gte: ['$nextRunAt', new Date()]}
73
- ]},
74
- queued: {$and: [
75
- '$nextRunAt',
76
- {$gte: [new Date(), '$nextRunAt']},
77
- {$gte: ['$nextRunAt', '$lastFinishedAt']}
78
- ]},
79
- completed: {$and: [
80
- '$lastFinishedAt',
81
- {$gt: ['$lastFinishedAt', '$failedAt']}
82
- ]},
83
- failed: {$and: [
84
- '$lastFinishedAt',
85
- '$failedAt',
86
- {$eq: ['$lastFinishedAt', '$failedAt']}
87
- ]},
88
- repeating: {$and: [
89
- '$repeatInterval',
90
- {$ne: ['$repeatInterval', null]}
91
- ]}
92
- }},
93
- {$match: postMatch},
94
- {$facet: {
95
- pages: [
96
- {$count: 'totalMatchs'},
97
- {$project: {
98
- totalPages: {$ceil: {$divide: ['$totalMatchs', options.limit]}}
99
- }}
100
- ],
101
- filtered: [
102
- {$skip: options.skip},
103
- {$limit: options.limit}
104
- ]
105
- }}
106
- ]).toArray();
59
+ return collection
60
+ .aggregate([
61
+ { $match: preMatch },
62
+ {
63
+ $sort: {
64
+ nextRunAt: -1,
65
+ lastRunAt: -1,
66
+ lastFinishedAt: -1,
67
+ },
68
+ },
69
+ {
70
+ $project: {
71
+ job: "$$ROOT",
72
+ _id: "$$ROOT._id",
73
+ running: {
74
+ $and: ["$lastRunAt", { $gt: ["$lastRunAt", "$lastFinishedAt"] }],
75
+ },
76
+ scheduled: {
77
+ $and: ["$nextRunAt", { $gte: ["$nextRunAt", new Date()] }],
78
+ },
79
+ queued: {
80
+ $and: [
81
+ "$nextRunAt",
82
+ { $gte: [new Date(), "$nextRunAt"] },
83
+ { $gte: ["$nextRunAt", "$lastFinishedAt"] },
84
+ ],
85
+ },
86
+ completed: {
87
+ $and: [
88
+ "$lastFinishedAt",
89
+ { $gt: ["$lastFinishedAt", "$failedAt"] },
90
+ ],
91
+ },
92
+ failed: {
93
+ $and: [
94
+ "$lastFinishedAt",
95
+ "$failedAt",
96
+ { $eq: ["$lastFinishedAt", "$failedAt"] },
97
+ ],
98
+ },
99
+ repeating: {
100
+ $and: ["$repeatInterval", { $ne: ["$repeatInterval", null] }],
101
+ },
102
+ },
103
+ },
104
+ { $match: postMatch },
105
+ {
106
+ $facet: {
107
+ pages: [
108
+ { $count: "totalMatchs" },
109
+ {
110
+ $project: {
111
+ totalPages: {
112
+ $ceil: { $divide: ["$totalMatchs", options.limit] },
113
+ },
114
+ },
115
+ },
116
+ ],
117
+ filtered: [{ $skip: options.skip }, { $limit: options.limit }],
118
+ },
119
+ },
120
+ ])
121
+ .toArray();
107
122
  };
108
123
 
109
- const getOverview = async() => {
124
+ const getOverview = async () => {
110
125
  const collection = agenda._collection.collection || agenda._collection;
111
- const results = await collection.aggregate([
112
- {$group: {
113
- _id: '$name',
114
- displayName: {$first: '$name'},
115
- meta: {$addToSet: {
116
- type: '$type',
117
- priority: '$priority',
118
- repeatInterval: '$repeatInterval',
119
- repeatTimezone: '$repeatTimezone'
120
- }},
121
- total: {$sum: 1},
122
- running: {$sum: {$cond: [{$and: ['$lastRunAt', {$gt: ['$lastRunAt', '$lastFinishedAt']}]}, 1, 0]}},
123
- scheduled: {$sum: {$cond: [{$and: ['$nextRunAt', {$gte: ['$nextRunAt', new Date()]}]}, 1, 0]}},
124
- queued: {$sum: {$cond: [{$and: ['$nextRunAt', {$gte: [new Date(), '$nextRunAt']}, {$gte: ['$nextRunAt', '$lastFinishedAt']}]}, 1, 0]}},
125
- completed: {$sum: {$cond: [{$and: ['$lastFinishedAt', {$gt: ['$lastFinishedAt', '$failedAt']}]}, 1, 0]}},
126
- failed: {$sum: {$cond: [{$and: ['$lastFinishedAt', '$failedAt', {$eq: ['$lastFinishedAt', '$failedAt']}]}, 1, 0]}},
127
- repeating: {$sum: {$cond: [{$and: ['$repeatInterval', {$ne: ['$repeatInterval', null]}]}, 1, 0]}}
128
- }}
129
- ]).toArray();
130
- const states = {total: 0, running: 0, scheduled: 0, queued: 0, completed: 0, failed: 0, repeating: 0};
131
- const totals = {displayName: 'All Jobs', ...states};
126
+ const results = await collection
127
+ .aggregate([
128
+ {
129
+ $group: {
130
+ _id: "$name",
131
+ displayName: { $first: "$name" },
132
+ meta: {
133
+ $addToSet: {
134
+ type: "$type",
135
+ priority: "$priority",
136
+ repeatInterval: "$repeatInterval",
137
+ repeatTimezone: "$repeatTimezone",
138
+ },
139
+ },
140
+ total: { $sum: 1 },
141
+ running: {
142
+ $sum: {
143
+ $cond: [
144
+ {
145
+ $and: [
146
+ "$lastRunAt",
147
+ { $gt: ["$lastRunAt", "$lastFinishedAt"] },
148
+ ],
149
+ },
150
+ 1,
151
+ 0,
152
+ ],
153
+ },
154
+ },
155
+ scheduled: {
156
+ $sum: {
157
+ $cond: [
158
+ {
159
+ $and: ["$nextRunAt", { $gte: ["$nextRunAt", new Date()] }],
160
+ },
161
+ 1,
162
+ 0,
163
+ ],
164
+ },
165
+ },
166
+ queued: {
167
+ $sum: {
168
+ $cond: [
169
+ {
170
+ $and: [
171
+ "$nextRunAt",
172
+ { $gte: [new Date(), "$nextRunAt"] },
173
+ { $gte: ["$nextRunAt", "$lastFinishedAt"] },
174
+ ],
175
+ },
176
+ 1,
177
+ 0,
178
+ ],
179
+ },
180
+ },
181
+ completed: {
182
+ $sum: {
183
+ $cond: [
184
+ {
185
+ $and: [
186
+ "$lastFinishedAt",
187
+ { $gt: ["$lastFinishedAt", "$failedAt"] },
188
+ ],
189
+ },
190
+ 1,
191
+ 0,
192
+ ],
193
+ },
194
+ },
195
+ failed: {
196
+ $sum: {
197
+ $cond: [
198
+ {
199
+ $and: [
200
+ "$lastFinishedAt",
201
+ "$failedAt",
202
+ { $eq: ["$lastFinishedAt", "$failedAt"] },
203
+ ],
204
+ },
205
+ 1,
206
+ 0,
207
+ ],
208
+ },
209
+ },
210
+ repeating: {
211
+ $sum: {
212
+ $cond: [
213
+ {
214
+ $and: [
215
+ "$repeatInterval",
216
+ { $ne: ["$repeatInterval", null] },
217
+ ],
218
+ },
219
+ 1,
220
+ 0,
221
+ ],
222
+ },
223
+ },
224
+ },
225
+ },
226
+ ])
227
+ .toArray();
228
+ const states = {
229
+ total: 0,
230
+ running: 0,
231
+ scheduled: 0,
232
+ queued: 0,
233
+ completed: 0,
234
+ failed: 0,
235
+ repeating: 0,
236
+ };
237
+ const totals = { displayName: "All Jobs", ...states };
132
238
 
133
239
  for (const job of results) {
134
240
  for (const state of Object.keys(states)) {
@@ -140,38 +246,49 @@ module.exports = function(agenda, options) {
140
246
  return results;
141
247
  };
142
248
 
143
- const api = async function(job, state, {query: q, property, isObjectId, skip, limit}) {
249
+ const api = async function (
250
+ job,
251
+ state,
252
+ { query: q, property, isObjectId, skip, limit }
253
+ ) {
144
254
  if (!agenda) {
145
- return Promise.reject(new Error('Agenda instance is not ready'));
255
+ return Promise.reject(new Error("Agenda instance is not ready"));
146
256
  }
147
257
 
148
258
  limit = Number.parseInt(limit, 10) || 200;
149
259
  skip = Number.parseInt(skip, 10) || 0;
150
260
 
151
- const [overview, jobs] = await Promise.all([getOverview(), getJobs(job, state, {query: q, property, isObjectId, skip, limit})]);
261
+ const [overview, jobs] = await Promise.all([
262
+ getOverview(),
263
+ getJobs(job, state, { query: q, property, isObjectId, skip, limit }),
264
+ ]);
152
265
  const apiResponse = {
153
266
  overview,
154
267
  jobs: jobs[0].filtered,
155
- totalPages: jobs[0].pages[0] ? jobs[0].pages[0].totalPages : 0
268
+ totalPages: jobs[0].pages[0] ? jobs[0].pages[0].totalPages : 0,
156
269
  };
157
- apiResponse.title = options.title || 'Agendash';
270
+ apiResponse.title = options.title || "Agendash";
158
271
  apiResponse.currentRequest = {
159
- title: options.title || 'Agendash',
160
- job: job || 'All Jobs',
161
- state
272
+ title: options.title || "Agendash",
273
+ job: job || "All Jobs",
274
+ state,
162
275
  };
163
276
  return apiResponse;
164
277
  };
165
278
 
166
- const requeueJobs = async jobIds => {
279
+ const requeueJobs = async (jobIds) => {
167
280
  if (!agenda) {
168
- return Promise.reject(new Error('Agenda instance is not ready'));
281
+ return Promise.reject(new Error("Agenda instance is not ready"));
169
282
  }
170
283
 
171
284
  const collection = agenda._collection.collection || agenda._collection;
172
- const jobs = await collection.find({_id: {$in: jobIds.map(jobId => collection.s.pkFactory(jobId))}}).toArray();
285
+ const jobs = await collection
286
+ .find({
287
+ _id: { $in: jobIds.map((jobId) => ObjectId(jobId)) },
288
+ })
289
+ .toArray();
173
290
  if (jobs.length === 0) {
174
- throw new Error('Job not found');
291
+ throw new Error("Job not found");
175
292
  }
176
293
 
177
294
  for (const job of jobs) {
@@ -180,21 +297,22 @@ module.exports = function(agenda, options) {
180
297
  await newJob.save();
181
298
  }
182
299
 
183
- return 'Jobs create successfully';
300
+ return "Jobs create successfully";
184
301
  };
185
302
 
186
- const deleteJobs = jobIds => {
303
+ const deleteJobs = (jobIds) => {
187
304
  if (!agenda) {
188
- return Promise.reject(new Error('Agenda instance is not ready'));
305
+ return Promise.reject(new Error("Agenda instance is not ready"));
189
306
  }
190
307
 
191
- const collection = agenda._collection.collection || agenda._collection;
192
- return agenda.cancel({_id: {$in: jobIds.map(jobId => collection.s.pkFactory(jobId))}});
308
+ return agenda.cancel({
309
+ _id: { $in: jobIds.map((jobId) => ObjectId(jobId)) },
310
+ });
193
311
  };
194
312
 
195
313
  const createJob = (jobName, jobSchedule, jobRepeatEvery, jobData) => {
196
314
  if (!agenda) {
197
- return Promise.reject(new Error('Agenda instance is not ready'));
315
+ return Promise.reject(new Error("Agenda instance is not ready"));
198
316
  }
199
317
 
200
318
  // @TODO: Need to validate user input.
@@ -207,7 +325,7 @@ module.exports = function(agenda, options) {
207
325
  } else if (jobRepeatEvery) {
208
326
  job.repeatEvery(jobRepeatEvery);
209
327
  } else {
210
- return Promise.reject(new Error('Jobs not created'));
328
+ return Promise.reject(new Error("Jobs not created"));
211
329
  }
212
330
 
213
331
  return job.save();
@@ -217,6 +335,6 @@ module.exports = function(agenda, options) {
217
335
  api,
218
336
  requeueJobs,
219
337
  deleteJobs,
220
- createJob
338
+ createJob,
221
339
  };
222
340
  };
package/lib/csp.js ADDED
@@ -0,0 +1,28 @@
1
+ const CSP = {
2
+ "default-src": [
3
+ "'self'",
4
+ ],
5
+ "script-src": [
6
+ "https://code.jquery.com",
7
+ "https://cdn.jsdelivr.net",
8
+ "https://cdnjs.cloudflare.com",
9
+ "https://stackpath.bootstrapcdn.com",
10
+ "'unsafe-inline'",
11
+ "'unsafe-eval'",
12
+ "'self'",
13
+ ],
14
+ "style-src": [
15
+ "https://cdn.jsdelivr.net",
16
+ "https://stackpath.bootstrapcdn.com",
17
+ "https://fonts.googleapis.com",
18
+ "https://unpkg.com",
19
+ "'self'",
20
+ ],
21
+ "font-src": [
22
+ "https://fonts.gstatic.com",
23
+ ],
24
+ };
25
+
26
+ module.exports = Object.entries(CSP)
27
+ .map(([type, values]) => `${type} ${values.join(" ")}`)
28
+ .join("; ")
@@ -5,6 +5,7 @@
5
5
  Works with [Hapijs](https://hapijs.com/) >= v18
6
6
 
7
7
  ### usage
8
+
8
9
  First install the dependencies in your app:
9
10
 
10
11
  ```bash
@@ -12,11 +13,12 @@ npm install @hapi/hapi @hapi/inert agenda agendash
12
13
  ```
13
14
 
14
15
  Then use it as follows:
16
+
15
17
  ```javascript
16
18
  'use strict';
17
19
 
18
20
  const Hapi = require('hapi');
19
- const Agenda = require('agenda');
21
+ const { Agenda } = require('agenda');
20
22
  const Agendash = require('agendash');
21
23
 
22
24
  var agenda = new Agenda({
@@ -1,25 +1,46 @@
1
- const path = require('path');
2
- const express = require('express');
3
- const bodyParser = require('body-parser');
1
+ const path = require("path");
2
+ const express = require("express");
3
+ const bodyParser = require("body-parser");
4
+ const csp = require("../csp");
4
5
 
5
- module.exports = agendash => {
6
- const {api, requeueJobs, deleteJobs, createJob} = agendash;
6
+ module.exports = (agendash) => {
7
+ const { api, requeueJobs, deleteJobs, createJob } = agendash;
7
8
  const app = express();
8
9
  app.use(bodyParser.json());
9
- app.use(bodyParser.urlencoded({extended: false}));
10
- app.use('/', express.static(path.join(__dirname, '../../public')));
10
+ app.use(bodyParser.urlencoded({ extended: false }));
11
11
 
12
- app.get('/api', async(request, response) => {
12
+ app.use((req, res, next) => {
13
+ res.header("Content-Security-Policy", csp);
14
+ next();
15
+ });
16
+
17
+ app.use("/", express.static(path.join(__dirname, "../../public")));
18
+
19
+ app.get("/api", async (request, response) => {
13
20
  try {
14
- const {job, state, skip, limit, q, property, isObjectId} = request.query;
15
- const apiResponse = await api(job, state, {query: q, property, isObjectId, skip, limit});
21
+ const {
22
+ job,
23
+ state,
24
+ skip,
25
+ limit,
26
+ q,
27
+ property,
28
+ isObjectId,
29
+ } = request.query;
30
+ const apiResponse = await api(job, state, {
31
+ query: q,
32
+ property,
33
+ isObjectId,
34
+ skip,
35
+ limit,
36
+ });
16
37
  response.json(apiResponse);
17
38
  } catch (error) {
18
39
  response.status(400).json(error);
19
40
  }
20
41
  });
21
42
 
22
- app.post('/api/jobs/requeue', async(request, response) => {
43
+ app.post("/api/jobs/requeue", async (request, response) => {
23
44
  try {
24
45
  const newJobs = await requeueJobs(request.body.jobIds);
25
46
  response.send(newJobs);
@@ -28,23 +49,28 @@ module.exports = agendash => {
28
49
  }
29
50
  });
30
51
 
31
- app.post('/api/jobs/delete', async(request, response) => {
52
+ app.post("/api/jobs/delete", async (request, response) => {
32
53
  try {
33
54
  const deleted = await deleteJobs(request.body.jobIds);
34
55
  if (deleted) {
35
- response.json({deleted: true});
56
+ response.json({ deleted: true });
36
57
  } else {
37
- response.json({message: 'Jobs not deleted'});
58
+ response.json({ message: "Jobs not deleted" });
38
59
  }
39
60
  } catch (error) {
40
61
  response.status(404).json(error);
41
62
  }
42
63
  });
43
64
 
44
- app.post('/api/jobs/create', async(request, response) => {
65
+ app.post("/api/jobs/create", async (request, response) => {
45
66
  try {
46
- await createJob(request.body.jobName, request.body.jobSchedule, request.body.jobRepeatEvery, request.body.jobData);
47
- response.json({created: true});
67
+ await createJob(
68
+ request.body.jobName,
69
+ request.body.jobSchedule,
70
+ request.body.jobRepeatEvery,
71
+ request.body.jobData
72
+ );
73
+ response.json({ created: true });
48
74
  } catch (error) {
49
75
  response.status(400).json(error);
50
76
  }
@@ -0,0 +1,77 @@
1
+ const path = require("path");
2
+ const csp = require("../csp");
3
+
4
+ module.exports = (agendash) => (instance, opts, done) => {
5
+ const { api, requeueJobs, deleteJobs, createJob } = agendash;
6
+
7
+ instance.register(require("fastify-static"), {
8
+ root: path.join(__dirname, "../../public"),
9
+ });
10
+
11
+ instance.get("/", function (req, reply) {
12
+ reply.header("Content-Security-Policy", csp);
13
+ return reply.sendFile("index.html");
14
+ });
15
+
16
+ instance.get("/api", async (request, response) => {
17
+ try {
18
+ const {
19
+ job,
20
+ state,
21
+ skip,
22
+ limit,
23
+ q,
24
+ property,
25
+ isObjectId,
26
+ } = request.query;
27
+ const apiResponse = await api(job, state, {
28
+ query: q,
29
+ property,
30
+ isObjectId,
31
+ skip,
32
+ limit,
33
+ });
34
+ response.send(apiResponse);
35
+ } catch (error) {
36
+ response.status(400).send(error);
37
+ }
38
+ });
39
+
40
+ instance.post("/api/jobs/requeue", async (request, response) => {
41
+ try {
42
+ const newJobs = await requeueJobs(request.body.jobIds);
43
+ response.send(newJobs);
44
+ } catch (error) {
45
+ response.status(404).send(error);
46
+ }
47
+ });
48
+
49
+ instance.post("/api/jobs/delete", async (request, response) => {
50
+ try {
51
+ const deleted = await deleteJobs(request.body.jobIds);
52
+ if (deleted) {
53
+ response.send({ deleted: true });
54
+ } else {
55
+ response.send({ message: "Jobs not deleted" });
56
+ }
57
+ } catch (error) {
58
+ response.status(404).send(error);
59
+ }
60
+ });
61
+
62
+ instance.post("/api/jobs/create", async (request, response) => {
63
+ try {
64
+ await createJob(
65
+ request.body.jobName,
66
+ request.body.jobSchedule,
67
+ request.body.jobRepeatEvery,
68
+ request.body.jobData
69
+ );
70
+ response.send({ created: true });
71
+ } catch (error) {
72
+ response.status(400).send(error);
73
+ }
74
+ });
75
+
76
+ done();
77
+ };