@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 +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +73 -3
- package/index.ts +103 -4
- package/package.json +1 -1
package/build.log
CHANGED
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);
|