@adminforth/background-jobs 1.2.4 → 1.4.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/build.log +2 -2
- package/custom/JobInfoPopup.vue +16 -4
- package/custom/NavbarJobs.vue +10 -0
- package/custom/utils.ts +1 -0
- package/dist/custom/JobInfoPopup.vue +16 -4
- package/dist/custom/NavbarJobs.vue +10 -0
- package/dist/custom/utils.ts +1 -0
- package/dist/index.js +8 -2
- package/index.ts +12 -5
- package/package.json +2 -1
package/build.log
CHANGED
|
@@ -11,5 +11,5 @@ custom/StateToIcon.vue
|
|
|
11
11
|
custom/tsconfig.json
|
|
12
12
|
custom/utils.ts
|
|
13
13
|
|
|
14
|
-
sent
|
|
15
|
-
total size is
|
|
14
|
+
sent 12,501 bytes received 134 bytes 25,270.00 bytes/sec
|
|
15
|
+
total size is 12,016 speedup is 0.95
|
package/custom/JobInfoPopup.vue
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="flex flex-col w-full min-w-96">
|
|
3
3
|
<div class="flex items-center mb-1">
|
|
4
|
-
<
|
|
5
|
-
|
|
4
|
+
<div class="flex flex-col items-start">
|
|
5
|
+
<h2 class="text-lg font-semibold dark:text-white">{{ job.name }}</h2>
|
|
6
|
+
<Tooltip>
|
|
7
|
+
<p class="text-xs text-gray-600 dark:text-gray-200 h-full">{{ t('Created:') }} {{ getTimeAgoString(new Date(job.createdAt)) }}</p>
|
|
8
|
+
<template #tooltip>
|
|
9
|
+
{{ t('Created at:') }} {{ new Date(job.createdAt).toLocaleString() }}
|
|
10
|
+
</template>
|
|
11
|
+
</Tooltip>
|
|
12
|
+
</div>
|
|
6
13
|
<div class="ml-auto flex flex-col items-start">
|
|
7
14
|
<div class="flex items-center">
|
|
8
15
|
<p class=" text-gray-800 dark:text-white h-full"> {{ t('Progress:') }} <span class="font-semibold" >{{ job.progress }}%</span></p>
|
|
9
16
|
<StateToIcon :job="job" />
|
|
10
17
|
</div>
|
|
11
|
-
<
|
|
18
|
+
<Tooltip v-if="job.finishedAt">
|
|
19
|
+
<p class="text-xs text-gray-600 dark:text-gray-200 h-full"> {{ t('Finished:') }} {{ getTimeAgoString(new Date(job.finishedAt)) }}</p>
|
|
20
|
+
<template #tooltip>
|
|
21
|
+
{{ t('Finished at:') }} {{ new Date(job.finishedAt).toLocaleString() }}
|
|
22
|
+
</template>
|
|
23
|
+
</Tooltip>
|
|
12
24
|
</div>
|
|
13
25
|
</div>
|
|
14
26
|
<div class="flex items-center gap-4 w-full">
|
|
@@ -40,7 +52,7 @@
|
|
|
40
52
|
|
|
41
53
|
<script setup lang="ts">
|
|
42
54
|
import type { IJob } from './utils';
|
|
43
|
-
import { ProgressBar, Button } from '@/afcl';
|
|
55
|
+
import { ProgressBar, Button, Tooltip } from '@/afcl';
|
|
44
56
|
import { getTimeAgoString, callAdminForthApi, getCustomComponent} from '@/utils';
|
|
45
57
|
import { useI18n } from 'vue-i18n';
|
|
46
58
|
import StateToIcon from './StateToIcon.vue';
|
package/custom/NavbarJobs.vue
CHANGED
|
@@ -94,11 +94,21 @@
|
|
|
94
94
|
if (data.progress !== undefined) {
|
|
95
95
|
jobs.value[jobIndex].progress = data.progress;
|
|
96
96
|
}
|
|
97
|
+
if (data.finishedAt) {
|
|
98
|
+
jobs.value[jobIndex].finishedAt = data.finishedAt;
|
|
99
|
+
}
|
|
100
|
+
if (data.state) {
|
|
101
|
+
jobs.value[jobIndex].state = {
|
|
102
|
+
...jobs.value[jobIndex].state,
|
|
103
|
+
...data.state,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
97
106
|
} else {
|
|
98
107
|
jobs.value.unshift({
|
|
99
108
|
id: data.jobId,
|
|
100
109
|
name: data.name || 'Unknown Job',
|
|
101
110
|
status: data.status || 'IN_PROGRESS',
|
|
111
|
+
state: data.state || {},
|
|
102
112
|
progress: data.progress || 0,
|
|
103
113
|
createdAt: data.createdAt || new Date().toISOString(),
|
|
104
114
|
customComponent: data.customComponent,
|
package/custom/utils.ts
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="flex flex-col w-full min-w-96">
|
|
3
3
|
<div class="flex items-center mb-1">
|
|
4
|
-
<
|
|
5
|
-
|
|
4
|
+
<div class="flex flex-col items-start">
|
|
5
|
+
<h2 class="text-lg font-semibold dark:text-white">{{ job.name }}</h2>
|
|
6
|
+
<Tooltip>
|
|
7
|
+
<p class="text-xs text-gray-600 dark:text-gray-200 h-full">{{ t('Created:') }} {{ getTimeAgoString(new Date(job.createdAt)) }}</p>
|
|
8
|
+
<template #tooltip>
|
|
9
|
+
{{ t('Created at:') }} {{ new Date(job.createdAt).toLocaleString() }}
|
|
10
|
+
</template>
|
|
11
|
+
</Tooltip>
|
|
12
|
+
</div>
|
|
6
13
|
<div class="ml-auto flex flex-col items-start">
|
|
7
14
|
<div class="flex items-center">
|
|
8
15
|
<p class=" text-gray-800 dark:text-white h-full"> {{ t('Progress:') }} <span class="font-semibold" >{{ job.progress }}%</span></p>
|
|
9
16
|
<StateToIcon :job="job" />
|
|
10
17
|
</div>
|
|
11
|
-
<
|
|
18
|
+
<Tooltip v-if="job.finishedAt">
|
|
19
|
+
<p class="text-xs text-gray-600 dark:text-gray-200 h-full"> {{ t('Finished:') }} {{ getTimeAgoString(new Date(job.finishedAt)) }}</p>
|
|
20
|
+
<template #tooltip>
|
|
21
|
+
{{ t('Finished at:') }} {{ new Date(job.finishedAt).toLocaleString() }}
|
|
22
|
+
</template>
|
|
23
|
+
</Tooltip>
|
|
12
24
|
</div>
|
|
13
25
|
</div>
|
|
14
26
|
<div class="flex items-center gap-4 w-full">
|
|
@@ -40,7 +52,7 @@
|
|
|
40
52
|
|
|
41
53
|
<script setup lang="ts">
|
|
42
54
|
import type { IJob } from './utils';
|
|
43
|
-
import { ProgressBar, Button } from '@/afcl';
|
|
55
|
+
import { ProgressBar, Button, Tooltip } from '@/afcl';
|
|
44
56
|
import { getTimeAgoString, callAdminForthApi, getCustomComponent} from '@/utils';
|
|
45
57
|
import { useI18n } from 'vue-i18n';
|
|
46
58
|
import StateToIcon from './StateToIcon.vue';
|
|
@@ -94,11 +94,21 @@
|
|
|
94
94
|
if (data.progress !== undefined) {
|
|
95
95
|
jobs.value[jobIndex].progress = data.progress;
|
|
96
96
|
}
|
|
97
|
+
if (data.finishedAt) {
|
|
98
|
+
jobs.value[jobIndex].finishedAt = data.finishedAt;
|
|
99
|
+
}
|
|
100
|
+
if (data.state) {
|
|
101
|
+
jobs.value[jobIndex].state = {
|
|
102
|
+
...jobs.value[jobIndex].state,
|
|
103
|
+
...data.state,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
97
106
|
} else {
|
|
98
107
|
jobs.value.unshift({
|
|
99
108
|
id: data.jobId,
|
|
100
109
|
name: data.name || 'Unknown Job',
|
|
101
110
|
status: data.status || 'IN_PROGRESS',
|
|
111
|
+
state: data.state || {},
|
|
102
112
|
progress: data.progress || 0,
|
|
103
113
|
createdAt: data.createdAt || new Date().toISOString(),
|
|
104
114
|
customComponent: data.customComponent,
|
package/dist/custom/utils.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -12,6 +12,8 @@ import { afLogger } from "adminforth";
|
|
|
12
12
|
import pLimit from 'p-limit';
|
|
13
13
|
import { Level } from 'level';
|
|
14
14
|
import fs from 'fs/promises';
|
|
15
|
+
import { Mutex } from 'async-mutex';
|
|
16
|
+
const mutex = new Mutex();
|
|
15
17
|
export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
16
18
|
constructor(options) {
|
|
17
19
|
super(options, import.meta.url);
|
|
@@ -147,6 +149,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
147
149
|
[this.options.progressField]: 0,
|
|
148
150
|
[this.options.statusField]: 'IN_PROGRESS',
|
|
149
151
|
[this.options.jobHandlerField]: jobHandlerName,
|
|
152
|
+
[this.options.stateField]: '{}'
|
|
150
153
|
};
|
|
151
154
|
const creationResult = yield this.adminforth.resource(this.getResourceId()).create(objectToSave);
|
|
152
155
|
let createdRecord = null;
|
|
@@ -174,6 +177,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
174
177
|
});
|
|
175
178
|
yield Promise.all(createTaskRecordsPromises);
|
|
176
179
|
this.runProcessingTasks(tasks, jobLevelDb, jobId, handleTask, parrallelLimit);
|
|
180
|
+
return jobId;
|
|
177
181
|
});
|
|
178
182
|
}
|
|
179
183
|
runProcessingTasks(tasks, jobLevelDb, jobId, handleTask, parrallelLimit) {
|
|
@@ -209,7 +213,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
209
213
|
this.adminforth.websocket.publish(`/background-jobs-task-update/${jobId}`, { taskIndex, status: "IN_PROGRESS" });
|
|
210
214
|
//handling the task
|
|
211
215
|
try {
|
|
212
|
-
yield handleTask({ setTaskStateField, getTaskStateField });
|
|
216
|
+
yield handleTask({ jobId, setTaskStateField, getTaskStateField });
|
|
213
217
|
//Set task status to completed in level db
|
|
214
218
|
yield this.setLevelDbTaskStatusField(jobLevelDb, taskIndex.toString(), 'DONE');
|
|
215
219
|
this.adminforth.websocket.publish(`/background-jobs-task-update/${jobId}`, { taskIndex, status: "DONE" });
|
|
@@ -242,7 +246,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
242
246
|
[this.options.statusField]: 'DONE',
|
|
243
247
|
[this.options.finishedAtField]: (new Date()).toISOString(),
|
|
244
248
|
});
|
|
245
|
-
this.adminforth.websocket.publish('/background-jobs', { jobId, status: 'DONE' });
|
|
249
|
+
this.adminforth.websocket.publish('/background-jobs', { jobId, status: 'DONE', finishedAt: (new Date()).toISOString() });
|
|
246
250
|
}
|
|
247
251
|
else if (failedTasks > 0) {
|
|
248
252
|
yield this.adminforth.resource(this.getResourceId()).update(jobId, {
|
|
@@ -319,6 +323,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
319
323
|
const state = jobRecord[this.options.stateField];
|
|
320
324
|
const parsedState = JSON.parse(state);
|
|
321
325
|
parsedState[key] = value;
|
|
326
|
+
this.adminforth.websocket.publish(`/background-jobs`, { jobId, state: parsedState });
|
|
322
327
|
yield this.adminforth.resource(this.getResourceId()).update(jobId, {
|
|
323
328
|
[this.options.stateField]: JSON.stringify(parsedState),
|
|
324
329
|
});
|
|
@@ -385,6 +390,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
385
390
|
createdAt: job[this.options.createdAtField],
|
|
386
391
|
finishedAt: job[this.options.finishedAtField] || null,
|
|
387
392
|
status: job[this.options.statusField],
|
|
393
|
+
state: JSON.parse(job[this.options.stateField]),
|
|
388
394
|
progress: job[this.options.progressField],
|
|
389
395
|
customComponent: this.jobCustomComponents[job[this.options.jobHandlerField]],
|
|
390
396
|
};
|
package/index.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { AdminForthPlugin, Filters, Sorts } from "adminforth";
|
|
2
|
-
import type { IAdminForth, IHttpServer,
|
|
2
|
+
import type { IAdminForth, IHttpServer, AdminForthResource, AdminUser, AdminForthComponentDeclarationFull } from "adminforth";
|
|
3
3
|
import type { PluginOptions } from './types.js';
|
|
4
4
|
import { afLogger } from "adminforth";
|
|
5
5
|
import pLimit from 'p-limit';
|
|
6
6
|
import { Level } from 'level';
|
|
7
7
|
import fs from 'fs/promises';
|
|
8
|
+
import {Mutex, MutexInterface, Semaphore, SemaphoreInterface, withTimeout} from 'async-mutex';
|
|
9
|
+
|
|
10
|
+
const mutex = new Mutex();
|
|
8
11
|
|
|
9
12
|
type TaskStatus = 'SCHEDULED' | 'IN_PROGRESS' | 'DONE' | 'FAILED';
|
|
10
13
|
type setStateFieldParams = (state: Record<string, any>) => void;
|
|
11
14
|
type getStateFieldParams = () => any;
|
|
12
|
-
type taskHandlerType = ( { setTaskStateField, getTaskStateField }: { setTaskStateField: setStateFieldParams; getTaskStateField: getStateFieldParams } ) => Promise<void>;
|
|
15
|
+
type taskHandlerType = ( { jobId, setTaskStateField, getTaskStateField }: { jobId: string; setTaskStateField: setStateFieldParams; getTaskStateField: getStateFieldParams } ) => Promise<void>;
|
|
13
16
|
type taskType = {
|
|
14
17
|
skip?: boolean;
|
|
15
18
|
state: Record<string, any>;
|
|
@@ -146,7 +149,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
146
149
|
adminUser: AdminUser,
|
|
147
150
|
tasks: taskType[],
|
|
148
151
|
jobHandlerName: string,
|
|
149
|
-
) {
|
|
152
|
+
): Promise<string> {
|
|
150
153
|
|
|
151
154
|
const handleTask: taskHandlerType = this.taskHandlers[jobHandlerName];
|
|
152
155
|
if (!handleTask) {
|
|
@@ -161,6 +164,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
161
164
|
[this.options.progressField]: 0,
|
|
162
165
|
[this.options.statusField]: 'IN_PROGRESS',
|
|
163
166
|
[this.options.jobHandlerField]: jobHandlerName,
|
|
167
|
+
[this.options.stateField]: '{}'
|
|
164
168
|
}
|
|
165
169
|
|
|
166
170
|
const creationResult = await this.adminforth.resource(this.getResourceId()).create(objectToSave);
|
|
@@ -193,6 +197,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
193
197
|
await Promise.all(createTaskRecordsPromises);
|
|
194
198
|
|
|
195
199
|
this.runProcessingTasks(tasks, jobLevelDb, jobId, handleTask, parrallelLimit);
|
|
200
|
+
return jobId;
|
|
196
201
|
}
|
|
197
202
|
|
|
198
203
|
private async runProcessingTasks(
|
|
@@ -238,7 +243,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
238
243
|
|
|
239
244
|
//handling the task
|
|
240
245
|
try {
|
|
241
|
-
await handleTask({ setTaskStateField, getTaskStateField });
|
|
246
|
+
await handleTask({ jobId, setTaskStateField, getTaskStateField });
|
|
242
247
|
|
|
243
248
|
//Set task status to completed in level db
|
|
244
249
|
await this.setLevelDbTaskStatusField(jobLevelDb, taskIndex.toString(), 'DONE');
|
|
@@ -273,7 +278,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
273
278
|
[this.options.statusField]: 'DONE',
|
|
274
279
|
[this.options.finishedAtField]: (new Date()).toISOString(),
|
|
275
280
|
})
|
|
276
|
-
this.adminforth.websocket.publish('/background-jobs', { jobId, status: 'DONE' });
|
|
281
|
+
this.adminforth.websocket.publish('/background-jobs', { jobId, status: 'DONE', finishedAt: (new Date()).toISOString() });
|
|
277
282
|
} else if (failedTasks > 0) {
|
|
278
283
|
await this.adminforth.resource(this.getResourceId()).update(jobId, {
|
|
279
284
|
[this.options.statusField]: 'DONE_WITH_ERRORS',
|
|
@@ -348,6 +353,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
348
353
|
const state = jobRecord[this.options.stateField];
|
|
349
354
|
const parsedState = JSON.parse(state);
|
|
350
355
|
parsedState[key] = value;
|
|
356
|
+
this.adminforth.websocket.publish(`/background-jobs`, { jobId, state: parsedState });
|
|
351
357
|
await this.adminforth.resource(this.getResourceId()).update(jobId, {
|
|
352
358
|
[this.options.stateField]: JSON.stringify(parsedState),
|
|
353
359
|
});
|
|
@@ -415,6 +421,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
415
421
|
createdAt: job[this.options.createdAtField],
|
|
416
422
|
finishedAt: job[this.options.finishedAtField] || null,
|
|
417
423
|
status: job[this.options.statusField],
|
|
424
|
+
state: JSON.parse(job[this.options.stateField]),
|
|
418
425
|
progress: job[this.options.progressField],
|
|
419
426
|
customComponent: this.jobCustomComponents[job[this.options.jobHandlerField]],
|
|
420
427
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adminforth/background-jobs",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@vueuse/core": "^14.2.1",
|
|
23
23
|
"adminforth": "latest",
|
|
24
|
+
"async-mutex": "^0.5.0",
|
|
24
25
|
"level": "^10.0.0",
|
|
25
26
|
"p-limit": "^7.3.0"
|
|
26
27
|
}
|