@adminforth/upload 2.4.0 → 2.5.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/imageGenerator.vue +46 -9
- package/dist/custom/imageGenerator.vue +46 -9
- package/dist/index.js +86 -56
- package/index.ts +103 -71
- package/package.json +2 -2
package/build.log
CHANGED
|
@@ -11,5 +11,5 @@ custom/preview.vue
|
|
|
11
11
|
custom/tsconfig.json
|
|
12
12
|
custom/uploader.vue
|
|
13
13
|
|
|
14
|
-
sent
|
|
15
|
-
total size is
|
|
14
|
+
sent 49,208 bytes received 134 bytes 98,684.00 bytes/sec
|
|
15
|
+
total size is 48,726 speedup is 0.99
|
|
@@ -248,7 +248,6 @@ onMounted(async () => {
|
|
|
248
248
|
|
|
249
249
|
if (resp?.files?.length) {
|
|
250
250
|
attachmentFiles.value = resp.files;
|
|
251
|
-
console.log('attachmentFiles', attachmentFiles.value);
|
|
252
251
|
}
|
|
253
252
|
} catch (err) {
|
|
254
253
|
console.error('Failed to fetch attachment files', err);
|
|
@@ -337,7 +336,7 @@ async function generateImages() {
|
|
|
337
336
|
let error = null;
|
|
338
337
|
try {
|
|
339
338
|
resp = await callAdminForthApi({
|
|
340
|
-
path: `/plugin/${props.meta.pluginInstanceId}/
|
|
339
|
+
path: `/plugin/${props.meta.pluginInstanceId}/create-image-generation-job`,
|
|
341
340
|
method: 'POST',
|
|
342
341
|
body: {
|
|
343
342
|
prompt: prompt.value,
|
|
@@ -346,16 +345,13 @@ async function generateImages() {
|
|
|
346
345
|
});
|
|
347
346
|
} catch (e) {
|
|
348
347
|
console.error(e);
|
|
349
|
-
} finally {
|
|
350
|
-
clearInterval(ticker);
|
|
351
|
-
loadingTimer.value = null;
|
|
352
|
-
loading.value = false;
|
|
353
348
|
}
|
|
349
|
+
|
|
354
350
|
if (resp?.error) {
|
|
355
351
|
error = resp.error;
|
|
356
352
|
}
|
|
357
353
|
if (!resp) {
|
|
358
|
-
error = $t('Error
|
|
354
|
+
error = $t('Error creating image generation job');
|
|
359
355
|
}
|
|
360
356
|
|
|
361
357
|
if (error) {
|
|
@@ -371,11 +367,53 @@ async function generateImages() {
|
|
|
371
367
|
return;
|
|
372
368
|
}
|
|
373
369
|
|
|
370
|
+
const jobId = resp.jobId;
|
|
371
|
+
let jobStatus = null;
|
|
372
|
+
let jobResponse = null;
|
|
373
|
+
do {
|
|
374
|
+
jobResponse = await callAdminForthApi({
|
|
375
|
+
path: `/plugin/${props.meta.pluginInstanceId}/get-image-generation-job-status`,
|
|
376
|
+
method: 'POST',
|
|
377
|
+
body: { jobId },
|
|
378
|
+
});
|
|
379
|
+
if (jobResponse?.error) {
|
|
380
|
+
error = jobResponse.error;
|
|
381
|
+
break;
|
|
382
|
+
};
|
|
383
|
+
jobStatus = jobResponse?.job?.status;
|
|
384
|
+
if (jobStatus === 'failed') {
|
|
385
|
+
error = jobResponse?.job?.error || $t('Image generation job failed');
|
|
386
|
+
}
|
|
387
|
+
if (jobStatus === 'timeout') {
|
|
388
|
+
error = jobResponse?.job?.error || $t('Image generation job timeout');
|
|
389
|
+
}
|
|
390
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
391
|
+
} while (jobStatus === 'in_progress')
|
|
392
|
+
|
|
393
|
+
if (error) {
|
|
394
|
+
adminforth.alert({
|
|
395
|
+
message: error,
|
|
396
|
+
variant: 'danger',
|
|
397
|
+
timeout: 'unlimited',
|
|
398
|
+
});
|
|
399
|
+
clearInterval(ticker);
|
|
400
|
+
loadingTimer.value = null;
|
|
401
|
+
loading.value = false;
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const respImages = jobResponse?.job?.images || [];
|
|
406
|
+
|
|
374
407
|
images.value = [
|
|
375
408
|
...images.value,
|
|
376
|
-
...
|
|
409
|
+
...respImages,
|
|
377
410
|
];
|
|
378
411
|
|
|
412
|
+
clearInterval(ticker);
|
|
413
|
+
loadingTimer.value = null;
|
|
414
|
+
loading.value = false;
|
|
415
|
+
|
|
416
|
+
|
|
379
417
|
// images.value = [
|
|
380
418
|
// 'https://via.placeholder.com/600x400?text=Image+1',
|
|
381
419
|
// 'https://via.placeholder.com/600x400?text=Image+2',
|
|
@@ -386,7 +424,6 @@ async function generateImages() {
|
|
|
386
424
|
caurosel.value = new Carousel(
|
|
387
425
|
document.getElementById('gallery'),
|
|
388
426
|
images.value.map((img, index) => {
|
|
389
|
-
console.log('mapping image', img, index);
|
|
390
427
|
return {
|
|
391
428
|
image: img,
|
|
392
429
|
el: document.getElementById('gallery').querySelector(`[data-carousel-item]:nth-child(${index + 1})`),
|
|
@@ -248,7 +248,6 @@ onMounted(async () => {
|
|
|
248
248
|
|
|
249
249
|
if (resp?.files?.length) {
|
|
250
250
|
attachmentFiles.value = resp.files;
|
|
251
|
-
console.log('attachmentFiles', attachmentFiles.value);
|
|
252
251
|
}
|
|
253
252
|
} catch (err) {
|
|
254
253
|
console.error('Failed to fetch attachment files', err);
|
|
@@ -337,7 +336,7 @@ async function generateImages() {
|
|
|
337
336
|
let error = null;
|
|
338
337
|
try {
|
|
339
338
|
resp = await callAdminForthApi({
|
|
340
|
-
path: `/plugin/${props.meta.pluginInstanceId}/
|
|
339
|
+
path: `/plugin/${props.meta.pluginInstanceId}/create-image-generation-job`,
|
|
341
340
|
method: 'POST',
|
|
342
341
|
body: {
|
|
343
342
|
prompt: prompt.value,
|
|
@@ -346,16 +345,13 @@ async function generateImages() {
|
|
|
346
345
|
});
|
|
347
346
|
} catch (e) {
|
|
348
347
|
console.error(e);
|
|
349
|
-
} finally {
|
|
350
|
-
clearInterval(ticker);
|
|
351
|
-
loadingTimer.value = null;
|
|
352
|
-
loading.value = false;
|
|
353
348
|
}
|
|
349
|
+
|
|
354
350
|
if (resp?.error) {
|
|
355
351
|
error = resp.error;
|
|
356
352
|
}
|
|
357
353
|
if (!resp) {
|
|
358
|
-
error = $t('Error
|
|
354
|
+
error = $t('Error creating image generation job');
|
|
359
355
|
}
|
|
360
356
|
|
|
361
357
|
if (error) {
|
|
@@ -371,11 +367,53 @@ async function generateImages() {
|
|
|
371
367
|
return;
|
|
372
368
|
}
|
|
373
369
|
|
|
370
|
+
const jobId = resp.jobId;
|
|
371
|
+
let jobStatus = null;
|
|
372
|
+
let jobResponse = null;
|
|
373
|
+
do {
|
|
374
|
+
jobResponse = await callAdminForthApi({
|
|
375
|
+
path: `/plugin/${props.meta.pluginInstanceId}/get-image-generation-job-status`,
|
|
376
|
+
method: 'POST',
|
|
377
|
+
body: { jobId },
|
|
378
|
+
});
|
|
379
|
+
if (jobResponse?.error) {
|
|
380
|
+
error = jobResponse.error;
|
|
381
|
+
break;
|
|
382
|
+
};
|
|
383
|
+
jobStatus = jobResponse?.job?.status;
|
|
384
|
+
if (jobStatus === 'failed') {
|
|
385
|
+
error = jobResponse?.job?.error || $t('Image generation job failed');
|
|
386
|
+
}
|
|
387
|
+
if (jobStatus === 'timeout') {
|
|
388
|
+
error = jobResponse?.job?.error || $t('Image generation job timeout');
|
|
389
|
+
}
|
|
390
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
391
|
+
} while (jobStatus === 'in_progress')
|
|
392
|
+
|
|
393
|
+
if (error) {
|
|
394
|
+
adminforth.alert({
|
|
395
|
+
message: error,
|
|
396
|
+
variant: 'danger',
|
|
397
|
+
timeout: 'unlimited',
|
|
398
|
+
});
|
|
399
|
+
clearInterval(ticker);
|
|
400
|
+
loadingTimer.value = null;
|
|
401
|
+
loading.value = false;
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const respImages = jobResponse?.job?.images || [];
|
|
406
|
+
|
|
374
407
|
images.value = [
|
|
375
408
|
...images.value,
|
|
376
|
-
...
|
|
409
|
+
...respImages,
|
|
377
410
|
];
|
|
378
411
|
|
|
412
|
+
clearInterval(ticker);
|
|
413
|
+
loadingTimer.value = null;
|
|
414
|
+
loading.value = false;
|
|
415
|
+
|
|
416
|
+
|
|
379
417
|
// images.value = [
|
|
380
418
|
// 'https://via.placeholder.com/600x400?text=Image+1',
|
|
381
419
|
// 'https://via.placeholder.com/600x400?text=Image+2',
|
|
@@ -386,7 +424,6 @@ async function generateImages() {
|
|
|
386
424
|
caurosel.value = new Carousel(
|
|
387
425
|
document.getElementById('gallery'),
|
|
388
426
|
images.value.map((img, index) => {
|
|
389
|
-
console.log('mapping image', img, index);
|
|
390
427
|
return {
|
|
391
428
|
image: img,
|
|
392
429
|
el: document.getElementById('gallery').querySelector(`[data-carousel-item]:nth-child(${index + 1})`),
|
package/dist/index.js
CHANGED
|
@@ -10,9 +10,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import { AdminForthPlugin, Filters, suggestIfTypo } from "adminforth";
|
|
11
11
|
import { Readable } from "stream";
|
|
12
12
|
import { RateLimiter } from "adminforth";
|
|
13
|
+
import { randomUUID } from "crypto";
|
|
13
14
|
import { interpretResource } from 'adminforth';
|
|
14
15
|
import { ActionCheckSource } from 'adminforth';
|
|
15
16
|
const ADMINFORTH_NOT_YET_USED_TAG = 'adminforth-candidate-for-cleanup';
|
|
17
|
+
const jobs = new Map();
|
|
16
18
|
export default class UploadPlugin extends AdminForthPlugin {
|
|
17
19
|
constructor(options) {
|
|
18
20
|
var _a, _b, _c;
|
|
@@ -25,6 +27,70 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
25
27
|
this.rateLimiter = new RateLimiter((_c = this.options.generation.rateLimit) === null || _c === void 0 ? void 0 : _c.limit);
|
|
26
28
|
}
|
|
27
29
|
}
|
|
30
|
+
generateImages(jobId, prompt, recordId, adminUser, headers) {
|
|
31
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
32
|
+
var _a, _b;
|
|
33
|
+
if ((_a = this.options.generation.rateLimit) === null || _a === void 0 ? void 0 : _a.limit) {
|
|
34
|
+
// rate limit
|
|
35
|
+
// const { error } = RateLimiter.checkRateLimit(
|
|
36
|
+
// this.pluginInstanceId,
|
|
37
|
+
// this.options.generation.rateLimit?.limit,
|
|
38
|
+
// this.adminforth.auth.getClientIp(headers),
|
|
39
|
+
// );
|
|
40
|
+
if (!(yield this.rateLimiter.consume(`${this.pluginInstanceId}-${this.adminforth.auth.getClientIp(headers)}`))) {
|
|
41
|
+
jobs.set(jobId, { status: "failed", error: this.options.generation.rateLimit.errorMessage });
|
|
42
|
+
return { error: this.options.generation.rateLimit.errorMessage };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
let attachmentFiles = [];
|
|
46
|
+
if (this.options.generation.attachFiles) {
|
|
47
|
+
// TODO - does it require additional allowed action to check this record id has access to get the image?
|
|
48
|
+
// or should we mention in docs that user should do validation in method itself
|
|
49
|
+
const record = yield this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ((_b = this.resourceConfig.columns.find(c => c.primaryKey)) === null || _b === void 0 ? void 0 : _b.name, recordId)]);
|
|
50
|
+
if (!record) {
|
|
51
|
+
return { error: `Record with id ${recordId} not found` };
|
|
52
|
+
}
|
|
53
|
+
attachmentFiles = yield this.options.generation.attachFiles({ record, adminUser });
|
|
54
|
+
// if files is not array, make it array
|
|
55
|
+
if (!Array.isArray(attachmentFiles)) {
|
|
56
|
+
attachmentFiles = [attachmentFiles];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
let error = undefined;
|
|
60
|
+
const STUB_MODE = false;
|
|
61
|
+
const images = yield Promise.all((new Array(this.options.generation.countToGenerate)).fill(0).map(() => __awaiter(this, void 0, void 0, function* () {
|
|
62
|
+
if (STUB_MODE) {
|
|
63
|
+
yield new Promise((resolve) => setTimeout(resolve, 2000));
|
|
64
|
+
return `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
|
|
65
|
+
}
|
|
66
|
+
const start = +new Date();
|
|
67
|
+
let resp;
|
|
68
|
+
try {
|
|
69
|
+
resp = yield this.options.generation.adapter.generate({
|
|
70
|
+
prompt,
|
|
71
|
+
inputFiles: attachmentFiles,
|
|
72
|
+
n: 1,
|
|
73
|
+
size: this.options.generation.outputSize,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
error = `No response from image generation provider: ${e.message}. Please check your prompt or try again later.`;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (resp.error) {
|
|
81
|
+
console.error('Error generating image', resp.error);
|
|
82
|
+
error = resp.error;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
this.totalCalls++;
|
|
86
|
+
this.totalDuration += (+new Date() - start) / 1000;
|
|
87
|
+
return resp.imageURLs[0];
|
|
88
|
+
})));
|
|
89
|
+
jobs.set(jobId, { status: "completed", images, error });
|
|
90
|
+
return { ok: true };
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
;
|
|
28
94
|
instanceUniqueRepresentation(pluginOptions) {
|
|
29
95
|
return `${pluginOptions.pathColumnName}`;
|
|
30
96
|
}
|
|
@@ -282,66 +348,30 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
282
348
|
// }'
|
|
283
349
|
server.endpoint({
|
|
284
350
|
method: 'POST',
|
|
285
|
-
path: `/plugin/${this.pluginInstanceId}/
|
|
351
|
+
path: `/plugin/${this.pluginInstanceId}/create-image-generation-job`,
|
|
286
352
|
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers }) {
|
|
287
|
-
var _b;
|
|
288
353
|
const { prompt, recordId } = body;
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
354
|
+
const jobId = randomUUID();
|
|
355
|
+
jobs.set(jobId, { status: "in_progress" });
|
|
356
|
+
this.generateImages(jobId, prompt, recordId, adminUser, headers);
|
|
357
|
+
setTimeout(() => jobs.delete(jobId), 1800000);
|
|
358
|
+
setTimeout(() => { jobs.set(jobId, { status: "timeout" }); }, 300000);
|
|
359
|
+
return { ok: true, jobId };
|
|
360
|
+
})
|
|
361
|
+
});
|
|
362
|
+
server.endpoint({
|
|
363
|
+
method: 'POST',
|
|
364
|
+
path: `/plugin/${this.pluginInstanceId}/get-image-generation-job-status`,
|
|
365
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers }) {
|
|
366
|
+
const jobId = body.jobId;
|
|
367
|
+
if (!jobId) {
|
|
368
|
+
return { error: "Can't find job id" };
|
|
299
369
|
}
|
|
300
|
-
|
|
301
|
-
if (
|
|
302
|
-
|
|
303
|
-
// or should we mention in docs that user should do validation in method itself
|
|
304
|
-
const record = yield this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ((_b = this.resourceConfig.columns.find((column) => column.primaryKey)) === null || _b === void 0 ? void 0 : _b.name, recordId)]);
|
|
305
|
-
if (!record) {
|
|
306
|
-
return { error: `Record with id ${recordId} not found` };
|
|
307
|
-
}
|
|
308
|
-
attachmentFiles = yield this.options.generation.attachFiles({ record, adminUser });
|
|
309
|
-
// if files is not array, make it array
|
|
310
|
-
if (!Array.isArray(attachmentFiles)) {
|
|
311
|
-
attachmentFiles = [attachmentFiles];
|
|
312
|
-
}
|
|
370
|
+
const job = jobs.get(jobId);
|
|
371
|
+
if (!job) {
|
|
372
|
+
return { error: "Job not found" };
|
|
313
373
|
}
|
|
314
|
-
|
|
315
|
-
const STUB_MODE = false;
|
|
316
|
-
const images = yield Promise.all((new Array(this.options.generation.countToGenerate)).fill(0).map(() => __awaiter(this, void 0, void 0, function* () {
|
|
317
|
-
if (STUB_MODE) {
|
|
318
|
-
yield new Promise((resolve) => setTimeout(resolve, 2000));
|
|
319
|
-
return `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
|
|
320
|
-
}
|
|
321
|
-
const start = +new Date();
|
|
322
|
-
let resp;
|
|
323
|
-
try {
|
|
324
|
-
resp = yield this.options.generation.adapter.generate({
|
|
325
|
-
prompt,
|
|
326
|
-
inputFiles: attachmentFiles,
|
|
327
|
-
n: 1,
|
|
328
|
-
size: this.options.generation.outputSize,
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
catch (e) {
|
|
332
|
-
error = `No response from image generation provider: ${e.message}. Please check your prompt or try again later.`;
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
|
-
if (resp.error) {
|
|
336
|
-
console.error('Error generating image', resp.error);
|
|
337
|
-
error = resp.error;
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
this.totalCalls++;
|
|
341
|
-
this.totalDuration += (+new Date() - start) / 1000;
|
|
342
|
-
return resp.imageURLs[0];
|
|
343
|
-
})));
|
|
344
|
-
return { error, images };
|
|
374
|
+
return { ok: true, job };
|
|
345
375
|
})
|
|
346
376
|
});
|
|
347
377
|
server.endpoint({
|
package/index.ts
CHANGED
|
@@ -3,11 +3,12 @@ import { PluginOptions } from './types.js';
|
|
|
3
3
|
import { AdminForthPlugin, AdminForthResourceColumn, AdminForthResource, Filters, IAdminForth, IHttpServer, suggestIfTypo } from "adminforth";
|
|
4
4
|
import { Readable } from "stream";
|
|
5
5
|
import { RateLimiter } from "adminforth";
|
|
6
|
+
import { randomUUID } from "crypto";
|
|
6
7
|
import { interpretResource } from 'adminforth';
|
|
7
8
|
import { ActionCheckSource } from 'adminforth';
|
|
8
9
|
|
|
9
10
|
const ADMINFORTH_NOT_YET_USED_TAG = 'adminforth-candidate-for-cleanup';
|
|
10
|
-
|
|
11
|
+
const jobs = new Map();
|
|
11
12
|
export default class UploadPlugin extends AdminForthPlugin {
|
|
12
13
|
options: PluginOptions;
|
|
13
14
|
|
|
@@ -28,10 +29,87 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
28
29
|
this.totalCalls = 0;
|
|
29
30
|
this.totalDuration = 0;
|
|
30
31
|
if (this.options.generation?.rateLimit?.limit) {
|
|
31
|
-
|
|
32
|
+
this.rateLimiter = new RateLimiter(this.options.generation.rateLimit?.limit)
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
private async generateImages(jobId: string, prompt: string, recordId: any, adminUser: any, headers: any) {
|
|
37
|
+
if (this.options.generation.rateLimit?.limit) {
|
|
38
|
+
// rate limit
|
|
39
|
+
// const { error } = RateLimiter.checkRateLimit(
|
|
40
|
+
// this.pluginInstanceId,
|
|
41
|
+
// this.options.generation.rateLimit?.limit,
|
|
42
|
+
// this.adminforth.auth.getClientIp(headers),
|
|
43
|
+
// );
|
|
44
|
+
if (!await this.rateLimiter.consume(`${this.pluginInstanceId}-${this.adminforth.auth.getClientIp(headers)}`)) {
|
|
45
|
+
jobs.set(jobId, { status: "failed", error: this.options.generation.rateLimit.errorMessage });
|
|
46
|
+
return { error: this.options.generation.rateLimit.errorMessage };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
let attachmentFiles = [];
|
|
50
|
+
if (this.options.generation.attachFiles) {
|
|
51
|
+
// TODO - does it require additional allowed action to check this record id has access to get the image?
|
|
52
|
+
// or should we mention in docs that user should do validation in method itself
|
|
53
|
+
const record = await this.adminforth.resource(this.resourceConfig.resourceId).get(
|
|
54
|
+
[Filters.EQ(this.resourceConfig.columns.find(c => c.primaryKey)?.name, recordId)]
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if (!record) {
|
|
59
|
+
return { error: `Record with id ${recordId} not found` };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
attachmentFiles = await this.options.generation.attachFiles({ record, adminUser });
|
|
63
|
+
// if files is not array, make it array
|
|
64
|
+
if (!Array.isArray(attachmentFiles)) {
|
|
65
|
+
attachmentFiles = [attachmentFiles];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let error: string | undefined = undefined;
|
|
71
|
+
|
|
72
|
+
const STUB_MODE = false;
|
|
73
|
+
|
|
74
|
+
const images = await Promise.all(
|
|
75
|
+
(new Array(this.options.generation.countToGenerate)).fill(0).map(async () => {
|
|
76
|
+
if (STUB_MODE) {
|
|
77
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
78
|
+
return `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
|
|
79
|
+
}
|
|
80
|
+
const start = +new Date();
|
|
81
|
+
let resp;
|
|
82
|
+
try {
|
|
83
|
+
resp = await this.options.generation.adapter.generate(
|
|
84
|
+
{
|
|
85
|
+
prompt,
|
|
86
|
+
inputFiles: attachmentFiles,
|
|
87
|
+
n: 1,
|
|
88
|
+
size: this.options.generation.outputSize,
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
} catch (e: any) {
|
|
92
|
+
error = `No response from image generation provider: ${e.message}. Please check your prompt or try again later.`;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (resp.error) {
|
|
97
|
+
console.error('Error generating image', resp.error);
|
|
98
|
+
error = resp.error;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
this.totalCalls++;
|
|
103
|
+
this.totalDuration += (+new Date() - start) / 1000;
|
|
104
|
+
|
|
105
|
+
return resp.imageURLs[0]
|
|
106
|
+
|
|
107
|
+
})
|
|
108
|
+
);
|
|
109
|
+
jobs.set(jobId, { status: "completed", images, error });
|
|
110
|
+
return { ok: true };
|
|
111
|
+
};
|
|
112
|
+
|
|
35
113
|
instanceUniqueRepresentation(pluginOptions: any) : string {
|
|
36
114
|
return `${pluginOptions.pathColumnName}`;
|
|
37
115
|
}
|
|
@@ -315,81 +393,34 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
315
393
|
|
|
316
394
|
server.endpoint({
|
|
317
395
|
method: 'POST',
|
|
318
|
-
path: `/plugin/${this.pluginInstanceId}/
|
|
396
|
+
path: `/plugin/${this.pluginInstanceId}/create-image-generation-job`,
|
|
319
397
|
handler: async ({ body, adminUser, headers }) => {
|
|
320
398
|
const { prompt, recordId } = body;
|
|
321
|
-
if (this.rateLimiter) {
|
|
322
|
-
// rate limit
|
|
323
|
-
// const { error } = RateLimiter.checkRateLimit(
|
|
324
|
-
// this.pluginInstanceId,
|
|
325
|
-
// this.options.generation.rateLimit?.limit,
|
|
326
|
-
// this.adminforth.auth.getClientIp(headers),
|
|
327
|
-
// );
|
|
328
|
-
if (!await this.rateLimiter.consume(`${this.pluginInstanceId}-${this.adminforth.auth.getClientIp(headers)}`)) {
|
|
329
|
-
return { error: this.options.generation.rateLimit.errorMessage };
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
let attachmentFiles = [];
|
|
333
|
-
if (this.options.generation.attachFiles) {
|
|
334
|
-
// TODO - does it require additional allowed action to check this record id has access to get the image?
|
|
335
|
-
// or should we mention in docs that user should do validation in method itself
|
|
336
|
-
const record = await this.adminforth.resource(this.resourceConfig.resourceId).get(
|
|
337
|
-
[Filters.EQ(this.resourceConfig.columns.find((column: any) => column.primaryKey)?.name, recordId)]
|
|
338
|
-
);
|
|
339
|
-
|
|
340
|
-
if (!record) {
|
|
341
|
-
return { error: `Record with id ${recordId} not found` };
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
attachmentFiles = await this.options.generation.attachFiles({ record, adminUser });
|
|
345
|
-
// if files is not array, make it array
|
|
346
|
-
if (!Array.isArray(attachmentFiles)) {
|
|
347
|
-
attachmentFiles = [attachmentFiles];
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
let error: string | undefined = undefined;
|
|
353
|
-
|
|
354
|
-
const STUB_MODE = false;
|
|
355
|
-
|
|
356
|
-
const images = await Promise.all(
|
|
357
|
-
(new Array(this.options.generation.countToGenerate)).fill(0).map(async () => {
|
|
358
|
-
if (STUB_MODE) {
|
|
359
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
360
|
-
return `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
|
|
361
|
-
}
|
|
362
|
-
const start = +new Date();
|
|
363
|
-
let resp;
|
|
364
|
-
try {
|
|
365
|
-
resp = await this.options.generation.adapter.generate(
|
|
366
|
-
{
|
|
367
|
-
prompt,
|
|
368
|
-
inputFiles: attachmentFiles,
|
|
369
|
-
n: 1,
|
|
370
|
-
size: this.options.generation.outputSize,
|
|
371
|
-
}
|
|
372
|
-
)
|
|
373
|
-
} catch (e: any) {
|
|
374
|
-
error = `No response from image generation provider: ${e.message}. Please check your prompt or try again later.`;
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
399
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
error = resp.error;
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
400
|
+
const jobId = randomUUID();
|
|
401
|
+
jobs.set(jobId, { status: "in_progress" });
|
|
383
402
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
return resp.imageURLs[0]
|
|
403
|
+
this.generateImages(jobId, prompt, recordId, adminUser, headers);
|
|
404
|
+
setTimeout(() => jobs.delete(jobId), 1_800_000);
|
|
405
|
+
setTimeout(() => {jobs.set(jobId, { status: "timeout" });}, 300_000);
|
|
388
406
|
|
|
389
|
-
|
|
390
|
-
|
|
407
|
+
return { ok: true, jobId };
|
|
408
|
+
}
|
|
409
|
+
});
|
|
391
410
|
|
|
392
|
-
|
|
411
|
+
server.endpoint({
|
|
412
|
+
method: 'POST',
|
|
413
|
+
path: `/plugin/${this.pluginInstanceId}/get-image-generation-job-status`,
|
|
414
|
+
handler: async ({ body, adminUser, headers }) => {
|
|
415
|
+
const jobId = body.jobId;
|
|
416
|
+
if (!jobId) {
|
|
417
|
+
return { error: "Can't find job id" };
|
|
418
|
+
}
|
|
419
|
+
const job = jobs.get(jobId);
|
|
420
|
+
if (!job) {
|
|
421
|
+
return { error: "Job not found" };
|
|
422
|
+
}
|
|
423
|
+
return { ok: true, job };
|
|
393
424
|
}
|
|
394
425
|
});
|
|
395
426
|
|
|
@@ -451,5 +482,6 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
451
482
|
});
|
|
452
483
|
|
|
453
484
|
}
|
|
485
|
+
|
|
454
486
|
|
|
455
487
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adminforth/upload",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "Plugin for uploading files for adminforth",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@aws-sdk/client-s3": "^3.629.0",
|
|
24
24
|
"@aws-sdk/s3-request-presigner": "^3.629.0",
|
|
25
|
-
"adminforth": "^2.
|
|
25
|
+
"adminforth": "^2.13.0-next.51"
|
|
26
26
|
},
|
|
27
27
|
"keywords": [
|
|
28
28
|
"adminforth",
|