@adminforth/upload 1.0.13 → 1.0.15
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/custom/preview.vue +1 -1
- package/custom/uploader.vue +12 -1
- package/dist/custom/preview.vue +1 -1
- package/dist/custom/uploader.vue +12 -1
- package/dist/index.js +92 -49
- package/index.ts +76 -43
- package/package.json +2 -2
- package/types.ts +9 -2
package/custom/preview.vue
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
</video>
|
|
19
19
|
|
|
20
20
|
<a v-else :href="url" target="_blank"
|
|
21
|
-
class="flex gap-1 items-center py-1 px-3 me-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-
|
|
21
|
+
class="flex gap-1 items-center py-1 px-3 me-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-darkListTable dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 rounded-default"
|
|
22
22
|
>
|
|
23
23
|
<!-- download file icon -->
|
|
24
24
|
<svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
package/custom/uploader.vue
CHANGED
|
@@ -171,7 +171,7 @@ const onFileChange = async (e) => {
|
|
|
171
171
|
reader.readAsDataURL(file);
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
const { uploadUrl, tagline, s3Path } = await callAdminForthApi({
|
|
174
|
+
const { uploadUrl, tagline, s3Path, error } = await callAdminForthApi({
|
|
175
175
|
path: `/plugin/${props.meta.pluginInstanceId}/get_s3_upload_url`,
|
|
176
176
|
method: 'POST',
|
|
177
177
|
body: {
|
|
@@ -182,6 +182,17 @@ const onFileChange = async (e) => {
|
|
|
182
182
|
},
|
|
183
183
|
});
|
|
184
184
|
|
|
185
|
+
if (error) {
|
|
186
|
+
window.adminforth.alert({
|
|
187
|
+
message: `File was not uploaded because of error: ${error}`,
|
|
188
|
+
variant: 'danger'
|
|
189
|
+
});
|
|
190
|
+
imgPreview.value = null;
|
|
191
|
+
uploaded.value = false;
|
|
192
|
+
progress.value = 0;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
185
196
|
const xhr = new XMLHttpRequest();
|
|
186
197
|
const success = await new Promise((resolve) => {
|
|
187
198
|
xhr.upload.onprogress = (e) => {
|
package/dist/custom/preview.vue
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
</video>
|
|
19
19
|
|
|
20
20
|
<a v-else :href="url" target="_blank"
|
|
21
|
-
class="flex gap-1 items-center py-1 px-3 me-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-
|
|
21
|
+
class="flex gap-1 items-center py-1 px-3 me-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-darkListTable dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 rounded-default"
|
|
22
22
|
>
|
|
23
23
|
<!-- download file icon -->
|
|
24
24
|
<svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
package/dist/custom/uploader.vue
CHANGED
|
@@ -171,7 +171,7 @@ const onFileChange = async (e) => {
|
|
|
171
171
|
reader.readAsDataURL(file);
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
const { uploadUrl, tagline, s3Path } = await callAdminForthApi({
|
|
174
|
+
const { uploadUrl, tagline, s3Path, error } = await callAdminForthApi({
|
|
175
175
|
path: `/plugin/${props.meta.pluginInstanceId}/get_s3_upload_url`,
|
|
176
176
|
method: 'POST',
|
|
177
177
|
body: {
|
|
@@ -182,6 +182,17 @@ const onFileChange = async (e) => {
|
|
|
182
182
|
},
|
|
183
183
|
});
|
|
184
184
|
|
|
185
|
+
if (error) {
|
|
186
|
+
window.adminforth.alert({
|
|
187
|
+
message: `File was not uploaded because of error: ${error}`,
|
|
188
|
+
variant: 'danger'
|
|
189
|
+
});
|
|
190
|
+
imgPreview.value = null;
|
|
191
|
+
uploaded.value = false;
|
|
192
|
+
progress.value = 0;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
185
196
|
const xhr = new XMLHttpRequest();
|
|
186
197
|
const success = await new Promise((resolve) => {
|
|
187
198
|
xhr.upload.onprogress = (e) => {
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
11
11
|
import { ExpirationStatus, GetObjectCommand, PutObjectCommand, S3 } from '@aws-sdk/client-s3';
|
|
12
|
-
import { AdminForthPlugin
|
|
12
|
+
import { AdminForthPlugin } from "adminforth";
|
|
13
13
|
const ADMINFORTH_NOT_YET_USED_TAG = 'adminforth-candidate-for-cleanup';
|
|
14
14
|
export default class UploadPlugin extends AdminForthPlugin {
|
|
15
15
|
constructor(options) {
|
|
@@ -95,7 +95,7 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
95
95
|
modifyResourceConfig: { get: () => super.modifyResourceConfig }
|
|
96
96
|
});
|
|
97
97
|
return __awaiter(this, void 0, void 0, function* () {
|
|
98
|
-
var _a, _b, _c;
|
|
98
|
+
var _a, _b, _c, _d, _e, _f;
|
|
99
99
|
_super.modifyResourceConfig.call(this, adminforth, resourceConfig);
|
|
100
100
|
// after column to store the path of the uploaded file, add new VirtualColumn,
|
|
101
101
|
// show only in edit and create views
|
|
@@ -109,29 +109,38 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
109
109
|
const virtualColumn = {
|
|
110
110
|
virtual: true,
|
|
111
111
|
name: `uploader_${this.pluginInstanceId}`,
|
|
112
|
-
components:
|
|
112
|
+
components: {
|
|
113
|
+
edit: {
|
|
113
114
|
file: this.componentPath('uploader.vue'),
|
|
114
115
|
meta: pluginFrontendOptions,
|
|
115
|
-
},
|
|
116
|
+
},
|
|
117
|
+
create: {
|
|
116
118
|
file: this.componentPath('uploader.vue'),
|
|
117
119
|
meta: pluginFrontendOptions,
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
} }, (((_a = this.options.preview) === null || _a === void 0 ? void 0 : _a.showInList) ? {
|
|
122
|
-
list: {
|
|
123
|
-
file: this.componentPath('preview.vue'),
|
|
124
|
-
meta: pluginFrontendOptions,
|
|
125
|
-
}
|
|
126
|
-
} : {})),
|
|
127
|
-
showIn: ['edit', 'create', 'show', ...(((_b = this.options.preview) === null || _b === void 0 ? void 0 : _b.showInList) ? [
|
|
128
|
-
AdminForthResourcePages.list
|
|
129
|
-
] : [])],
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
showIn: ['edit', 'create'],
|
|
130
123
|
};
|
|
131
124
|
const pathColumnIndex = resourceConfig.columns.findIndex((column) => column.name === pathColumnName);
|
|
132
125
|
if (pathColumnIndex === -1) {
|
|
133
126
|
throw new Error(`Column with name "${pathColumnName}" not found in resource "${resourceConfig.name}"`);
|
|
134
127
|
}
|
|
128
|
+
if (!resourceConfig.columns[pathColumnIndex].components) {
|
|
129
|
+
resourceConfig.columns[pathColumnIndex].components = {};
|
|
130
|
+
}
|
|
131
|
+
if (((_a = this.options.preview) === null || _a === void 0 ? void 0 : _a.showInList) || ((_b = this.options.preview) === null || _b === void 0 ? void 0 : _b.showInList) === undefined) {
|
|
132
|
+
// add preview column to list
|
|
133
|
+
resourceConfig.columns[pathColumnIndex].components.list = {
|
|
134
|
+
file: this.componentPath('preview.vue'),
|
|
135
|
+
meta: pluginFrontendOptions,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (((_c = this.options.preview) === null || _c === void 0 ? void 0 : _c.showInShow) || ((_d = this.options.preview) === null || _d === void 0 ? void 0 : _d.showInShow) === undefined) {
|
|
139
|
+
resourceConfig.columns[pathColumnIndex].components.show = {
|
|
140
|
+
file: this.componentPath('preview.vue'),
|
|
141
|
+
meta: pluginFrontendOptions,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
135
144
|
// insert virtual column after path column if it is not already there
|
|
136
145
|
const virtualColumnIndex = resourceConfig.columns.findIndex((column) => column.name === virtualColumn.name);
|
|
137
146
|
if (virtualColumnIndex === -1) {
|
|
@@ -147,7 +156,7 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
147
156
|
virtualColumn.editingNote = pathColumn.editingNote;
|
|
148
157
|
// ** HOOKS FOR CREATE **//
|
|
149
158
|
// add beforeSave hook to save virtual column to path column
|
|
150
|
-
resourceConfig.hooks.create.beforeSave.push((
|
|
159
|
+
resourceConfig.hooks.create.beforeSave.push((_g) => __awaiter(this, [_g], void 0, function* ({ record }) {
|
|
151
160
|
if (record[virtualColumn.name]) {
|
|
152
161
|
record[pathColumnName] = record[virtualColumn.name];
|
|
153
162
|
delete record[virtualColumn.name];
|
|
@@ -155,7 +164,8 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
155
164
|
return { ok: true };
|
|
156
165
|
}));
|
|
157
166
|
// in afterSave hook, aremove tag adminforth-not-yet-used from the file
|
|
158
|
-
resourceConfig.hooks.create.afterSave.push((
|
|
167
|
+
resourceConfig.hooks.create.afterSave.push((_h) => __awaiter(this, [_h], void 0, function* ({ record }) {
|
|
168
|
+
process.env.HEAVY_DEBUG && console.log('💾💾 after save ', record === null || record === void 0 ? void 0 : record.id);
|
|
159
169
|
if (record[pathColumnName]) {
|
|
160
170
|
const s3 = new S3({
|
|
161
171
|
credentials: {
|
|
@@ -164,6 +174,8 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
164
174
|
},
|
|
165
175
|
region: this.options.s3Region,
|
|
166
176
|
});
|
|
177
|
+
process.env.HEAVY_DEBUG && console.log('🪥🪥 remove ObjectTagging', record[pathColumnName]);
|
|
178
|
+
// let it crash if it fails: this is a new file which just was uploaded.
|
|
167
179
|
yield s3.putObjectTagging({
|
|
168
180
|
Bucket: this.options.s3Bucket,
|
|
169
181
|
Key: record[pathColumnName],
|
|
@@ -176,7 +188,7 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
176
188
|
}));
|
|
177
189
|
// ** HOOKS FOR SHOW **//
|
|
178
190
|
// add show hook to get presigned URL
|
|
179
|
-
resourceConfig.hooks.show.afterDatasourceResponse.push((
|
|
191
|
+
resourceConfig.hooks.show.afterDatasourceResponse.push((_j) => __awaiter(this, [_j], void 0, function* ({ response }) {
|
|
180
192
|
const record = response[0];
|
|
181
193
|
if (!record) {
|
|
182
194
|
return { ok: true };
|
|
@@ -194,8 +206,8 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
194
206
|
return { ok: true };
|
|
195
207
|
}));
|
|
196
208
|
// ** HOOKS FOR LIST **//
|
|
197
|
-
if ((
|
|
198
|
-
resourceConfig.hooks.list.afterDatasourceResponse.push((
|
|
209
|
+
if (((_e = this.options.preview) === null || _e === void 0 ? void 0 : _e.showInList) || ((_f = this.options.preview) === null || _f === void 0 ? void 0 : _f.showInList) === undefined) {
|
|
210
|
+
resourceConfig.hooks.list.afterDatasourceResponse.push((_k) => __awaiter(this, [_k], void 0, function* ({ response }) {
|
|
199
211
|
const s3 = new S3({
|
|
200
212
|
credentials: {
|
|
201
213
|
accessKeyId: this.options.s3AccessKeyId,
|
|
@@ -213,7 +225,7 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
213
225
|
}
|
|
214
226
|
// ** HOOKS FOR DELETE **//
|
|
215
227
|
// add delete hook which sets tag adminforth-candidate-for-cleanup to true
|
|
216
|
-
resourceConfig.hooks.delete.afterSave.push((
|
|
228
|
+
resourceConfig.hooks.delete.afterSave.push((_l) => __awaiter(this, [_l], void 0, function* ({ record }) {
|
|
217
229
|
if (record[pathColumnName]) {
|
|
218
230
|
const s3 = new S3({
|
|
219
231
|
credentials: {
|
|
@@ -222,24 +234,30 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
222
234
|
},
|
|
223
235
|
region: this.options.s3Region,
|
|
224
236
|
});
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
+
try {
|
|
238
|
+
yield s3.putObjectTagging({
|
|
239
|
+
Bucket: this.options.s3Bucket,
|
|
240
|
+
Key: record[pathColumnName],
|
|
241
|
+
Tagging: {
|
|
242
|
+
TagSet: [
|
|
243
|
+
{
|
|
244
|
+
Key: ADMINFORTH_NOT_YET_USED_TAG,
|
|
245
|
+
Value: 'true'
|
|
246
|
+
}
|
|
247
|
+
]
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
catch (e) {
|
|
252
|
+
// file might be e.g. already deleted, so we catch error
|
|
253
|
+
console.error(`Error setting tag ${ADMINFORTH_NOT_YET_USED_TAG} to true for object ${record[pathColumnName]}. File will not be auto-cleaned up`, e);
|
|
254
|
+
}
|
|
237
255
|
}
|
|
238
256
|
return { ok: true };
|
|
239
257
|
}));
|
|
240
258
|
// ** HOOKS FOR EDIT **//
|
|
241
259
|
// beforeSave
|
|
242
|
-
resourceConfig.hooks.edit.beforeSave.push((
|
|
260
|
+
resourceConfig.hooks.edit.beforeSave.push((_m) => __awaiter(this, [_m], void 0, function* ({ record }) {
|
|
243
261
|
// null is when value is removed
|
|
244
262
|
if (record[virtualColumn.name] || record[virtualColumn.name] === null) {
|
|
245
263
|
record[pathColumnName] = record[virtualColumn.name];
|
|
@@ -247,7 +265,7 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
247
265
|
return { ok: true };
|
|
248
266
|
}));
|
|
249
267
|
// add edit postSave hook to delete old file and remove tag from new file
|
|
250
|
-
resourceConfig.hooks.edit.afterSave.push((
|
|
268
|
+
resourceConfig.hooks.edit.afterSave.push((_o) => __awaiter(this, [_o], void 0, function* ({ record, oldRecord }) {
|
|
251
269
|
if (record[virtualColumn.name] || record[virtualColumn.name] === null) {
|
|
252
270
|
const s3 = new S3({
|
|
253
271
|
credentials: {
|
|
@@ -258,21 +276,28 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
258
276
|
});
|
|
259
277
|
if (oldRecord[pathColumnName]) {
|
|
260
278
|
// put tag to delete old file
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
279
|
+
try {
|
|
280
|
+
yield s3.putObjectTagging({
|
|
281
|
+
Bucket: this.options.s3Bucket,
|
|
282
|
+
Key: oldRecord[pathColumnName],
|
|
283
|
+
Tagging: {
|
|
284
|
+
TagSet: [
|
|
285
|
+
{
|
|
286
|
+
Key: ADMINFORTH_NOT_YET_USED_TAG,
|
|
287
|
+
Value: 'true'
|
|
288
|
+
}
|
|
289
|
+
]
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
catch (e) {
|
|
294
|
+
// file might be e.g. already deleted, so we catch error
|
|
295
|
+
console.error(`Error setting tag ${ADMINFORTH_NOT_YET_USED_TAG} to true for object ${oldRecord[pathColumnName]}. File will not be auto-cleaned up`, e);
|
|
296
|
+
}
|
|
273
297
|
}
|
|
274
298
|
if (record[virtualColumn.name] !== null) {
|
|
275
299
|
// remove tag from new file
|
|
300
|
+
// in this case we let it crash if it fails: this is a new file which just was uploaded.
|
|
276
301
|
yield s3.putObjectTagging({
|
|
277
302
|
Bucket: this.options.s3Bucket,
|
|
278
303
|
Key: record[pathColumnName],
|
|
@@ -295,9 +320,12 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
295
320
|
method: 'POST',
|
|
296
321
|
path: `/plugin/${this.pluginInstanceId}/get_s3_upload_url`,
|
|
297
322
|
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body }) {
|
|
323
|
+
var _b;
|
|
298
324
|
const { originalFilename, contentType, size, originalExtension } = body;
|
|
299
325
|
if (this.options.allowedFileExtensions && !this.options.allowedFileExtensions.includes(originalExtension)) {
|
|
300
|
-
|
|
326
|
+
return {
|
|
327
|
+
error: `File extension "${originalExtension}" is not allowed, allowed extensions are: ${this.options.allowedFileExtensions.join(', ')}`
|
|
328
|
+
};
|
|
301
329
|
}
|
|
302
330
|
const s3Path = this.options.s3Path({ originalFilename, originalExtension, contentType });
|
|
303
331
|
if (s3Path.startsWith('/')) {
|
|
@@ -322,10 +350,25 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
322
350
|
expiresIn: 1800,
|
|
323
351
|
unhoistableHeaders: new Set(['x-amz-tagging']),
|
|
324
352
|
});
|
|
353
|
+
let previewUrl;
|
|
354
|
+
if ((_b = this.options.preview) === null || _b === void 0 ? void 0 : _b.previewUrl) {
|
|
355
|
+
previewUrl = this.options.preview.previewUrl({ s3Path });
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
else if (this.options.s3ACL === 'public-read') {
|
|
359
|
+
previewUrl = `https://${this.options.s3Bucket}.s3.${this.options.s3Region}.amazonaws.com/${s3Path}`;
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
previewUrl = yield getSignedUrl(s3, new GetObjectCommand({
|
|
363
|
+
Bucket: this.options.s3Bucket,
|
|
364
|
+
Key: s3Path,
|
|
365
|
+
}));
|
|
366
|
+
}
|
|
325
367
|
return {
|
|
326
368
|
uploadUrl,
|
|
327
369
|
s3Path,
|
|
328
370
|
tagline,
|
|
371
|
+
previewUrl,
|
|
329
372
|
};
|
|
330
373
|
})
|
|
331
374
|
});
|
package/index.ts
CHANGED
|
@@ -115,22 +115,8 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
115
115
|
file: this.componentPath('uploader.vue'),
|
|
116
116
|
meta: pluginFrontendOptions,
|
|
117
117
|
},
|
|
118
|
-
show: {
|
|
119
|
-
file: this.componentPath('preview.vue'),
|
|
120
|
-
meta: pluginFrontendOptions,
|
|
121
|
-
},
|
|
122
|
-
...(
|
|
123
|
-
this.options.preview?.showInList ? {
|
|
124
|
-
list: {
|
|
125
|
-
file: this.componentPath('preview.vue'),
|
|
126
|
-
meta: pluginFrontendOptions,
|
|
127
|
-
}
|
|
128
|
-
} : {}
|
|
129
|
-
),
|
|
130
118
|
},
|
|
131
|
-
showIn: ['edit', 'create',
|
|
132
|
-
AdminForthResourcePages.list
|
|
133
|
-
] : [])],
|
|
119
|
+
showIn: ['edit', 'create'],
|
|
134
120
|
};
|
|
135
121
|
|
|
136
122
|
|
|
@@ -139,6 +125,24 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
139
125
|
if (pathColumnIndex === -1) {
|
|
140
126
|
throw new Error(`Column with name "${pathColumnName}" not found in resource "${resourceConfig.name}"`);
|
|
141
127
|
}
|
|
128
|
+
if (!resourceConfig.columns[pathColumnIndex].components) {
|
|
129
|
+
resourceConfig.columns[pathColumnIndex].components = {};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (this.options.preview?.showInList || this.options.preview?.showInList === undefined) {
|
|
133
|
+
// add preview column to list
|
|
134
|
+
resourceConfig.columns[pathColumnIndex].components.list = {
|
|
135
|
+
file: this.componentPath('preview.vue'),
|
|
136
|
+
meta: pluginFrontendOptions,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (this.options.preview?.showInShow || this.options.preview?.showInShow === undefined) {
|
|
141
|
+
resourceConfig.columns[pathColumnIndex].components.show = {
|
|
142
|
+
file: this.componentPath('preview.vue'),
|
|
143
|
+
meta: pluginFrontendOptions,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
142
146
|
|
|
143
147
|
// insert virtual column after path column if it is not already there
|
|
144
148
|
const virtualColumnIndex = resourceConfig.columns.findIndex((column: any) => column.name === virtualColumn.name);
|
|
@@ -169,6 +173,8 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
169
173
|
|
|
170
174
|
// in afterSave hook, aremove tag adminforth-not-yet-used from the file
|
|
171
175
|
resourceConfig.hooks.create.afterSave.push(async ({ record }: { record: any }) => {
|
|
176
|
+
process.env.HEAVY_DEBUG && console.log('💾💾 after save ', record?.id);
|
|
177
|
+
|
|
172
178
|
if (record[pathColumnName]) {
|
|
173
179
|
const s3 = new S3({
|
|
174
180
|
credentials: {
|
|
@@ -178,7 +184,8 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
178
184
|
|
|
179
185
|
region: this.options.s3Region,
|
|
180
186
|
});
|
|
181
|
-
|
|
187
|
+
process.env.HEAVY_DEBUG && console.log('🪥🪥 remove ObjectTagging', record[pathColumnName]);
|
|
188
|
+
// let it crash if it fails: this is a new file which just was uploaded.
|
|
182
189
|
await s3.putObjectTagging({
|
|
183
190
|
Bucket: this.options.s3Bucket,
|
|
184
191
|
Key: record[pathColumnName],
|
|
@@ -217,7 +224,7 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
217
224
|
// ** HOOKS FOR LIST **//
|
|
218
225
|
|
|
219
226
|
|
|
220
|
-
if (this.options.preview?.showInList) {
|
|
227
|
+
if (this.options.preview?.showInList || this.options.preview?.showInList === undefined) {
|
|
221
228
|
resourceConfig.hooks.list.afterDatasourceResponse.push(async ({ response }: { response: any }) => {
|
|
222
229
|
const s3 = new S3({
|
|
223
230
|
credentials: {
|
|
@@ -251,18 +258,23 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
251
258
|
region: this.options.s3Region,
|
|
252
259
|
});
|
|
253
260
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
261
|
+
try {
|
|
262
|
+
await s3.putObjectTagging({
|
|
263
|
+
Bucket: this.options.s3Bucket,
|
|
264
|
+
Key: record[pathColumnName],
|
|
265
|
+
Tagging: {
|
|
266
|
+
TagSet: [
|
|
267
|
+
{
|
|
268
|
+
Key: ADMINFORTH_NOT_YET_USED_TAG,
|
|
269
|
+
Value: 'true'
|
|
270
|
+
}
|
|
271
|
+
]
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
} catch (e) {
|
|
275
|
+
// file might be e.g. already deleted, so we catch error
|
|
276
|
+
console.error(`Error setting tag ${ADMINFORTH_NOT_YET_USED_TAG} to true for object ${record[pathColumnName]}. File will not be auto-cleaned up`, e);
|
|
277
|
+
}
|
|
266
278
|
}
|
|
267
279
|
return { ok: true };
|
|
268
280
|
});
|
|
@@ -295,21 +307,27 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
295
307
|
|
|
296
308
|
if (oldRecord[pathColumnName]) {
|
|
297
309
|
// put tag to delete old file
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
+
try {
|
|
311
|
+
await s3.putObjectTagging({
|
|
312
|
+
Bucket: this.options.s3Bucket,
|
|
313
|
+
Key: oldRecord[pathColumnName],
|
|
314
|
+
Tagging: {
|
|
315
|
+
TagSet: [
|
|
316
|
+
{
|
|
317
|
+
Key: ADMINFORTH_NOT_YET_USED_TAG,
|
|
318
|
+
Value: 'true'
|
|
319
|
+
}
|
|
320
|
+
]
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
} catch (e) {
|
|
324
|
+
// file might be e.g. already deleted, so we catch error
|
|
325
|
+
console.error(`Error setting tag ${ADMINFORTH_NOT_YET_USED_TAG} to true for object ${oldRecord[pathColumnName]}. File will not be auto-cleaned up`, e);
|
|
326
|
+
}
|
|
310
327
|
}
|
|
311
328
|
if (record[virtualColumn.name] !== null) {
|
|
312
329
|
// remove tag from new file
|
|
330
|
+
// in this case we let it crash if it fails: this is a new file which just was uploaded.
|
|
313
331
|
await s3.putObjectTagging({
|
|
314
332
|
Bucket: this.options.s3Bucket,
|
|
315
333
|
Key: record[pathColumnName],
|
|
@@ -338,7 +356,9 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
338
356
|
const { originalFilename, contentType, size, originalExtension } = body;
|
|
339
357
|
|
|
340
358
|
if (this.options.allowedFileExtensions && !this.options.allowedFileExtensions.includes(originalExtension)) {
|
|
341
|
-
|
|
359
|
+
return {
|
|
360
|
+
error: `File extension "${originalExtension}" is not allowed, allowed extensions are: ${this.options.allowedFileExtensions.join(', ')}`
|
|
361
|
+
};
|
|
342
362
|
}
|
|
343
363
|
|
|
344
364
|
const s3Path: string = this.options.s3Path({ originalFilename, originalExtension, contentType });
|
|
@@ -366,13 +386,26 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
366
386
|
const uploadUrl = await await getSignedUrl(s3, new PutObjectCommand(params), {
|
|
367
387
|
expiresIn: 1800,
|
|
368
388
|
unhoistableHeaders: new Set(['x-amz-tagging']),
|
|
369
|
-
})
|
|
389
|
+
});
|
|
370
390
|
|
|
391
|
+
let previewUrl;
|
|
392
|
+
if (this.options.preview?.previewUrl) {
|
|
393
|
+
previewUrl = this.options.preview.previewUrl({ s3Path });
|
|
394
|
+
return;
|
|
395
|
+
} else if (this.options.s3ACL === 'public-read') {
|
|
396
|
+
previewUrl = `https://${this.options.s3Bucket}.s3.${this.options.s3Region}.amazonaws.com/${s3Path}`;
|
|
397
|
+
} else {
|
|
398
|
+
previewUrl = await getSignedUrl(s3, new GetObjectCommand({
|
|
399
|
+
Bucket: this.options.s3Bucket,
|
|
400
|
+
Key: s3Path,
|
|
401
|
+
}));
|
|
402
|
+
}
|
|
371
403
|
|
|
372
404
|
return {
|
|
373
405
|
uploadUrl,
|
|
374
406
|
s3Path,
|
|
375
407
|
tagline,
|
|
408
|
+
previewUrl,
|
|
376
409
|
};
|
|
377
410
|
}
|
|
378
411
|
});
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adminforth/upload",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"rollout": "tsc &&
|
|
8
|
+
"rollout": "tsc && rsync -av --exclude 'node_modules' custom dist/ && npm version patch && npm publish --access public",
|
|
9
9
|
"prepare": "npm link adminforth",
|
|
10
10
|
"build": "tsc"
|
|
11
11
|
},
|
package/types.ts
CHANGED
|
@@ -60,9 +60,16 @@ export type PluginOptions = {
|
|
|
60
60
|
preview: {
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
*
|
|
63
|
+
* Whether to show preview of image instead of path in list field
|
|
64
|
+
* By default true
|
|
64
65
|
*/
|
|
65
|
-
showInList
|
|
66
|
+
showInList?: boolean,
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Whether to show preview of image instead of path in list field
|
|
70
|
+
* By default true
|
|
71
|
+
*/
|
|
72
|
+
showInShow?: boolean,
|
|
66
73
|
|
|
67
74
|
/**
|
|
68
75
|
* Used to display preview (if it is image) in list and show views.
|