@adminforth/import-export 1.4.27 → 1.5.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.
package/build.log CHANGED
@@ -11,5 +11,5 @@ custom/package.json
11
11
  custom/pnpm-lock.yaml
12
12
  custom/tsconfig.json
13
13
 
14
- sent 17,871 bytes received 134 bytes 36,010.00 bytes/sec
14
+ sent 17,878 bytes received 134 bytes 36,024.00 bytes/sec
15
15
  total size is 17,389 speedup is 0.97
package/dist/index.d.ts CHANGED
@@ -6,8 +6,11 @@ export default class ImportExport extends AdminForthPlugin {
6
6
  emailField: AdminForthResourceColumn;
7
7
  authResourceId: string;
8
8
  adminforth: IAdminForth;
9
+ auditLogPlugin: Record<string, any> | undefined;
9
10
  constructor(options: PluginOptions);
10
11
  private isRowValid;
12
+ private tryToAuditLogAction;
13
+ private ensureAnyAllowed;
11
14
  modifyResourceConfig(adminforth: IAdminForth, resourceConfig: AdminForthResource): Promise<void>;
12
15
  validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource): void;
13
16
  instanceUniqueRepresentation(pluginOptions: any): string;
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { AdminForthPlugin, suggestIfTypo, AdminForthFilterOperators, Filters, AdminForthDataTypes, rejectApiRawFilters } from "adminforth";
10
+ import { AdminForthPlugin, suggestIfTypo, AdminForthFilterOperators, Filters, AdminForthDataTypes, rejectApiRawFilters, interpretResource, ActionCheckSource, AllowedActionsEnum } from "adminforth";
11
11
  import pLimit from 'p-limit';
12
12
  export default class ImportExport extends AdminForthPlugin {
13
13
  constructor(options) {
@@ -31,6 +31,42 @@ export default class ImportExport extends AdminForthPlugin {
31
31
  }
32
32
  return errors;
33
33
  }
34
+ tryToAuditLogAction(actionName, actionDetails, adminUser, headers) {
35
+ if (!this.auditLogPlugin) {
36
+ console.warn('AuditLogPlugin not found, skipping audit log for action:', actionDetails);
37
+ return;
38
+ }
39
+ try {
40
+ this.auditLogPlugin.logCustomAction({
41
+ resourceId: this.resourceConfig.resourceId,
42
+ recordId: null,
43
+ actionId: actionName,
44
+ oldData: null,
45
+ data: {
46
+ details: actionDetails,
47
+ },
48
+ user: adminUser,
49
+ headers: headers || {},
50
+ });
51
+ }
52
+ catch (e) {
53
+ console.error('Failed to log action to AuditLogPlugin:', e);
54
+ }
55
+ }
56
+ ensureAnyAllowed(adminUser_1, checks_1) {
57
+ return __awaiter(this, arguments, void 0, function* (adminUser, checks, meta = {}) {
58
+ for (const { source, action } of checks) {
59
+ const { allowedActions } = yield interpretResource(adminUser, this.resourceConfig, meta, source, this.adminforth);
60
+ if (allowedActions[action] === true) {
61
+ return { ok: true };
62
+ }
63
+ }
64
+ return {
65
+ ok: false,
66
+ error: 'Action is not allowed',
67
+ };
68
+ });
69
+ }
34
70
  modifyResourceConfig(adminforth, resourceConfig) {
35
71
  const _super = Object.create(null, {
36
72
  modifyResourceConfig: { get: () => super.modifyResourceConfig }
@@ -61,6 +97,12 @@ export default class ImportExport extends AdminForthPlugin {
61
97
  }
62
98
  validateConfigAfterDiscover(adminforth, resourceConfig) {
63
99
  // optional method where you can safely check field types after database discovery was performed
100
+ try {
101
+ this.auditLogPlugin = this.adminforth.getPluginByClassName('AuditLogPlugin');
102
+ }
103
+ catch (e) {
104
+ console.warn('Failed to get AuditLogPlugin for imort-export plugin. Audit logging will be skipped.');
105
+ }
64
106
  }
65
107
  instanceUniqueRepresentation(pluginOptions) {
66
108
  // optional method to return unique string representation of plugin instance.
@@ -71,8 +113,15 @@ export default class ImportExport extends AdminForthPlugin {
71
113
  server.endpoint({
72
114
  method: 'POST',
73
115
  path: `/plugin/${this.pluginInstanceId}/export-csv`,
74
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body }) {
116
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers }) {
75
117
  const { filters, sort } = body;
118
+ const access = yield this.ensureAnyAllowed(adminUser, [
119
+ { source: ActionCheckSource.ListRequest, action: AllowedActionsEnum.list },
120
+ { source: ActionCheckSource.ShowRequest, action: AllowedActionsEnum.show },
121
+ ], { requestBody: body });
122
+ if (!access.ok) {
123
+ return { ok: false, error: access.error };
124
+ }
76
125
  const rawFilterError = rejectApiRawFilters(body.filters);
77
126
  if (rawFilterError) {
78
127
  return rawFilterError;
@@ -96,6 +145,7 @@ export default class ImportExport extends AdminForthPlugin {
96
145
  const rows = data.data.map((row) => {
97
146
  return columns.map((col) => row[col.name]);
98
147
  });
148
+ this.tryToAuditLogAction('export', `Export CSV with filters: ${JSON.stringify(filters)} and sort: ${JSON.stringify(sort)}. Total records: ${rows.length}`, adminUser, headers);
99
149
  return {
100
150
  data: { fields, data: rows },
101
151
  columnsToForceQuote,
@@ -109,6 +159,13 @@ export default class ImportExport extends AdminForthPlugin {
109
159
  path: `/plugin/${this.pluginInstanceId}/import-csv`,
110
160
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, query, headers, cookies, requestUrl, response }) {
111
161
  const { data } = body;
162
+ const createEditAccess = yield this.ensureAnyAllowed(adminUser, [
163
+ { source: ActionCheckSource.CreateRequest, action: AllowedActionsEnum.create },
164
+ { source: ActionCheckSource.EditRequest, action: AllowedActionsEnum.edit }
165
+ ], { requestBody: body });
166
+ if (!createEditAccess.ok) {
167
+ return { ok: false, error: createEditAccess.error };
168
+ }
112
169
  const columns = this.getColumnNames(data);
113
170
  const { errors, resourceColumns } = this.validateColumns(columns);
114
171
  const resource = this.adminforth.config.resources.find(r => r.resourceId === this.resourceConfig.resourceId);
@@ -118,6 +175,7 @@ export default class ImportExport extends AdminForthPlugin {
118
175
  const primaryKeyColumn = this.resourceConfig.columns.find(col => col.primaryKey);
119
176
  const rows = this.buildRowsFromData(data, columns, resourceColumns, { coerceTypes: true });
120
177
  console.log('Prepared rows for import:', rows);
178
+ this.tryToAuditLogAction('import', `Import CSV with ${Object.keys(data).length} columns`, adminUser, headers);
121
179
  let importedCount = 0;
122
180
  let updatedCount = 0;
123
181
  const limit = pLimit(100);
@@ -170,6 +228,10 @@ export default class ImportExport extends AdminForthPlugin {
170
228
  path: `/plugin/${this.pluginInstanceId}/import-csv-new-only`,
171
229
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, query, headers, cookies, requestUrl, response }) {
172
230
  const { data } = body;
231
+ const access = yield this.ensureAnyAllowed(adminUser, [{ source: ActionCheckSource.CreateRequest, action: AllowedActionsEnum.create }], { requestBody: body });
232
+ if (!access.ok) {
233
+ return { ok: false, error: access.error };
234
+ }
173
235
  const columns = this.getColumnNames(data);
174
236
  const resource = this.adminforth.config.resources.find(r => r.resourceId === this.resourceConfig.resourceId);
175
237
  const { errors, resourceColumns } = this.validateColumns(columns);
@@ -178,6 +240,7 @@ export default class ImportExport extends AdminForthPlugin {
178
240
  }
179
241
  const primaryKeyColumn = this.resourceConfig.columns.find(col => col.primaryKey);
180
242
  const rows = this.buildRowsFromData(data, columns, resourceColumns, { coerceTypes: true });
243
+ this.tryToAuditLogAction('import', `Import CSV (new only) with ${Object.keys(data).length} columns`, adminUser, headers);
181
244
  let importedCount = 0;
182
245
  const limit = pLimit(100);
183
246
  yield Promise.all(rows.map((row) => limit(() => __awaiter(this, void 0, void 0, function* () {
@@ -212,7 +275,14 @@ export default class ImportExport extends AdminForthPlugin {
212
275
  server.endpoint({
213
276
  method: 'POST',
214
277
  path: `/plugin/${this.pluginInstanceId}/check-records`,
215
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body }) {
278
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser }) {
279
+ const access = yield this.ensureAnyAllowed(adminUser, [
280
+ { source: ActionCheckSource.ListRequest, action: AllowedActionsEnum.list },
281
+ { source: ActionCheckSource.ShowRequest, action: AllowedActionsEnum.show },
282
+ ], { requestBody: body });
283
+ if (!access.ok) {
284
+ return { ok: false, error: access.error };
285
+ }
216
286
  const { data } = body;
217
287
  const primaryKeyColumn = this.resourceConfig.columns.find(col => col.primaryKey);
218
288
  const columns = this.getColumnNames(data);
package/index.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { AdminForthPlugin, suggestIfTypo, AdminForthFilterOperators, Filters, AdminForthDataTypes, rejectApiRawFilters } from "adminforth";
2
- import type { IAdminForth, IHttpServer, AdminForthResourceColumn, AdminForthComponentDeclaration, AdminForthResource } from "adminforth";
1
+ import { AdminForthPlugin, suggestIfTypo, AdminForthFilterOperators, Filters, AdminForthDataTypes, rejectApiRawFilters, interpretResource, ActionCheckSource, AllowedActionsEnum } from "adminforth";
2
+ import type { IAdminForth, IHttpServer, AdminForthResourceColumn, AdminForthComponentDeclaration, AdminForthResource, AdminUser } from "adminforth";
3
3
  import type { PluginOptions } from './types.js';
4
4
  import pLimit from 'p-limit';
5
5
 
@@ -8,6 +8,7 @@ export default class ImportExport extends AdminForthPlugin {
8
8
  emailField: AdminForthResourceColumn;
9
9
  authResourceId: string;
10
10
  adminforth: IAdminForth;
11
+ auditLogPlugin: Record<string, any> | undefined;
11
12
 
12
13
 
13
14
  constructor(options: PluginOptions) {
@@ -33,6 +34,54 @@ export default class ImportExport extends AdminForthPlugin {
33
34
  return errors;
34
35
  }
35
36
 
37
+ private tryToAuditLogAction(actionName: 'import' | 'export', actionDetails: string, adminUser: AdminUser, headers?: Record<string, string> ) {
38
+ if (!this.auditLogPlugin) {
39
+ console.warn('AuditLogPlugin not found, skipping audit log for action:', actionDetails);
40
+ return;
41
+ }
42
+ try {
43
+ this.auditLogPlugin.logCustomAction({
44
+ resourceId: this.resourceConfig.resourceId,
45
+ recordId: null,
46
+ actionId: actionName,
47
+ oldData: null,
48
+ data: {
49
+ details: actionDetails,
50
+
51
+ },
52
+ user: adminUser,
53
+ headers: headers || {},
54
+ });
55
+ } catch (e) {
56
+ console.error('Failed to log action to AuditLogPlugin:', e);
57
+ }
58
+ }
59
+
60
+ private async ensureAnyAllowed(
61
+ adminUser: AdminUser,
62
+ checks: { source: ActionCheckSource; action: AllowedActionsEnum }[],
63
+ meta: Record<string, unknown> = {}
64
+ ): Promise<{ ok: boolean; error?: string }> {
65
+ for (const { source, action } of checks) {
66
+ const { allowedActions } = await interpretResource(
67
+ adminUser,
68
+ this.resourceConfig,
69
+ meta,
70
+ source,
71
+ this.adminforth
72
+ );
73
+
74
+ if (allowedActions[action] === true) {
75
+ return { ok: true };
76
+ }
77
+ }
78
+
79
+ return {
80
+ ok: false,
81
+ error: 'Action is not allowed',
82
+ };
83
+ }
84
+
36
85
  async modifyResourceConfig(adminforth: IAdminForth, resourceConfig: AdminForthResource) {
37
86
  super.modifyResourceConfig(adminforth, resourceConfig);
38
87
  if (!resourceConfig.options.pageInjections) {
@@ -61,6 +110,11 @@ export default class ImportExport extends AdminForthPlugin {
61
110
 
62
111
  validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource) {
63
112
  // optional method where you can safely check field types after database discovery was performed
113
+ try {
114
+ this.auditLogPlugin = this.adminforth.getPluginByClassName('AuditLogPlugin');
115
+ } catch (e) {
116
+ console.warn('Failed to get AuditLogPlugin for imort-export plugin. Audit logging will be skipped.');
117
+ }
64
118
  }
65
119
 
66
120
  instanceUniqueRepresentation(pluginOptions: any) : string {
@@ -73,8 +127,19 @@ export default class ImportExport extends AdminForthPlugin {
73
127
  server.endpoint({
74
128
  method: 'POST',
75
129
  path: `/plugin/${this.pluginInstanceId}/export-csv`,
76
- handler: async ({ body }) => {
130
+ handler: async ({ body, adminUser, headers }) => {
77
131
  const { filters, sort } = body;
132
+ const access = await this.ensureAnyAllowed(
133
+ adminUser,
134
+ [
135
+ { source: ActionCheckSource.ListRequest, action: AllowedActionsEnum.list },
136
+ { source: ActionCheckSource.ShowRequest, action: AllowedActionsEnum.show },
137
+ ],
138
+ { requestBody: body }
139
+ );
140
+ if (!access.ok) {
141
+ return { ok: false, error: access.error };
142
+ }
78
143
  const rawFilterError = rejectApiRawFilters(body.filters);
79
144
  if (rawFilterError) {
80
145
  return rawFilterError;
@@ -103,6 +168,8 @@ export default class ImportExport extends AdminForthPlugin {
103
168
  return columns.map((col) => row[col.name]);
104
169
  });
105
170
 
171
+ this.tryToAuditLogAction('export', `Export CSV with filters: ${JSON.stringify(filters)} and sort: ${JSON.stringify(sort)}. Total records: ${rows.length}`, adminUser, headers);
172
+
106
173
  return {
107
174
  data: { fields, data: rows },
108
175
  columnsToForceQuote,
@@ -117,6 +184,17 @@ export default class ImportExport extends AdminForthPlugin {
117
184
  path: `/plugin/${this.pluginInstanceId}/import-csv`,
118
185
  handler: async ({ body, adminUser, query, headers, cookies, requestUrl, response }) => {
119
186
  const { data } = body;
187
+ const createEditAccess = await this.ensureAnyAllowed(
188
+ adminUser,
189
+ [
190
+ { source: ActionCheckSource.CreateRequest, action: AllowedActionsEnum.create },
191
+ { source: ActionCheckSource.EditRequest, action: AllowedActionsEnum.edit }
192
+ ],
193
+ { requestBody: body }
194
+ );
195
+ if (!createEditAccess.ok) {
196
+ return { ok: false, error: createEditAccess.error };
197
+ }
120
198
  const columns = this.getColumnNames(data);
121
199
  const { errors, resourceColumns } = this.validateColumns(columns);
122
200
  const resource = this.adminforth.config.resources.find(r => r.resourceId === this.resourceConfig.resourceId);
@@ -128,6 +206,7 @@ export default class ImportExport extends AdminForthPlugin {
128
206
  const rows = this.buildRowsFromData(data, columns, resourceColumns, { coerceTypes: true });
129
207
 
130
208
  console.log('Prepared rows for import:', rows);
209
+ this.tryToAuditLogAction('import', `Import CSV with ${Object.keys(data).length} columns`, adminUser, headers);
131
210
 
132
211
  let importedCount = 0;
133
212
  let updatedCount = 0;
@@ -184,6 +263,14 @@ export default class ImportExport extends AdminForthPlugin {
184
263
  path: `/plugin/${this.pluginInstanceId}/import-csv-new-only`,
185
264
  handler: async ({ body, adminUser, query, headers, cookies, requestUrl, response }) => {
186
265
  const { data } = body;
266
+ const access = await this.ensureAnyAllowed(
267
+ adminUser,
268
+ [{ source: ActionCheckSource.CreateRequest, action: AllowedActionsEnum.create }],
269
+ { requestBody: body }
270
+ );
271
+ if (!access.ok) {
272
+ return { ok: false, error: access.error };
273
+ }
187
274
  const columns = this.getColumnNames(data);
188
275
  const resource = this.adminforth.config.resources.find(r => r.resourceId === this.resourceConfig.resourceId);
189
276
  const { errors, resourceColumns } = this.validateColumns(columns);
@@ -193,6 +280,7 @@ export default class ImportExport extends AdminForthPlugin {
193
280
 
194
281
  const primaryKeyColumn = this.resourceConfig.columns.find(col => col.primaryKey);
195
282
  const rows = this.buildRowsFromData(data, columns, resourceColumns, { coerceTypes: true });
283
+ this.tryToAuditLogAction('import', `Import CSV (new only) with ${Object.keys(data).length} columns`, adminUser, headers);
196
284
 
197
285
  let importedCount = 0;
198
286
  const limit = pLimit(100);
@@ -231,7 +319,18 @@ export default class ImportExport extends AdminForthPlugin {
231
319
  server.endpoint({
232
320
  method: 'POST',
233
321
  path: `/plugin/${this.pluginInstanceId}/check-records`,
234
- handler: async ({ body }) => {
322
+ handler: async ({ body, adminUser }) => {
323
+ const access = await this.ensureAnyAllowed(
324
+ adminUser,
325
+ [
326
+ { source: ActionCheckSource.ListRequest, action: AllowedActionsEnum.list },
327
+ { source: ActionCheckSource.ShowRequest, action: AllowedActionsEnum.show },
328
+ ],
329
+ { requestBody: body }
330
+ );
331
+ if (!access.ok) {
332
+ return { ok: false, error: access.error };
333
+ }
235
334
  const { data } = body as { data: Record<string, unknown[]> };
236
335
  const primaryKeyColumn = this.resourceConfig.columns.find(col => col.primaryKey);
237
336
  const columns = this.getColumnNames(data);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/import-export",
3
- "version": "1.4.27",
3
+ "version": "1.5.1",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",