agendash 2.1.1 → 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.
package/History.md CHANGED
@@ -1,3 +1,13 @@
1
+ # 3.0.0
2
+
3
+ - BREAKING: Drop support for MongoDB v3.4, only >=v3.6 is supported now.
4
+ - BREAKING: Drop support for node 10, but added node 16 support.
5
+ - feat: add `fastify` support #194
6
+ - fix: a lot of rendering issues
7
+ - fix: newer version of `mongodb` compatibility #193
8
+
9
+ This file won't mention contributors anymore. Because all contributors are listed on the [main page](https://github.com/agenda/agendash) now. Thanks, GitHub!
10
+
1
11
  # 2.1.0 - JSON validation when creating jobs
2
12
 
3
13
  - feat: Add json validator message when createJob #169 (thanks @love8587)
package/README.md CHANGED
@@ -191,6 +191,33 @@ await app.listen(3002);
191
191
 
192
192
  Then browse to `http://localhost:3002/`.
193
193
 
194
+ #### Fastify
195
+
196
+ ```shell
197
+ npm i fastify
198
+ ```
199
+
200
+ ```js
201
+ const agenda = new Agenda().database(
202
+ "mongodb://127.0.0.1/agendaDb",
203
+ "agendaJobs"
204
+ );
205
+
206
+ const Fastify = require("fastify");
207
+ const fastify = new Fastify();
208
+
209
+ fastify.register(
210
+ Agendash(
211
+ agenda,
212
+ { middleware: "fastify" }
213
+ );
214
+ );
215
+
216
+ await fastify.listen(3002);
217
+ ```
218
+
219
+ Then browse to `http://localhost:3002/`.
220
+
194
221
  ### Standalone usage
195
222
 
196
223
  Agendash comes with a standalone Express app which you can use like this:
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ const Agenda = require("agenda");
4
+ const agendash = require("../app");
5
+ const Fastify = require("fastify");
6
+ const program = require("commander");
7
+
8
+ program
9
+ .option(
10
+ "-d, --db <db>",
11
+ "[required] Mongo connection string, same as Agenda connection string"
12
+ )
13
+ .option(
14
+ "-c, --collection <collection>",
15
+ "[optional] Mongo collection, same as Agenda collection name, default agendaJobs",
16
+ "agendaJobs"
17
+ )
18
+ .option(
19
+ "-p, --port <port>",
20
+ "[optional] Server port, default 3000",
21
+ (n, d) => Number(n) || d,
22
+ 3000
23
+ )
24
+ .option(
25
+ "-t, --title <title>",
26
+ "[optional] Page title, default Agendash",
27
+ "Agendash"
28
+ )
29
+ .option(
30
+ "-p, --path <path>",
31
+ "[optional] Path to bind Agendash to, default /",
32
+ "/"
33
+ )
34
+ .parse(process.argv);
35
+
36
+ if (!program.db) {
37
+ console.error("--db required");
38
+ process.exit(1);
39
+ }
40
+
41
+ if (!program.path.startsWith("/")) {
42
+ console.error("--path must begin with /");
43
+ process.exit(1);
44
+ }
45
+
46
+ const fastify = Fastify();
47
+
48
+ const agenda = new Agenda().database(program.db, program.collection);
49
+
50
+ fastify.register(
51
+ agendash(agenda, {
52
+ middleware: "fastify",
53
+ title: program.title,
54
+ })
55
+ );
56
+
57
+ fastify.listen(program.port, function () {
58
+ console.log(
59
+ `Agendash started http://localhost:${program.port}${program.path}`
60
+ );
61
+ });
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- const Agenda = require("agenda");
3
+ const { Agenda } = require("agenda");
4
4
  const agendash = require("../app");
5
5
  const Hapi = require("@hapi/hapi");
6
6
  const program = require("commander");
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- const Agenda = require("agenda");
3
+ const { Agenda } = require("agenda");
4
4
  const agendash = require("../app");
5
5
  const Koa = require("koa");
6
6
  const program = require("commander");
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  const http = require("http");
4
- const Agenda = require("agenda");
4
+ const { Agenda } = require("agenda");
5
5
  const agendash = require("../app");
6
6
  const express = require("express");
7
7
  const program = require("commander");
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
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
3
+ const { ObjectId } = require("mongodb"); // We rely on the Agenda's "mongodb", thus our package.json lists "*" as required version
4
4
 
5
5
  module.exports = function (agenda, options) {
6
6
  options = options || {};
@@ -27,7 +27,7 @@ module.exports = function (agenda, options) {
27
27
  throw error;
28
28
  }
29
29
 
30
- if (!semver.satisfies(serverInfo.version, ">=2.6.0")) {
30
+ if (!semver.satisfies(semver.coerce(serverInfo.version), ">=3.6.0")) {
31
31
  throw new Error("MongoDB version not supported");
32
32
  }
33
33
  });
@@ -42,9 +42,7 @@ module.exports = function (agenda, options) {
42
42
 
43
43
  if (options.query && options.property) {
44
44
  if (options.isObjectId) {
45
- preMatch[options.property] = ObjectID.createFromHexString(
46
- options.query
47
- );
45
+ preMatch[options.property] = ObjectId(options.query);
48
46
  } else if (/^\d+$/.test(options.query)) {
49
47
  preMatch[options.property] = Number.parseInt(options.query, 10);
50
48
  } else {
@@ -286,7 +284,7 @@ module.exports = function (agenda, options) {
286
284
  const collection = agenda._collection.collection || agenda._collection;
287
285
  const jobs = await collection
288
286
  .find({
289
- _id: { $in: jobIds.map((jobId) => collection.s.pkFactory(jobId)) },
287
+ _id: { $in: jobIds.map((jobId) => ObjectId(jobId)) },
290
288
  })
291
289
  .toArray();
292
290
  if (jobs.length === 0) {
@@ -307,9 +305,8 @@ module.exports = function (agenda, options) {
307
305
  return Promise.reject(new Error("Agenda instance is not ready"));
308
306
  }
309
307
 
310
- const collection = agenda._collection.collection || agenda._collection;
311
308
  return agenda.cancel({
312
- _id: { $in: jobIds.map((jobId) => collection.s.pkFactory(jobId)) },
309
+ _id: { $in: jobIds.map((jobId) => ObjectId(jobId)) },
313
310
  });
314
311
  };
315
312
 
@@ -18,7 +18,7 @@ Then use it as follows:
18
18
  'use strict';
19
19
 
20
20
  const Hapi = require('hapi');
21
- const Agenda = require('agenda');
21
+ const { Agenda } = require('agenda');
22
22
  const Agendash = require('agendash');
23
23
 
24
24
  var agenda = new Agenda({
@@ -0,0 +1,75 @@
1
+ const path = require("path");
2
+
3
+ module.exports = (agendash) => (instance, opts, done) => {
4
+ const { api, requeueJobs, deleteJobs, createJob } = agendash;
5
+
6
+ instance.register(require("fastify-static"), {
7
+ root: path.join(__dirname, "../../public"),
8
+ });
9
+
10
+ instance.get("/", function (req, reply) {
11
+ return reply.sendFile("index.html");
12
+ });
13
+
14
+ instance.get("/api", async (request, response) => {
15
+ try {
16
+ const {
17
+ job,
18
+ state,
19
+ skip,
20
+ limit,
21
+ q,
22
+ property,
23
+ isObjectId,
24
+ } = request.query;
25
+ const apiResponse = await api(job, state, {
26
+ query: q,
27
+ property,
28
+ isObjectId,
29
+ skip,
30
+ limit,
31
+ });
32
+ response.send(apiResponse);
33
+ } catch (error) {
34
+ response.status(400).send(error);
35
+ }
36
+ });
37
+
38
+ instance.post("/api/jobs/requeue", async (request, response) => {
39
+ try {
40
+ const newJobs = await requeueJobs(request.body.jobIds);
41
+ response.send(newJobs);
42
+ } catch (error) {
43
+ response.status(404).send(error);
44
+ }
45
+ });
46
+
47
+ instance.post("/api/jobs/delete", async (request, response) => {
48
+ try {
49
+ const deleted = await deleteJobs(request.body.jobIds);
50
+ if (deleted) {
51
+ response.send({ deleted: true });
52
+ } else {
53
+ response.send({ message: "Jobs not deleted" });
54
+ }
55
+ } catch (error) {
56
+ response.status(404).send(error);
57
+ }
58
+ });
59
+
60
+ instance.post("/api/jobs/create", async (request, response) => {
61
+ try {
62
+ await createJob(
63
+ request.body.jobName,
64
+ request.body.jobSchedule,
65
+ request.body.jobRepeatEvery,
66
+ request.body.jobData
67
+ );
68
+ response.send({ created: true });
69
+ } catch (error) {
70
+ response.status(400).send(error);
71
+ }
72
+ });
73
+
74
+ done();
75
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agendash",
3
- "version": "2.1.1",
3
+ "version": "3.0.0",
4
4
  "description": "A modern dashboard for Agenda.js with Pagination and Search capabilities",
5
5
  "main": "app.js",
6
6
  "bin": "bin/agendash-standalone.js",
@@ -37,7 +37,7 @@
37
37
  "entrypoint.sh"
38
38
  ],
39
39
  "dependencies": {
40
- "agenda": "^3.1.0 || ^4.0.0",
40
+ "agenda": "^4.2.1",
41
41
  "body-parser": "^1.15.0",
42
42
  "commander": "^2.9.0",
43
43
  "express": "^4.0.0",
@@ -45,17 +45,19 @@
45
45
  "semver": "^7.3.4"
46
46
  },
47
47
  "devDependencies": {
48
- "@hapi/hapi": "19.2.0",
49
- "@hapi/inert": "6.0.3",
48
+ "@hapi/hapi": "^20.2.1",
49
+ "@hapi/inert": "^6.0.5",
50
50
  "ava": "3.15.0",
51
51
  "eslint": "7.19.0",
52
+ "fastify": "^3.24.0",
53
+ "fastify-static": "^4.5.0",
52
54
  "koa": "2.13.1",
53
55
  "koa-bodyparser": "4.3.0",
54
56
  "koa-router": "10.0.0",
55
57
  "koa-static": "5.0.0",
56
58
  "npm-run-all": "4.1.5",
57
59
  "prettier": "2.2.1",
58
- "supertest": "6.1.3"
60
+ "supertest": "^6.2.2"
59
61
  },
60
62
  "eslintConfig": {
61
63
  "parserOptions": {
@@ -24,7 +24,7 @@ const jobDetail = Vue.component("job-detail", {
24
24
  <div class="modal-body">
25
25
  <div class="row my-3">
26
26
  <div class="col">
27
- <p><strong>Next run starts: </strong>{{ formatDate(job.job.lastRunAt) }}</p>
27
+ <p><strong>Next run starts: </strong>{{ formatDate(job.job.nextRunAt) }}</p>
28
28
  <p><strong>Last run started: </strong>{{ formatDate(job.job.lastRunAt) }}</p>
29
29
  </div>
30
30
  </div>
@@ -53,9 +53,13 @@ const jobList = Vue.component("job-list", {
53
53
  cleanMulti() {
54
54
  return console.log("received Clean Multi");
55
55
  },
56
+ formatTitle(date) {
57
+ if (!date) return;
58
+ return moment(date).format();
59
+ },
56
60
  formatDate(date) {
61
+ if (!date) return;
57
62
  return moment(date).fromNow();
58
- // return new Date(date).toLocaleDateString('en-US', { day: 'numeric', month: 'numeric', year: '2-digit', hour: "numeric", minute: "numeric", second: "numeric" })
59
63
  },
60
64
  checkAllCheckboxes() {
61
65
  const checkboxes = document.querySelectorAll(".checkbox-triggerable");
@@ -139,10 +143,10 @@ const jobList = Vue.component("job-list", {
139
143
  <i v-if="job.running" class="pill-own bg-warning pill-withoutIcon"><span>Running</span></i>
140
144
  </td>
141
145
  <td class="job-name" @click="toggleList(job)"> {{job.job.name}} </td>
142
- <td class="job-lastRunAt" @click="toggleList(job)"> {{ formatDate(job.job.lastRunAt) }} </td>
143
- <td class="job-nextRunAt" @click="toggleList(job)"> {{ formatDate(job.job.nextRunAt) }} </td>
144
- <td class="job-finishedAt" @click="toggleList(job)"> {{ formatDate(job.job.lastFinishedAt) }} </td>
145
- <td class="job-lockedAt" @click="toggleList(job)"> {{ formatDate(job.job.lockedAt) }} </td>
146
+ <td class="job-lastRunAt" :title="formatTitle(job.job.lastRunAt)" @click="toggleList(job)"> {{ formatDate(job.job.lastRunAt) }} </td>
147
+ <td class="job-nextRunAt" :title="formatTitle(job.job.nextRunAt)" @click="toggleList(job)"> {{ formatDate(job.job.nextRunAt) }} </td>
148
+ <td class="job-finishedAt" :title="formatTitle(job.job.lastFinishedAt)" @click="toggleList(job)"> {{ formatDate(job.job.lastFinishedAt) }} </td>
149
+ <td class="job-lockedAt" :title="formatTitle(job.job.lockedAt)" @click="toggleList(job)"> {{ formatDate(job.job.lockedAt) }} </td>
146
150
  <td class="job-actions">
147
151
  <i class="material-icons md-dark md-custom action-btn viewData text-primary" data-toggle="modal" data-target="#modalRequeueSure" @click="$emit('confirm-requeue', job)" data-placement="left" title="Requeue">update</i>
148
152
  <i class="material-icons md-dark md-custom action-btn viewData text-success" data-toggle="modal" data-target="#modalData" @click="$emit('show-job-detail', job)" data-placement="top" title="Job Data">visibility</i>
@@ -184,13 +188,13 @@ const jobList = Vue.component("job-list", {
184
188
  <div class="card-responsive-status-title">
185
189
  Last run started
186
190
  </div>
187
- <div class="mb-3">
191
+ <div class="mb-3" :title="formatTitle(job.job.lastRunAt)">
188
192
  {{ formatDate(job.job.lastRunAt) }}
189
193
  </div>
190
194
  <div class="card-responsive-status-title">
191
195
  Last finished
192
196
  </div>
193
- <div>
197
+ <div :title="formatTitle(job.job.lastFinishedAt)">
194
198
  {{ formatDate(job.job.lastFinishedAt) }}
195
199
  </div>
196
200
  </div>
@@ -198,14 +202,14 @@ const jobList = Vue.component("job-list", {
198
202
  <div class="card-responsive-status-title">
199
203
  Next run starts
200
204
  </div>
201
- <div class="mb-3">
205
+ <div class="mb-3" :title="formatTitle(job.job.nextRunAt)">
202
206
  {{ formatDate(job.job.nextRunAt) }}
203
207
  </div>
204
208
  <div class="card-responsive-status-title">
205
209
  Locked
206
210
  </div>
207
- <div>
208
- {{ job.job.lockedAt ? formatDate(job.job.lockedAt) : "-" }}
211
+ <div :title="formatTitle(job.job.lockedAt)">
212
+ {{ formatDate(job.job.lockedAt) || "-" }}
209
213
  </div>
210
214
  </div>
211
215
  </div>