@adminforth/bulk-ai-flow 1.26.10 → 1.27.1

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 (3) hide show
  1. package/dist/index.js +91 -30
  2. package/index.ts +105 -33
  3. package/package.json +5 -4
package/dist/index.js CHANGED
@@ -12,6 +12,49 @@ import { suggestIfTypo, filtersTools } from "adminforth";
12
12
  import Handlebars from 'handlebars';
13
13
  import { RateLimiter } from "adminforth";
14
14
  import { randomUUID } from "crypto";
15
+ import { z } from "zod";
16
+ const getRecordsBodySchema = z.object({
17
+ record: z.array(z.any()).nullish(),
18
+ }).strict();
19
+ const getImagesBodySchema = z.object({
20
+ record: z.array(z.any()).nullish(),
21
+ }).strict();
22
+ const getOldDataBodySchema = z.object({
23
+ recordId: z.union([z.string(), z.number()]).nullish(),
24
+ }).strict();
25
+ const updateFieldsBodySchema = z.object({
26
+ selectedIds: z.array(z.any()).nullish(),
27
+ fields: z.any(),
28
+ saveImages: z.boolean().nullish(),
29
+ }).strict();
30
+ const getImageGenerationPromptsBodySchema = z.object({
31
+ recordId: z.union([z.string(), z.number()]).nullish(),
32
+ customPrompt: z.string().nullish(),
33
+ }).strict();
34
+ const createJobBodySchema = z.object({
35
+ actionType: z.string().nullish(),
36
+ recordId: z.union([z.string(), z.number()]).nullish(),
37
+ customPrompt: z.string().nullish(),
38
+ filterFilledFields: z.boolean().nullish(),
39
+ sessionIds: z.array(z.string()).nullish(),
40
+ prompt: z.string().nullish(),
41
+ fieldName: z.string().nullish(),
42
+ fieldToRegenerate: z.string().nullish(),
43
+ action: z.string().nullish(),
44
+ }).strict();
45
+ const jobStatusBodySchema = z.object({
46
+ jobId: z.string(),
47
+ }).strict();
48
+ const updateRateLimitsBodySchema = z.object({
49
+ actionType: z.string().nullish(),
50
+ }).strict();
51
+ const compileOldImageLinkBodySchema = z.object({
52
+ image: z.string().nullish(),
53
+ columnName: z.string().nullish(),
54
+ }).strict();
55
+ const getFilteredIdsBodySchema = z.object({
56
+ filters: z.any(),
57
+ }).passthrough();
15
58
  const STUB_MODE = false;
16
59
  const jobs = new Map();
17
60
  export default class BulkAiFlowPlugin extends AdminForthPlugin {
@@ -771,18 +814,19 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
771
814
  server.endpoint({
772
815
  method: 'POST',
773
816
  path: `/plugin/${this.pluginInstanceId}/get_records`,
774
- handler: (body) => __awaiter(this, void 0, void 0, function* () {
775
- var _a;
776
- if (!Array.isArray((_a = body.body) === null || _a === void 0 ? void 0 : _a.record)) {
817
+ request_schema: getRecordsBodySchema,
818
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, response }) {
819
+ const data = body;
820
+ if (!Array.isArray(data.record)) {
777
821
  return { records: [] };
778
822
  }
779
823
  let records = [];
780
824
  const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
781
- records = yield this.adminforth.resource(this.resourceConfig.resourceId).list([Filters.IN(primaryKeyColumn.name, body.body.record)]);
825
+ records = yield this.adminforth.resource(this.resourceConfig.resourceId).list([Filters.IN(primaryKeyColumn.name, data.record)]);
782
826
  for (const [index, record] of records.entries()) {
783
827
  records[index]._label = this.resourceConfig.recordLabel(records[index]);
784
828
  }
785
- const order = Object.fromEntries(body.body.record.map((id, i) => [id, i]));
829
+ const order = Object.fromEntries(data.record.map((id, i) => [id, i]));
786
830
  const sortedRecords = records.sort((a, b) => order[a.id] - order[b.id]);
787
831
  return {
788
832
  records: sortedRecords,
@@ -792,8 +836,10 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
792
836
  server.endpoint({
793
837
  method: 'POST',
794
838
  path: `/plugin/${this.pluginInstanceId}/get_old_data`,
795
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body }) {
796
- const recordId = body.recordId;
839
+ request_schema: getOldDataBodySchema,
840
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, response }) {
841
+ const data = body;
842
+ const recordId = data.recordId;
797
843
  if (recordId === undefined || recordId === null) {
798
844
  return { ok: false, error: "Missing recordId" };
799
845
  }
@@ -810,10 +856,12 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
810
856
  server.endpoint({
811
857
  method: 'POST',
812
858
  path: `/plugin/${this.pluginInstanceId}/get_images`,
813
- handler: (body) => __awaiter(this, void 0, void 0, function* () {
859
+ request_schema: getImagesBodySchema,
860
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, response }) {
861
+ const data = body;
814
862
  let images = [];
815
- if (body.body.record) {
816
- for (const record of body.body.record) {
863
+ if (data.record) {
864
+ for (const record of data.record) {
817
865
  if (this.options.attachFiles) {
818
866
  images.push(yield this.options.attachFiles({ record: record }));
819
867
  }
@@ -827,15 +875,17 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
827
875
  server.endpoint({
828
876
  method: 'POST',
829
877
  path: `/plugin/${this.pluginInstanceId}/update_fields`,
830
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers }) {
878
+ request_schema: updateFieldsBodySchema,
879
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers, response }) {
880
+ const data = body;
831
881
  let isAllowedToSave = { ok: true, error: '' };
832
882
  if (this.options.isAllowedToSave) {
833
883
  isAllowedToSave = yield this.options.isAllowedToSave({ record: {}, adminUser: adminUser, resource: this.resourceConfig });
834
884
  }
835
885
  if (isAllowedToSave.ok !== false) {
836
- const selectedIds = body.selectedIds || [];
837
- const fieldsToUpdate = body.fields || {};
838
- const saveImages = body.saveImages;
886
+ const selectedIds = data.selectedIds || [];
887
+ const fieldsToUpdate = data.fields || {};
888
+ const saveImages = data.saveImages;
839
889
  const outputImageFields = [];
840
890
  if (this.options.generateImages) {
841
891
  for (const [key, value] of Object.entries(this.options.generateImages)) {
@@ -928,10 +978,12 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
928
978
  server.endpoint({
929
979
  method: 'POST',
930
980
  path: `/plugin/${this.pluginInstanceId}/get_image_generation_prompts`,
931
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, headers }) {
981
+ request_schema: getImageGenerationPromptsBodySchema,
982
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, headers, response }) {
932
983
  var _b;
933
- const Id = body.recordId || [];
934
- const customPrompt = body.customPrompt || null;
984
+ const data = body;
985
+ const Id = data.recordId || [];
986
+ const customPrompt = data.customPrompt || null;
935
987
  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, Id)]);
936
988
  const compiledGenerationOptions = yield this.compileGenerationFieldTemplates(record, JSON.stringify({ "prompt": customPrompt }));
937
989
  return compiledGenerationOptions;
@@ -951,8 +1003,10 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
951
1003
  server.endpoint({
952
1004
  method: 'POST',
953
1005
  path: `/plugin/${this.pluginInstanceId}/create-job`,
954
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers }) {
955
- const { actionType, recordId, customPrompt, filterFilledFields, sessionIds } = body;
1006
+ request_schema: createJobBodySchema,
1007
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers, response }) {
1008
+ const data = body;
1009
+ const { actionType, recordId, customPrompt, filterFilledFields, sessionIds } = data;
956
1010
  if (this.options.rateLimits) {
957
1011
  if (sessionIds && sessionIds.length > 0) {
958
1012
  for (const sessionId of sessionIds) {
@@ -988,15 +1042,15 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
988
1042
  this.analyze_image(jobId, recordId, adminUser, headers, customPrompt, filterFilledFields);
989
1043
  break;
990
1044
  case 'regenerate_images':
991
- if (!body.prompt || !body.fieldName) {
1045
+ if (!data.prompt || !data.fieldName) {
992
1046
  jobs.set(jobId, { status: "failed", error: "Missing prompt or field name" });
993
1047
  break;
994
1048
  }
995
- this.regenerateImage(jobId, recordId, body.fieldName, body.prompt, adminUser, headers);
1049
+ this.regenerateImage(jobId, recordId, data.fieldName, data.prompt, adminUser, headers);
996
1050
  break;
997
1051
  case 'regenerate_cell':
998
- const fieldToRegenerate = body.fieldToRegenerate;
999
- this.regenerateCell(jobId, fieldToRegenerate, recordId, body.action, body.prompt);
1052
+ const fieldToRegenerate = data.fieldToRegenerate;
1053
+ this.regenerateCell(jobId, fieldToRegenerate, recordId, data.action, data.prompt);
1000
1054
  break;
1001
1055
  default:
1002
1056
  jobs.set(jobId, { status: "failed", error: "Unknown action type" });
@@ -1010,8 +1064,10 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
1010
1064
  server.endpoint({
1011
1065
  method: 'POST',
1012
1066
  path: `/plugin/${this.pluginInstanceId}/get-job-status`,
1067
+ request_schema: jobStatusBodySchema,
1013
1068
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers, response }) {
1014
- const jobId = body.jobId;
1069
+ const data = body;
1070
+ const jobId = data.jobId;
1015
1071
  if (!jobId) {
1016
1072
  response.setStatus(400);
1017
1073
  return {
@@ -1043,9 +1099,11 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
1043
1099
  server.endpoint({
1044
1100
  method: 'POST',
1045
1101
  path: `/plugin/${this.pluginInstanceId}/update-rate-limits`,
1046
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers }) {
1102
+ request_schema: updateRateLimitsBodySchema,
1103
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers, response }) {
1047
1104
  var _b, _c, _d;
1048
- const actionType = body.actionType;
1105
+ const data = body;
1106
+ const actionType = data.actionType;
1049
1107
  const sessionId = randomUUID();
1050
1108
  this.sessionIds.add(sessionId);
1051
1109
  if (actionType === 'analyze' && ((_b = this.options.rateLimits) === null || _b === void 0 ? void 0 : _b.fillFieldsFromImages)) {
@@ -1070,10 +1128,12 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
1070
1128
  server.endpoint({
1071
1129
  method: 'POST',
1072
1130
  path: `/plugin/${this.pluginInstanceId}/compile_old_image_link`,
1073
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers }) {
1131
+ request_schema: compileOldImageLinkBodySchema,
1132
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers, response }) {
1074
1133
  var _b, _c;
1075
- const image = body.image;
1076
- const columnName = body.columnName;
1134
+ const data = body;
1135
+ const image = data.image;
1136
+ const columnName = data.columnName;
1077
1137
  if (!image) {
1078
1138
  return { ok: false, error: "Can't find image url" };
1079
1139
  }
@@ -1099,7 +1159,8 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
1099
1159
  server.endpoint({
1100
1160
  method: 'POST',
1101
1161
  path: `/plugin/${this.pluginInstanceId}/get_filtered_ids`,
1102
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers, query, cookies, requestUrl }) {
1162
+ request_schema: getFilteredIdsBodySchema,
1163
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers, query, cookies, requestUrl, response }) {
1103
1164
  var _b, _c;
1104
1165
  const resource = this.resourceConfig;
1105
1166
  for (const hook of ((_c = (_b = resource.hooks) === null || _b === void 0 ? void 0 : _b.list) === null || _c === void 0 ? void 0 : _c.beforeDatasourceRequest) || []) {
package/index.ts CHANGED
@@ -5,6 +5,59 @@ import type { PluginOptions } from './types.js';
5
5
  import Handlebars from 'handlebars';
6
6
  import { RateLimiter } from "adminforth";
7
7
  import { randomUUID } from "crypto";
8
+ import { z } from "zod";
9
+
10
+ const getRecordsBodySchema = z.object({
11
+ record: z.array(z.any()).nullish(),
12
+ }).strict();
13
+
14
+ const getImagesBodySchema = z.object({
15
+ record: z.array(z.any()).nullish(),
16
+ }).strict();
17
+
18
+ const getOldDataBodySchema = z.object({
19
+ recordId: z.union([z.string(), z.number()]).nullish(),
20
+ }).strict();
21
+
22
+ const updateFieldsBodySchema = z.object({
23
+ selectedIds: z.array(z.any()).nullish(),
24
+ fields: z.any(),
25
+ saveImages: z.boolean().nullish(),
26
+ }).strict();
27
+
28
+ const getImageGenerationPromptsBodySchema = z.object({
29
+ recordId: z.union([z.string(), z.number()]).nullish(),
30
+ customPrompt: z.string().nullish(),
31
+ }).strict();
32
+
33
+ const createJobBodySchema = z.object({
34
+ actionType: z.string().nullish(),
35
+ recordId: z.union([z.string(), z.number()]).nullish(),
36
+ customPrompt: z.string().nullish(),
37
+ filterFilledFields: z.boolean().nullish(),
38
+ sessionIds: z.array(z.string()).nullish(),
39
+ prompt: z.string().nullish(),
40
+ fieldName: z.string().nullish(),
41
+ fieldToRegenerate: z.string().nullish(),
42
+ action: z.string().nullish(),
43
+ }).strict();
44
+
45
+ const jobStatusBodySchema = z.object({
46
+ jobId: z.string(),
47
+ }).strict();
48
+
49
+ const updateRateLimitsBodySchema = z.object({
50
+ actionType: z.string().nullish(),
51
+ }).strict();
52
+
53
+ const compileOldImageLinkBodySchema = z.object({
54
+ image: z.string().nullish(),
55
+ columnName: z.string().nullish(),
56
+ }).strict();
57
+
58
+ const getFilteredIdsBodySchema = z.object({
59
+ filters: z.any(),
60
+ }).passthrough();
8
61
  const STUB_MODE = false;
9
62
  const jobs = new Map();
10
63
  export default class BulkAiFlowPlugin extends AdminForthPlugin {
@@ -110,7 +163,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
110
163
  return prompt;
111
164
  }
112
165
 
113
- private async analyze_image(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string, filterFilledFields: boolean = true) {
166
+ private async analyze_image(jobId: string, recordId: string | number, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string, filterFilledFields: boolean = true) {
114
167
  // TODO: need to do correct check of rate limit
115
168
  // if (this.options.rateLimits && this.options.rateLimits.fillFieldsFromImages && await this.checkRateLimit("fillFieldsFromImages" ,this.options.rateLimits.fillFieldsFromImages, headers)) {
116
169
  // jobs.set(jobId, { status: 'failed', error: "Rate limit exceeded" });
@@ -195,7 +248,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
195
248
 
196
249
  }
197
250
 
198
- private async analyzeNoImages(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string, filterFilledFields: boolean = true) {
251
+ private async analyzeNoImages(jobId: string, recordId: string | number, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string, filterFilledFields: boolean = true) {
199
252
  // TODO: need to do correct check of rate limit
200
253
  // if (this.options.rateLimits && this.options.rateLimits.fillPlainFields && await this.checkRateLimit("fillPlainFields" ,this.options.rateLimits.fillPlainFields, headers)) {
201
254
  // jobs.set(jobId, { status: 'failed', error: "Rate limit exceeded" });
@@ -259,7 +312,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
259
312
  }
260
313
  }
261
314
 
262
- private async initialImageGenerate(jobId: string, recordId: string, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string, filterFilledFields: boolean = true) {
315
+ private async initialImageGenerate(jobId: string, recordId: string | number, adminUser: any, headers: Record<string, string | string[] | undefined>, customPrompt? : string, filterFilledFields: boolean = true) {
263
316
  // TODO: need to do correct check of rate limit
264
317
  // if (this.options.rateLimits && this.options.rateLimits.generateImages && await this.checkRateLimit("generateImages" ,this.options.rateLimits.generateImages, headers)) {
265
318
  // jobs.set(jobId, { status: 'failed', error: "Rate limit exceeded" });
@@ -371,7 +424,7 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
371
424
  }
372
425
  }
373
426
 
374
- private async regenerateImage(jobId: string, recordId: string, fieldName: string, prompt: string, adminUser: any, headers: Record<string, string | string[] | undefined>) {
427
+ private async regenerateImage(jobId: string, recordId: string | number, fieldName: string, prompt: string, adminUser: any, headers: Record<string, string | string[] | undefined>) {
375
428
  const Id = recordId;
376
429
  let isError = false;
377
430
  // TODO: need to do correct check of rate limit
@@ -779,17 +832,19 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
779
832
  server.endpoint({
780
833
  method: 'POST',
781
834
  path: `/plugin/${this.pluginInstanceId}/get_records`,
782
- handler: async ( body ) => {
783
- if (!Array.isArray(body.body?.record)) {
835
+ request_schema: getRecordsBodySchema,
836
+ handler: async ({ body, response }) => {
837
+ const data = body as z.infer<typeof getRecordsBodySchema>;
838
+ if (!Array.isArray(data.record)) {
784
839
  return { records: [] };
785
840
  }
786
841
  let records = [];
787
842
  const primaryKeyColumn = this.resourceConfig.columns.find((col) => col.primaryKey);
788
- records = await this.adminforth.resource(this.resourceConfig.resourceId).list([Filters.IN(primaryKeyColumn.name, body.body.record)]);
843
+ records = await this.adminforth.resource(this.resourceConfig.resourceId).list([Filters.IN(primaryKeyColumn.name, data.record)]);
789
844
  for( const [index, record] of records.entries() ) {
790
845
  records[index]._label = this.resourceConfig.recordLabel(records[index]);
791
846
  }
792
- const order = Object.fromEntries(body.body.record.map((id, i) => [id, i]));
847
+ const order = Object.fromEntries(data.record.map((id, i) => [id, i]));
793
848
 
794
849
  const sortedRecords = records.sort(
795
850
  (a, b) => order[a.id] - order[b.id]
@@ -804,8 +859,10 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
804
859
  server.endpoint({
805
860
  method: 'POST',
806
861
  path: `/plugin/${this.pluginInstanceId}/get_old_data`,
807
- handler: async ({ body }) => {
808
- const recordId = body.recordId;
862
+ request_schema: getOldDataBodySchema,
863
+ handler: async ({ body, response }) => {
864
+ const data = body as z.infer<typeof getOldDataBodySchema>;
865
+ const recordId = data.recordId;
809
866
  if (recordId === undefined || recordId === null) {
810
867
  return { ok: false, error: "Missing recordId" };
811
868
  }
@@ -824,10 +881,12 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
824
881
  server.endpoint({
825
882
  method: 'POST',
826
883
  path: `/plugin/${this.pluginInstanceId}/get_images`,
827
- handler: async ( body ) => {
884
+ request_schema: getImagesBodySchema,
885
+ handler: async ({ body, response }) => {
886
+ const data = body as z.infer<typeof getImagesBodySchema>;
828
887
  let images = [];
829
- if(body.body.record){
830
- for( const record of body.body.record ) {
888
+ if(data.record){
889
+ for( const record of data.record ) {
831
890
  if (this.options.attachFiles) {
832
891
  images.push(await this.options.attachFiles({ record: record }));
833
892
  }
@@ -843,15 +902,17 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
843
902
  server.endpoint({
844
903
  method: 'POST',
845
904
  path: `/plugin/${this.pluginInstanceId}/update_fields`,
846
- handler: async ({ body, adminUser, headers }) => {
905
+ request_schema: updateFieldsBodySchema,
906
+ handler: async ({ body, adminUser, headers, response }) => {
907
+ const data = body as z.infer<typeof updateFieldsBodySchema>;
847
908
  let isAllowedToSave: any = { ok: true, error: '' };
848
909
  if(this.options.isAllowedToSave) {
849
910
  isAllowedToSave = await this.options.isAllowedToSave({ record: {}, adminUser: adminUser, resource: this.resourceConfig });
850
911
  }
851
912
  if (isAllowedToSave.ok !== false) {
852
- const selectedIds = body.selectedIds || [];
853
- const fieldsToUpdate = body.fields || {};
854
- const saveImages = body.saveImages;
913
+ const selectedIds = data.selectedIds || [];
914
+ const fieldsToUpdate = data.fields || {};
915
+ const saveImages = data.saveImages;
855
916
  const outputImageFields = [];
856
917
  if (this.options.generateImages) {
857
918
  for (const [key, value] of Object.entries(this.options.generateImages)) {
@@ -950,9 +1011,11 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
950
1011
  server.endpoint({
951
1012
  method: 'POST',
952
1013
  path: `/plugin/${this.pluginInstanceId}/get_image_generation_prompts`,
953
- handler: async ({ body, headers }) => {
954
- const Id = body.recordId || [];
955
- const customPrompt = body.customPrompt || null;
1014
+ request_schema: getImageGenerationPromptsBodySchema,
1015
+ handler: async ({ body, headers, response }) => {
1016
+ const data = body as z.infer<typeof getImageGenerationPromptsBodySchema>;
1017
+ const Id = data.recordId || [];
1018
+ const customPrompt = data.customPrompt || null;
956
1019
  const record = await this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ(this.resourceConfig.columns.find(c => c.primaryKey)?.name, Id)]);
957
1020
  const compiledGenerationOptions = await this.compileGenerationFieldTemplates(record, JSON.stringify({"prompt": customPrompt}));
958
1021
  return compiledGenerationOptions;
@@ -976,8 +1039,10 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
976
1039
  server.endpoint({
977
1040
  method: 'POST',
978
1041
  path: `/plugin/${this.pluginInstanceId}/create-job`,
979
- handler: async ({ body, adminUser, headers }) => {
980
- const { actionType, recordId, customPrompt, filterFilledFields, sessionIds } = body;
1042
+ request_schema: createJobBodySchema,
1043
+ handler: async ({ body, adminUser, headers, response }) => {
1044
+ const data = body as z.infer<typeof createJobBodySchema>;
1045
+ const { actionType, recordId, customPrompt, filterFilledFields, sessionIds } = data;
981
1046
  if (this.options.rateLimits) {
982
1047
  if (sessionIds && sessionIds.length > 0) {
983
1048
  for (const sessionId of sessionIds) {
@@ -1011,15 +1076,15 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
1011
1076
  this.analyze_image(jobId, recordId, adminUser, headers, customPrompt, filterFilledFields);
1012
1077
  break;
1013
1078
  case 'regenerate_images':
1014
- if (!body.prompt || !body.fieldName) {
1079
+ if (!data.prompt || !data.fieldName) {
1015
1080
  jobs.set(jobId, { status: "failed", error: "Missing prompt or field name" });
1016
1081
  break;
1017
1082
  }
1018
- this.regenerateImage(jobId, recordId, body.fieldName, body.prompt, adminUser, headers);
1083
+ this.regenerateImage(jobId, recordId, data.fieldName, data.prompt, adminUser, headers);
1019
1084
  break;
1020
1085
  case 'regenerate_cell':
1021
- const fieldToRegenerate = body.fieldToRegenerate;
1022
- this.regenerateCell(jobId, fieldToRegenerate, recordId, body.action, body.prompt);
1086
+ const fieldToRegenerate = data.fieldToRegenerate;
1087
+ this.regenerateCell(jobId, fieldToRegenerate, recordId, data.action, data.prompt);
1023
1088
  break;
1024
1089
  default:
1025
1090
  jobs.set(jobId, { status: "failed", error: "Unknown action type" });
@@ -1035,8 +1100,10 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
1035
1100
  server.endpoint({
1036
1101
  method: 'POST',
1037
1102
  path: `/plugin/${this.pluginInstanceId}/get-job-status`,
1103
+ request_schema: jobStatusBodySchema,
1038
1104
  handler: async ({ body, adminUser, headers, response }) => {
1039
- const jobId = body.jobId;
1105
+ const data = body as z.infer<typeof jobStatusBodySchema>;
1106
+ const jobId = data.jobId;
1040
1107
  if (!jobId) {
1041
1108
  response.setStatus(400);
1042
1109
 
@@ -1073,8 +1140,10 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
1073
1140
  server.endpoint({
1074
1141
  method: 'POST',
1075
1142
  path: `/plugin/${this.pluginInstanceId}/update-rate-limits`,
1076
- handler: async ({ body, adminUser, headers }) => {
1077
- const actionType = body.actionType;
1143
+ request_schema: updateRateLimitsBodySchema,
1144
+ handler: async ({ body, adminUser, headers, response }) => {
1145
+ const data = body as z.infer<typeof updateRateLimitsBodySchema>;
1146
+ const actionType = data.actionType;
1078
1147
  const sessionId = randomUUID();
1079
1148
  this.sessionIds.add(sessionId);
1080
1149
  if (actionType === 'analyze' && this.options.rateLimits?.fillFieldsFromImages) {
@@ -1101,9 +1170,11 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
1101
1170
  server.endpoint({
1102
1171
  method: 'POST',
1103
1172
  path: `/plugin/${this.pluginInstanceId}/compile_old_image_link`,
1104
- handler: async ({ body, adminUser, headers }) => {
1105
- const image = body.image;
1106
- const columnName = body.columnName;
1173
+ request_schema: compileOldImageLinkBodySchema,
1174
+ handler: async ({ body, adminUser, headers, response }) => {
1175
+ const data = body as z.infer<typeof compileOldImageLinkBodySchema>;
1176
+ const image = data.image;
1177
+ const columnName = data.columnName;
1107
1178
  if (!image) {
1108
1179
  return { ok: false, error: "Can't find image url" };
1109
1180
  }
@@ -1131,7 +1202,8 @@ export default class BulkAiFlowPlugin extends AdminForthPlugin {
1131
1202
  server.endpoint({
1132
1203
  method: 'POST',
1133
1204
  path: `/plugin/${this.pluginInstanceId}/get_filtered_ids`,
1134
- handler: async ({ body, adminUser, headers, query, cookies, requestUrl }) => {
1205
+ request_schema: getFilteredIdsBodySchema,
1206
+ handler: async ({ body, adminUser, headers, query, cookies, requestUrl, response }) => {
1135
1207
  const resource = this.resourceConfig;
1136
1208
 
1137
1209
  for (const hook of resource.hooks?.list?.beforeDatasourceRequest || []) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/bulk-ai-flow",
3
- "version": "1.26.10",
3
+ "version": "1.27.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -25,17 +25,18 @@
25
25
  "description": "Bulk AI workflow plugin for AdminForth for batch field generation and image processing",
26
26
  "devDependencies": {
27
27
  "@types/node": "latest",
28
- "adminforth": "^3.6.21",
28
+ "adminforth": "^3.8.2",
29
29
  "semantic-release": "^24.2.1",
30
30
  "semantic-release-slack-bot": "^4.0.2",
31
31
  "typescript": "^5.7.3"
32
32
  },
33
33
  "dependencies": {
34
34
  "@types/handlebars": "^4.0.40",
35
- "handlebars": "^4.7.8"
35
+ "handlebars": "^4.7.8",
36
+ "zod": "^4.3.6"
36
37
  },
37
38
  "peerDependencies": {
38
- "adminforth": "^3.6.21"
39
+ "adminforth": "^3.8.2"
39
40
  },
40
41
  "release": {
41
42
  "plugins": [