agendash 4.0.0 → 6.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 (71) hide show
  1. package/LICENSE.md +25 -0
  2. package/README.md +109 -200
  3. package/dist/AgendashController.d.ts +50 -0
  4. package/dist/AgendashController.js +176 -0
  5. package/dist/AgendashController.js.map +1 -0
  6. package/dist/csp.d.ts +1 -0
  7. package/dist/csp.js +15 -0
  8. package/dist/csp.js.map +1 -0
  9. package/dist/index.d.ts +12 -0
  10. package/dist/index.js +16 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/middlewares/express.d.ts +22 -0
  13. package/dist/middlewares/express.js +119 -0
  14. package/dist/middlewares/express.js.map +1 -0
  15. package/dist/middlewares/fastify.d.ts +18 -0
  16. package/dist/middlewares/fastify.js +129 -0
  17. package/dist/middlewares/fastify.js.map +1 -0
  18. package/dist/middlewares/hapi.d.ts +28 -0
  19. package/dist/middlewares/hapi.js +198 -0
  20. package/dist/middlewares/hapi.js.map +1 -0
  21. package/dist/middlewares/koa.d.ts +25 -0
  22. package/dist/middlewares/koa.js +139 -0
  23. package/dist/middlewares/koa.js.map +1 -0
  24. package/dist/types.d.ts +154 -0
  25. package/dist/types.js +2 -0
  26. package/dist/types.js.map +1 -0
  27. package/package.json +77 -62
  28. package/public/assets/index-CIdOb8D1.css +5 -0
  29. package/public/assets/index-Ct6Ovrls.js +17 -0
  30. package/public/index.html +11 -52
  31. package/public/site.webmanifest +8 -1
  32. package/Dockerfile +0 -23
  33. package/History.md +0 -118
  34. package/LICENSE +0 -21
  35. package/all-jobs.png +0 -0
  36. package/app.js +0 -23
  37. package/bin/agendash-standalone-fastify.js +0 -61
  38. package/bin/agendash-standalone-hapi.js +0 -60
  39. package/bin/agendash-standalone-koa.js +0 -56
  40. package/bin/agendash-standalone.js +0 -64
  41. package/create-job.png +0 -0
  42. package/entrypoint.sh +0 -5
  43. package/lib/controllers/agendash.js +0 -341
  44. package/lib/csp.js +0 -28
  45. package/lib/middlewares/README.md +0 -59
  46. package/lib/middlewares/express.js +0 -80
  47. package/lib/middlewares/fastify.js +0 -77
  48. package/lib/middlewares/hapi.js +0 -101
  49. package/lib/middlewares/koa.js +0 -77
  50. package/mobile-ui-sm.png +0 -0
  51. package/mobile-ui-xs.png +0 -0
  52. package/public/android-chrome-192x192.png +0 -0
  53. package/public/android-chrome-512x512.png +0 -0
  54. package/public/app/assets/ArgensdashV2Logo.svg +0 -18
  55. package/public/app/css/styles.css +0 -233
  56. package/public/app/js/confirmDelete.js +0 -40
  57. package/public/app/js/confirmDeleteMulti.js +0 -45
  58. package/public/app/js/confirmRequeue.js +0 -40
  59. package/public/app/js/confirmRequeueMulti.js +0 -45
  60. package/public/app/js/confirms.js +0 -8
  61. package/public/app/js/jobdetail.js +0 -48
  62. package/public/app/js/joblist.js +0 -248
  63. package/public/app/js/main.js +0 -283
  64. package/public/app/js/newJob.js +0 -89
  65. package/public/app/js/sidebar.js +0 -121
  66. package/public/app/js/topbar.js +0 -95
  67. package/public/apple-touch-icon.png +0 -0
  68. package/public/favicon-16x16.png +0 -0
  69. package/public/favicon-32x32.png +0 -0
  70. package/public/favicon.ico +0 -0
  71. package/search.png +0 -0
@@ -1,341 +0,0 @@
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
-
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
- [
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
- }
19
- }
20
- );
21
-
22
- // Mongoose internals changed at some point. This will fix crash for older versions.
23
- const mdb = agenda._mdb.admin ? agenda._mdb : agenda._mdb.db;
24
-
25
- mdb.admin().serverInfo((error, serverInfo) => {
26
- if (error) {
27
- throw error;
28
- }
29
-
30
- if (!semver.satisfies(semver.coerce(serverInfo.version), ">=3.6.0")) {
31
- throw new Error("MongoDB version not supported");
32
- }
33
- });
34
- });
35
-
36
- // Options = {query = '', property = '', isObjectId = false, limit, skip}
37
- const getJobs = function (job, state, options) {
38
- const preMatch = {};
39
- if (job) {
40
- preMatch.name = job;
41
- }
42
-
43
- if (options.query && options.property) {
44
- if (options.isObjectId) {
45
- preMatch[options.property] = new ObjectId(options.query);
46
- } else if (/^\d+$/.test(options.query)) {
47
- preMatch[options.property] = Number.parseInt(options.query, 10);
48
- } else {
49
- preMatch[options.property] = { $regex: options.query, $options: "i" };
50
- }
51
- }
52
-
53
- const postMatch = {};
54
- if (state) {
55
- postMatch[state] = true;
56
- }
57
-
58
- const collection = agenda._collection.collection || agenda._collection;
59
- return collection
60
- .aggregate([
61
- { $match: preMatch },
62
- {
63
- $sort: {
64
- repeatInterval: -1,
65
- nextRunAt: -1,
66
- lastRunAt: -1,
67
- lastFinishedAt: -1,
68
- },
69
- },
70
- {
71
- $project: {
72
- job: "$$ROOT",
73
- _id: "$$ROOT._id",
74
- running: {
75
- $and: ["$lastRunAt", { $gt: ["$lastRunAt", "$lastFinishedAt"] }],
76
- },
77
- scheduled: {
78
- $and: ["$nextRunAt", { $gte: ["$nextRunAt", new Date()] }],
79
- },
80
- queued: {
81
- $and: [
82
- "$nextRunAt",
83
- { $gte: [new Date(), "$nextRunAt"] },
84
- { $gte: ["$nextRunAt", "$lastFinishedAt"] },
85
- ],
86
- },
87
- completed: {
88
- $and: [
89
- "$lastFinishedAt",
90
- { $gt: ["$lastFinishedAt", "$failedAt"] },
91
- ],
92
- },
93
- failed: {
94
- $and: [
95
- "$lastFinishedAt",
96
- "$failedAt",
97
- { $eq: ["$lastFinishedAt", "$failedAt"] },
98
- ],
99
- },
100
- repeating: {
101
- $and: ["$repeatInterval", { $ne: ["$repeatInterval", null] }],
102
- },
103
- },
104
- },
105
- { $match: postMatch },
106
- {
107
- $facet: {
108
- pages: [
109
- { $count: "totalMatchs" },
110
- {
111
- $project: {
112
- totalPages: {
113
- $ceil: { $divide: ["$totalMatchs", options.limit] },
114
- },
115
- },
116
- },
117
- ],
118
- filtered: [{ $skip: options.skip }, { $limit: options.limit }],
119
- },
120
- },
121
- ])
122
- .toArray();
123
- };
124
-
125
- const getOverview = async () => {
126
- const collection = agenda._collection.collection || agenda._collection;
127
- const results = await collection
128
- .aggregate([
129
- {
130
- $group: {
131
- _id: "$name",
132
- displayName: { $first: "$name" },
133
- meta: {
134
- $addToSet: {
135
- type: "$type",
136
- priority: "$priority",
137
- repeatInterval: "$repeatInterval",
138
- repeatTimezone: "$repeatTimezone",
139
- },
140
- },
141
- total: { $sum: 1 },
142
- running: {
143
- $sum: {
144
- $cond: [
145
- {
146
- $and: [
147
- "$lastRunAt",
148
- { $gt: ["$lastRunAt", "$lastFinishedAt"] },
149
- ],
150
- },
151
- 1,
152
- 0,
153
- ],
154
- },
155
- },
156
- scheduled: {
157
- $sum: {
158
- $cond: [
159
- {
160
- $and: ["$nextRunAt", { $gte: ["$nextRunAt", new Date()] }],
161
- },
162
- 1,
163
- 0,
164
- ],
165
- },
166
- },
167
- queued: {
168
- $sum: {
169
- $cond: [
170
- {
171
- $and: [
172
- "$nextRunAt",
173
- { $gte: [new Date(), "$nextRunAt"] },
174
- { $gte: ["$nextRunAt", "$lastFinishedAt"] },
175
- ],
176
- },
177
- 1,
178
- 0,
179
- ],
180
- },
181
- },
182
- completed: {
183
- $sum: {
184
- $cond: [
185
- {
186
- $and: [
187
- "$lastFinishedAt",
188
- { $gt: ["$lastFinishedAt", "$failedAt"] },
189
- ],
190
- },
191
- 1,
192
- 0,
193
- ],
194
- },
195
- },
196
- failed: {
197
- $sum: {
198
- $cond: [
199
- {
200
- $and: [
201
- "$lastFinishedAt",
202
- "$failedAt",
203
- { $eq: ["$lastFinishedAt", "$failedAt"] },
204
- ],
205
- },
206
- 1,
207
- 0,
208
- ],
209
- },
210
- },
211
- repeating: {
212
- $sum: {
213
- $cond: [
214
- {
215
- $and: [
216
- "$repeatInterval",
217
- { $ne: ["$repeatInterval", null] },
218
- ],
219
- },
220
- 1,
221
- 0,
222
- ],
223
- },
224
- },
225
- },
226
- },
227
- ])
228
- .toArray();
229
- const states = {
230
- total: 0,
231
- running: 0,
232
- scheduled: 0,
233
- queued: 0,
234
- completed: 0,
235
- failed: 0,
236
- repeating: 0,
237
- };
238
- const totals = { displayName: "All Jobs", ...states };
239
-
240
- for (const job of results) {
241
- for (const state of Object.keys(states)) {
242
- totals[state] += job[state];
243
- }
244
- }
245
-
246
- results.unshift(totals);
247
- return results;
248
- };
249
-
250
- const api = async function (
251
- job,
252
- state,
253
- { query: q, property, isObjectId, skip, limit }
254
- ) {
255
- if (!agenda) {
256
- return Promise.reject(new Error("Agenda instance is not ready"));
257
- }
258
-
259
- limit = Number.parseInt(limit, 10) || 200;
260
- skip = Number.parseInt(skip, 10) || 0;
261
-
262
- const [overview, jobs] = await Promise.all([
263
- getOverview(),
264
- getJobs(job, state, { query: q, property, isObjectId, skip, limit }),
265
- ]);
266
- const apiResponse = {
267
- overview,
268
- jobs: jobs[0].filtered,
269
- totalPages: jobs[0].pages[0] ? jobs[0].pages[0].totalPages : 0,
270
- };
271
- apiResponse.title = options.title || "Agendash";
272
- apiResponse.currentRequest = {
273
- title: options.title || "Agendash",
274
- job: job || "All Jobs",
275
- state,
276
- };
277
- return apiResponse;
278
- };
279
-
280
- const requeueJobs = async (jobIds) => {
281
- if (!agenda) {
282
- return Promise.reject(new Error("Agenda instance is not ready"));
283
- }
284
-
285
- const collection = agenda._collection.collection || agenda._collection;
286
- const jobs = await collection
287
- .find({
288
- _id: { $in: jobIds.map((jobId) => new ObjectId(jobId)) },
289
- })
290
- .toArray();
291
- if (jobs.length === 0) {
292
- throw new Error("Job not found");
293
- }
294
-
295
- for (const job of jobs) {
296
- const newJob = agenda.create(job.name, job.data);
297
- // eslint-disable-next-line no-await-in-loop
298
- await newJob.save();
299
- }
300
-
301
- return "Jobs create successfully";
302
- };
303
-
304
- const deleteJobs = (jobIds) => {
305
- if (!agenda) {
306
- return Promise.reject(new Error("Agenda instance is not ready"));
307
- }
308
-
309
- return agenda.cancel({
310
- _id: { $in: jobIds.map((jobId) => new ObjectId(jobId)) },
311
- });
312
- };
313
-
314
- const createJob = (jobName, jobSchedule, jobRepeatEvery, jobData) => {
315
- if (!agenda) {
316
- return Promise.reject(new Error("Agenda instance is not ready"));
317
- }
318
-
319
- // @TODO: Need to validate user input.
320
- const job = agenda.create(jobName, jobData);
321
- if (jobSchedule && jobRepeatEvery) {
322
- job.repeatAt(jobSchedule);
323
- job.repeatEvery(jobRepeatEvery);
324
- } else if (jobSchedule) {
325
- job.schedule(jobSchedule);
326
- } else if (jobRepeatEvery) {
327
- job.repeatEvery(jobRepeatEvery);
328
- } else {
329
- return Promise.reject(new Error("Jobs not created"));
330
- }
331
-
332
- return job.save();
333
- };
334
-
335
- return {
336
- api,
337
- requeueJobs,
338
- deleteJobs,
339
- createJob,
340
- };
341
- };
package/lib/csp.js DELETED
@@ -1,28 +0,0 @@
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("; ")
@@ -1,59 +0,0 @@
1
- # Middlewares
2
-
3
- ## Hapi
4
-
5
- Works with [Hapijs](https://hapijs.com/) >= v18
6
-
7
- ### usage
8
-
9
- First install the dependencies in your app:
10
-
11
- ```bash
12
- npm install @hapi/hapi @hapi/inert agenda agendash
13
- ```
14
-
15
- Then use it as follows:
16
-
17
- ```javascript
18
- 'use strict';
19
-
20
- const Hapi = require('hapi');
21
- const { Agenda } = require('agenda');
22
- const Agendash = require('agendash');
23
-
24
- var agenda = new Agenda({
25
- db: { address: '<mongoUrl>'
26
- });
27
-
28
- const server = Hapi.server({
29
- port: 3000,
30
- host: 'localhost'
31
- });
32
-
33
- const init = async () => {
34
-
35
- await server.register(
36
- require('@hapi/inert'),
37
- {
38
- plugin: Agendash(agenda, {
39
- middleware: 'hapi'
40
- // can place other options (e.g. title) here
41
- }),
42
- options: {},
43
- routes: {
44
- prefix: '/agendash'
45
- }
46
- }
47
- );
48
-
49
- await server.start();
50
- console.log(`Server running at: ${server.info.uri}`);
51
- };
52
-
53
- process.on('unhandledRejection', (err) => {
54
- console.log(err);
55
- process.exit(1);
56
- });
57
-
58
- init();
59
- ```
@@ -1,80 +0,0 @@
1
- const path = require("path");
2
- const express = require("express");
3
- const bodyParser = require("body-parser");
4
- const csp = require("../csp");
5
-
6
- module.exports = (agendash) => {
7
- const { api, requeueJobs, deleteJobs, createJob } = agendash;
8
- const app = express();
9
- app.use(bodyParser.json());
10
- app.use(bodyParser.urlencoded({ extended: false }));
11
-
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) => {
20
- try {
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
- });
37
- response.json(apiResponse);
38
- } catch (error) {
39
- response.status(400).json(error);
40
- }
41
- });
42
-
43
- app.post("/api/jobs/requeue", async (request, response) => {
44
- try {
45
- const newJobs = await requeueJobs(request.body.jobIds);
46
- response.send(newJobs);
47
- } catch (error) {
48
- response.status(404).json(error);
49
- }
50
- });
51
-
52
- app.post("/api/jobs/delete", async (request, response) => {
53
- try {
54
- const deleted = await deleteJobs(request.body.jobIds);
55
- if (deleted) {
56
- response.json({ deleted: true });
57
- } else {
58
- response.json({ message: "Jobs not deleted" });
59
- }
60
- } catch (error) {
61
- response.status(404).json(error);
62
- }
63
- });
64
-
65
- app.post("/api/jobs/create", async (request, response) => {
66
- try {
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 });
74
- } catch (error) {
75
- response.status(400).json(error);
76
- }
77
- });
78
-
79
- return app;
80
- };
@@ -1,77 +0,0 @@
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
- };
@@ -1,101 +0,0 @@
1
- const path = require("path");
2
- const pack = require("../../package.json");
3
- const csp = require("../csp");
4
-
5
- module.exports = (agendash) => {
6
- const { api, requeueJobs, deleteJobs, createJob } = agendash;
7
- return {
8
- pkg: pack,
9
- register: (server, options) => {
10
- server.ext("onPreResponse", (req, h) => {
11
- req.response.header("Content-Security-Policy", csp);
12
- return h.continue;
13
- });
14
-
15
- server.route([
16
- {
17
- method: "GET",
18
- path: "/{param*}",
19
- handler: {
20
- directory: {
21
- path: path.join(__dirname, "../../public"),
22
- },
23
- },
24
- config: {
25
- auth: options.auth || false,
26
- },
27
- },
28
- {
29
- method: "GET",
30
- path: "/api",
31
- handler(request) {
32
- const {
33
- job,
34
- state,
35
- limit,
36
- skip,
37
- q,
38
- property,
39
- isObjectId,
40
- } = request.query;
41
- return api(job, state, {
42
- query: q,
43
- property,
44
- isObjectId,
45
- skip,
46
- limit,
47
- });
48
- },
49
- config: {
50
- auth: options.auth || false,
51
- },
52
- },
53
- {
54
- method: "POST",
55
- path: "/api/jobs/requeue",
56
- handler(request) {
57
- return requeueJobs(request.payload.jobIds);
58
- },
59
- config: {
60
- auth: options.auth || false,
61
- },
62
- },
63
- {
64
- method: "POST",
65
- path: "/api/jobs/delete",
66
- async handler(request, h) {
67
- const deleted = await deleteJobs(request.payload.jobIds);
68
- if (deleted) {
69
- return h.response({ deleted: true });
70
- }
71
-
72
- return h.code(404);
73
- },
74
- config: {
75
- auth: options.auth || false,
76
- },
77
- },
78
- {
79
- method: "POST",
80
- path: "/api/jobs/create",
81
- async handler(request, h) {
82
- const created = await createJob(
83
- request.payload.jobName,
84
- request.payload.jobSchedule,
85
- request.payload.jobRepeatEvery,
86
- request.payload.jobData
87
- );
88
- if (created) {
89
- return h.response({ created: true });
90
- }
91
-
92
- return h.code(404);
93
- },
94
- config: {
95
- auth: options.auth || false,
96
- },
97
- },
98
- ]);
99
- },
100
- };
101
- };