@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.
@@ -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-blue-700 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"
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">
@@ -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) => {
@@ -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-blue-700 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"
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">
@@ -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, AdminForthResourcePages } from "adminforth";
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: Object.assign({ edit: {
112
+ components: {
113
+ edit: {
113
114
  file: this.componentPath('uploader.vue'),
114
115
  meta: pluginFrontendOptions,
115
- }, create: {
116
+ },
117
+ create: {
116
118
  file: this.componentPath('uploader.vue'),
117
119
  meta: pluginFrontendOptions,
118
- }, show: {
119
- file: this.componentPath('preview.vue'),
120
- meta: pluginFrontendOptions,
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((_d) => __awaiter(this, [_d], void 0, function* ({ record }) {
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((_e) => __awaiter(this, [_e], void 0, function* ({ record }) {
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((_f) => __awaiter(this, [_f], void 0, function* ({ response }) {
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 ((_c = this.options.preview) === null || _c === void 0 ? void 0 : _c.showInList) {
198
- resourceConfig.hooks.list.afterDatasourceResponse.push((_g) => __awaiter(this, [_g], void 0, function* ({ response }) {
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((_h) => __awaiter(this, [_h], void 0, function* ({ record }) {
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
- yield s3.putObjectTagging({
226
- Bucket: this.options.s3Bucket,
227
- Key: record[pathColumnName],
228
- Tagging: {
229
- TagSet: [
230
- {
231
- Key: ADMINFORTH_NOT_YET_USED_TAG,
232
- Value: 'true'
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((_j) => __awaiter(this, [_j], void 0, function* ({ record }) {
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((_k) => __awaiter(this, [_k], void 0, function* ({ record, oldRecord }) {
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
- yield s3.putObjectTagging({
262
- Bucket: this.options.s3Bucket,
263
- Key: oldRecord[pathColumnName],
264
- Tagging: {
265
- TagSet: [
266
- {
267
- Key: ADMINFORTH_NOT_YET_USED_TAG,
268
- Value: 'true'
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
- throw new Error(`File extension "${originalExtension}" is not allowed, allowed extensions are: ${this.options.allowedFileExtensions.join(', ')}`);
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', 'show', ...(this.options.preview?.showInList ? [
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
- await s3.putObjectTagging({
255
- Bucket: this.options.s3Bucket,
256
- Key: record[pathColumnName],
257
- Tagging: {
258
- TagSet: [
259
- {
260
- Key: ADMINFORTH_NOT_YET_USED_TAG,
261
- Value: 'true'
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
- await s3.putObjectTagging({
299
- Bucket: this.options.s3Bucket,
300
- Key: oldRecord[pathColumnName],
301
- Tagging: {
302
- TagSet: [
303
- {
304
- Key: ADMINFORTH_NOT_YET_USED_TAG,
305
- Value: 'true'
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
- throw new Error(`File extension "${originalExtension}" is not allowed, allowed extensions are: ${this.options.allowedFileExtensions.join(', ')}`);
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.13",
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 && cp -rf custom dist/ && npm version patch && npm publish --access public",
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
- * By default preview is shown in the show view only. If you want to show it in the list view as well, set this to true
63
+ * Whether to show preview of image instead of path in list field
64
+ * By default true
64
65
  */
65
- showInList: boolean,
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.