@adminforth/upload 1.0.12 → 1.0.14
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 +13 -2
- package/dist/custom/preview.vue +1 -1
- package/dist/custom/uploader.vue +13 -2
- package/dist/index.js +153 -90
- package/index.ts +143 -83
- package/package.json +4 -3
- 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, 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) => {
|
|
@@ -196,7 +207,7 @@ const onFileChange = async (e) => {
|
|
|
196
207
|
});
|
|
197
208
|
xhr.open('PUT', uploadUrl, true);
|
|
198
209
|
xhr.setRequestHeader('Content-Type', type);
|
|
199
|
-
xhr.setRequestHeader('x-amz-tagging',
|
|
210
|
+
xhr.setRequestHeader('x-amz-tagging', tagline);
|
|
200
211
|
xhr.send(file);
|
|
201
212
|
});
|
|
202
213
|
if (!success) {
|
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, 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) => {
|
|
@@ -196,7 +207,7 @@ const onFileChange = async (e) => {
|
|
|
196
207
|
});
|
|
197
208
|
xhr.open('PUT', uploadUrl, true);
|
|
198
209
|
xhr.setRequestHeader('Content-Type', type);
|
|
199
|
-
xhr.setRequestHeader('x-amz-tagging',
|
|
210
|
+
xhr.setRequestHeader('x-amz-tagging', tagline);
|
|
200
211
|
xhr.send(file);
|
|
201
212
|
});
|
|
202
213
|
if (!success) {
|
package/dist/index.js
CHANGED
|
@@ -7,8 +7,9 @@ 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
|
|
11
|
-
import {
|
|
10
|
+
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
11
|
+
import { ExpirationStatus, GetObjectCommand, PutObjectCommand, S3 } from '@aws-sdk/client-s3';
|
|
12
|
+
import { AdminForthPlugin } from "adminforth";
|
|
12
13
|
const ADMINFORTH_NOT_YET_USED_TAG = 'adminforth-candidate-for-cleanup';
|
|
13
14
|
export default class UploadPlugin extends AdminForthPlugin {
|
|
14
15
|
constructor(options) {
|
|
@@ -22,24 +23,26 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
22
23
|
return __awaiter(this, void 0, void 0, function* () {
|
|
23
24
|
// check that lifecyle rule "adminforth-unused-cleaner" exists
|
|
24
25
|
const CLEANUP_RULE_ID = 'adminforth-unused-cleaner';
|
|
25
|
-
const s3 = new
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
const s3 = new S3({
|
|
27
|
+
credentials: {
|
|
28
|
+
accessKeyId: this.options.s3AccessKeyId,
|
|
29
|
+
secretAccessKey: this.options.s3SecretAccessKey,
|
|
30
|
+
},
|
|
31
|
+
region: this.options.s3Region,
|
|
29
32
|
});
|
|
30
33
|
// check bucket exists
|
|
31
|
-
const bucketExists = s3.headBucket({ Bucket: this.options.s3Bucket })
|
|
34
|
+
const bucketExists = s3.headBucket({ Bucket: this.options.s3Bucket });
|
|
32
35
|
if (!bucketExists) {
|
|
33
36
|
throw new Error(`Bucket ${this.options.s3Bucket} does not exist`);
|
|
34
37
|
}
|
|
35
38
|
// check that lifecycle rule exists
|
|
36
39
|
let ruleExists = false;
|
|
37
40
|
try {
|
|
38
|
-
const lifecycleConfig = yield s3.getBucketLifecycleConfiguration({ Bucket: this.options.s3Bucket })
|
|
41
|
+
const lifecycleConfig = yield s3.getBucketLifecycleConfiguration({ Bucket: this.options.s3Bucket });
|
|
39
42
|
ruleExists = lifecycleConfig.Rules.some((rule) => rule.ID === CLEANUP_RULE_ID);
|
|
40
43
|
}
|
|
41
44
|
catch (e) {
|
|
42
|
-
if (e.
|
|
45
|
+
if (e.name !== 'NoSuchLifecycleConfiguration') {
|
|
43
46
|
throw e;
|
|
44
47
|
}
|
|
45
48
|
else {
|
|
@@ -55,7 +58,7 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
55
58
|
Rules: [
|
|
56
59
|
{
|
|
57
60
|
ID: CLEANUP_RULE_ID,
|
|
58
|
-
Status:
|
|
61
|
+
Status: ExpirationStatus.Enabled,
|
|
59
62
|
Filter: {
|
|
60
63
|
Tag: {
|
|
61
64
|
Key: ADMINFORTH_NOT_YET_USED_TAG,
|
|
@@ -69,7 +72,7 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
69
72
|
]
|
|
70
73
|
}
|
|
71
74
|
};
|
|
72
|
-
yield s3.putBucketLifecycleConfiguration(params)
|
|
75
|
+
yield s3.putBucketLifecycleConfiguration(params);
|
|
73
76
|
}
|
|
74
77
|
});
|
|
75
78
|
}
|
|
@@ -80,10 +83,10 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
80
83
|
record[`previewUrl_${this.pluginInstanceId}`] = this.options.preview.previewUrl({ s3Path: record[this.options.pathColumnName] });
|
|
81
84
|
return;
|
|
82
85
|
}
|
|
83
|
-
const previewUrl = yield
|
|
86
|
+
const previewUrl = yield yield getSignedUrl(s3, new GetObjectCommand({
|
|
84
87
|
Bucket: this.options.s3Bucket,
|
|
85
88
|
Key: record[this.options.pathColumnName],
|
|
86
|
-
});
|
|
89
|
+
}));
|
|
87
90
|
record[`previewUrl_${this.pluginInstanceId}`] = previewUrl;
|
|
88
91
|
});
|
|
89
92
|
}
|
|
@@ -92,7 +95,7 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
92
95
|
modifyResourceConfig: { get: () => super.modifyResourceConfig }
|
|
93
96
|
});
|
|
94
97
|
return __awaiter(this, void 0, void 0, function* () {
|
|
95
|
-
var _a, _b, _c;
|
|
98
|
+
var _a, _b, _c, _d, _e;
|
|
96
99
|
_super.modifyResourceConfig.call(this, adminforth, resourceConfig);
|
|
97
100
|
// after column to store the path of the uploaded file, add new VirtualColumn,
|
|
98
101
|
// show only in edit and create views
|
|
@@ -106,29 +109,38 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
106
109
|
const virtualColumn = {
|
|
107
110
|
virtual: true,
|
|
108
111
|
name: `uploader_${this.pluginInstanceId}`,
|
|
109
|
-
components:
|
|
112
|
+
components: {
|
|
113
|
+
edit: {
|
|
110
114
|
file: this.componentPath('uploader.vue'),
|
|
111
115
|
meta: pluginFrontendOptions,
|
|
112
|
-
},
|
|
116
|
+
},
|
|
117
|
+
create: {
|
|
113
118
|
file: this.componentPath('uploader.vue'),
|
|
114
119
|
meta: pluginFrontendOptions,
|
|
115
|
-
},
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
} }, (((_a = this.options.preview) === null || _a === void 0 ? void 0 : _a.showInList) ? {
|
|
119
|
-
list: {
|
|
120
|
-
file: this.componentPath('preview.vue'),
|
|
121
|
-
meta: pluginFrontendOptions,
|
|
122
|
-
}
|
|
123
|
-
} : {})),
|
|
124
|
-
showIn: ['edit', 'create', 'show', ...(((_b = this.options.preview) === null || _b === void 0 ? void 0 : _b.showInList) ? [
|
|
125
|
-
AdminForthResourcePages.list
|
|
126
|
-
] : [])],
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
showIn: ['edit', 'create'],
|
|
127
123
|
};
|
|
128
124
|
const pathColumnIndex = resourceConfig.columns.findIndex((column) => column.name === pathColumnName);
|
|
129
125
|
if (pathColumnIndex === -1) {
|
|
130
126
|
throw new Error(`Column with name "${pathColumnName}" not found in resource "${resourceConfig.name}"`);
|
|
131
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
|
+
}
|
|
132
144
|
// insert virtual column after path column if it is not already there
|
|
133
145
|
const virtualColumnIndex = resourceConfig.columns.findIndex((column) => column.name === virtualColumn.name);
|
|
134
146
|
if (virtualColumnIndex === -1) {
|
|
@@ -144,7 +156,7 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
144
156
|
virtualColumn.editingNote = pathColumn.editingNote;
|
|
145
157
|
// ** HOOKS FOR CREATE **//
|
|
146
158
|
// add beforeSave hook to save virtual column to path column
|
|
147
|
-
resourceConfig.hooks.create.beforeSave.push((
|
|
159
|
+
resourceConfig.hooks.create.beforeSave.push((_f) => __awaiter(this, [_f], void 0, function* ({ record }) {
|
|
148
160
|
if (record[virtualColumn.name]) {
|
|
149
161
|
record[pathColumnName] = record[virtualColumn.name];
|
|
150
162
|
delete record[virtualColumn.name];
|
|
@@ -152,47 +164,56 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
152
164
|
return { ok: true };
|
|
153
165
|
}));
|
|
154
166
|
// in afterSave hook, aremove tag adminforth-not-yet-used from the file
|
|
155
|
-
resourceConfig.hooks.create.afterSave.push((
|
|
167
|
+
resourceConfig.hooks.create.afterSave.push((_g) => __awaiter(this, [_g], void 0, function* ({ record }) {
|
|
168
|
+
process.env.HEAVY_DEBUG && console.log('💾💾 after save ', record === null || record === void 0 ? void 0 : record.id);
|
|
156
169
|
if (record[pathColumnName]) {
|
|
157
|
-
const s3 = new
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
170
|
+
const s3 = new S3({
|
|
171
|
+
credentials: {
|
|
172
|
+
accessKeyId: this.options.s3AccessKeyId,
|
|
173
|
+
secretAccessKey: this.options.s3SecretAccessKey,
|
|
174
|
+
},
|
|
175
|
+
region: this.options.s3Region,
|
|
161
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.
|
|
162
179
|
yield s3.putObjectTagging({
|
|
163
180
|
Bucket: this.options.s3Bucket,
|
|
164
181
|
Key: record[pathColumnName],
|
|
165
182
|
Tagging: {
|
|
166
183
|
TagSet: []
|
|
167
184
|
}
|
|
168
|
-
})
|
|
185
|
+
});
|
|
169
186
|
}
|
|
170
187
|
return { ok: true };
|
|
171
188
|
}));
|
|
172
189
|
// ** HOOKS FOR SHOW **//
|
|
173
190
|
// add show hook to get presigned URL
|
|
174
|
-
resourceConfig.hooks.show.afterDatasourceResponse.push((
|
|
191
|
+
resourceConfig.hooks.show.afterDatasourceResponse.push((_h) => __awaiter(this, [_h], void 0, function* ({ response }) {
|
|
175
192
|
const record = response[0];
|
|
176
193
|
if (!record) {
|
|
177
194
|
return { ok: true };
|
|
178
195
|
}
|
|
179
196
|
if (record[pathColumnName]) {
|
|
180
|
-
const s3 = new
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
197
|
+
const s3 = new S3({
|
|
198
|
+
credentials: {
|
|
199
|
+
accessKeyId: this.options.s3AccessKeyId,
|
|
200
|
+
secretAccessKey: this.options.s3SecretAccessKey,
|
|
201
|
+
},
|
|
202
|
+
region: this.options.s3Region,
|
|
184
203
|
});
|
|
185
204
|
yield this.genPreviewUrl(record, s3);
|
|
186
205
|
}
|
|
187
206
|
return { ok: true };
|
|
188
207
|
}));
|
|
189
208
|
// ** HOOKS FOR LIST **//
|
|
190
|
-
if ((
|
|
191
|
-
resourceConfig.hooks.list.afterDatasourceResponse.push((
|
|
192
|
-
const s3 = new
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
209
|
+
if ((_e = this.options.preview) === null || _e === void 0 ? void 0 : _e.showInList) {
|
|
210
|
+
resourceConfig.hooks.list.afterDatasourceResponse.push((_j) => __awaiter(this, [_j], void 0, function* ({ response }) {
|
|
211
|
+
const s3 = new S3({
|
|
212
|
+
credentials: {
|
|
213
|
+
accessKeyId: this.options.s3AccessKeyId,
|
|
214
|
+
secretAccessKey: this.options.s3SecretAccessKey,
|
|
215
|
+
},
|
|
216
|
+
region: this.options.s3Region,
|
|
196
217
|
});
|
|
197
218
|
yield Promise.all(response.map((record) => __awaiter(this, void 0, void 0, function* () {
|
|
198
219
|
if (record[this.options.pathColumnName]) {
|
|
@@ -204,31 +225,39 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
204
225
|
}
|
|
205
226
|
// ** HOOKS FOR DELETE **//
|
|
206
227
|
// add delete hook which sets tag adminforth-candidate-for-cleanup to true
|
|
207
|
-
resourceConfig.hooks.delete.afterSave.push((
|
|
228
|
+
resourceConfig.hooks.delete.afterSave.push((_k) => __awaiter(this, [_k], void 0, function* ({ record }) {
|
|
208
229
|
if (record[pathColumnName]) {
|
|
209
|
-
const s3 = new
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
230
|
+
const s3 = new S3({
|
|
231
|
+
credentials: {
|
|
232
|
+
accessKeyId: this.options.s3AccessKeyId,
|
|
233
|
+
secretAccessKey: this.options.s3SecretAccessKey,
|
|
234
|
+
},
|
|
235
|
+
region: this.options.s3Region,
|
|
213
236
|
});
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
+
}
|
|
226
255
|
}
|
|
227
256
|
return { ok: true };
|
|
228
257
|
}));
|
|
229
258
|
// ** HOOKS FOR EDIT **//
|
|
230
259
|
// beforeSave
|
|
231
|
-
resourceConfig.hooks.edit.beforeSave.push((
|
|
260
|
+
resourceConfig.hooks.edit.beforeSave.push((_l) => __awaiter(this, [_l], void 0, function* ({ record }) {
|
|
232
261
|
// null is when value is removed
|
|
233
262
|
if (record[virtualColumn.name] || record[virtualColumn.name] === null) {
|
|
234
263
|
record[pathColumnName] = record[virtualColumn.name];
|
|
@@ -236,37 +265,46 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
236
265
|
return { ok: true };
|
|
237
266
|
}));
|
|
238
267
|
// add edit postSave hook to delete old file and remove tag from new file
|
|
239
|
-
resourceConfig.hooks.edit.afterSave.push((
|
|
268
|
+
resourceConfig.hooks.edit.afterSave.push((_m) => __awaiter(this, [_m], void 0, function* ({ record, oldRecord }) {
|
|
240
269
|
if (record[virtualColumn.name] || record[virtualColumn.name] === null) {
|
|
241
|
-
const s3 = new
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
270
|
+
const s3 = new S3({
|
|
271
|
+
credentials: {
|
|
272
|
+
accessKeyId: this.options.s3AccessKeyId,
|
|
273
|
+
secretAccessKey: this.options.s3SecretAccessKey,
|
|
274
|
+
},
|
|
275
|
+
region: this.options.s3Region,
|
|
245
276
|
});
|
|
246
277
|
if (oldRecord[pathColumnName]) {
|
|
247
278
|
// put tag to delete old file
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
+
}
|
|
260
297
|
}
|
|
261
298
|
if (record[virtualColumn.name] !== null) {
|
|
262
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.
|
|
263
301
|
yield s3.putObjectTagging({
|
|
264
302
|
Bucket: this.options.s3Bucket,
|
|
265
303
|
Key: record[pathColumnName],
|
|
266
304
|
Tagging: {
|
|
267
305
|
TagSet: []
|
|
268
306
|
}
|
|
269
|
-
})
|
|
307
|
+
});
|
|
270
308
|
}
|
|
271
309
|
}
|
|
272
310
|
return { ok: true };
|
|
@@ -282,30 +320,55 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
282
320
|
method: 'POST',
|
|
283
321
|
path: `/plugin/${this.pluginInstanceId}/get_s3_upload_url`,
|
|
284
322
|
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body }) {
|
|
323
|
+
var _b;
|
|
285
324
|
const { originalFilename, contentType, size, originalExtension } = body;
|
|
286
325
|
if (this.options.allowedFileExtensions && !this.options.allowedFileExtensions.includes(originalExtension)) {
|
|
287
|
-
|
|
326
|
+
return {
|
|
327
|
+
error: `File extension "${originalExtension}" is not allowed, allowed extensions are: ${this.options.allowedFileExtensions.join(', ')}`
|
|
328
|
+
};
|
|
288
329
|
}
|
|
289
330
|
const s3Path = this.options.s3Path({ originalFilename, originalExtension, contentType });
|
|
290
331
|
if (s3Path.startsWith('/')) {
|
|
291
332
|
throw new Error('s3Path should not start with /, please adjust s3path function to not return / at the start of the path');
|
|
292
333
|
}
|
|
293
|
-
const s3 = new
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
334
|
+
const s3 = new S3({
|
|
335
|
+
credentials: {
|
|
336
|
+
accessKeyId: this.options.s3AccessKeyId,
|
|
337
|
+
secretAccessKey: this.options.s3SecretAccessKey,
|
|
338
|
+
},
|
|
339
|
+
region: this.options.s3Region,
|
|
297
340
|
});
|
|
341
|
+
const tagline = `${ADMINFORTH_NOT_YET_USED_TAG}=true`;
|
|
298
342
|
const params = {
|
|
299
343
|
Bucket: this.options.s3Bucket,
|
|
300
344
|
Key: s3Path,
|
|
301
345
|
ContentType: contentType,
|
|
302
|
-
ACL: this.options.s3ACL || 'private',
|
|
303
|
-
Tagging:
|
|
346
|
+
ACL: (this.options.s3ACL || 'private'),
|
|
347
|
+
Tagging: tagline,
|
|
304
348
|
};
|
|
305
|
-
const uploadUrl = yield
|
|
349
|
+
const uploadUrl = yield yield getSignedUrl(s3, new PutObjectCommand(params), {
|
|
350
|
+
expiresIn: 1800,
|
|
351
|
+
unhoistableHeaders: new Set(['x-amz-tagging']),
|
|
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
|
+
}
|
|
306
367
|
return {
|
|
307
368
|
uploadUrl,
|
|
308
369
|
s3Path,
|
|
370
|
+
tagline,
|
|
371
|
+
previewUrl,
|
|
309
372
|
};
|
|
310
373
|
})
|
|
311
374
|
});
|
package/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import { PluginOptions } from './types.js';
|
|
3
|
-
import
|
|
3
|
+
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
4
|
+
import { ExpirationStatus, GetObjectCommand, ObjectCannedACL, PutObjectCommand, S3 } from '@aws-sdk/client-s3';
|
|
4
5
|
import { AdminForthPlugin, AdminForthResourceColumn, AdminForthResourcePages, IAdminForth, IHttpServer } from "adminforth";
|
|
5
6
|
|
|
6
7
|
const ADMINFORTH_NOT_YET_USED_TAG = 'adminforth-candidate-for-cleanup';
|
|
@@ -20,13 +21,16 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
20
21
|
async setupLifecycleRule() {
|
|
21
22
|
// check that lifecyle rule "adminforth-unused-cleaner" exists
|
|
22
23
|
const CLEANUP_RULE_ID = 'adminforth-unused-cleaner';
|
|
23
|
-
const s3 = new
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const s3 = new S3({
|
|
25
|
+
credentials: {
|
|
26
|
+
accessKeyId: this.options.s3AccessKeyId,
|
|
27
|
+
secretAccessKey: this.options.s3SecretAccessKey,
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
region: this.options.s3Region,
|
|
27
31
|
});
|
|
28
32
|
// check bucket exists
|
|
29
|
-
const bucketExists = s3.headBucket({ Bucket: this.options.s3Bucket })
|
|
33
|
+
const bucketExists = s3.headBucket({ Bucket: this.options.s3Bucket })
|
|
30
34
|
if (!bucketExists) {
|
|
31
35
|
throw new Error(`Bucket ${this.options.s3Bucket} does not exist`);
|
|
32
36
|
}
|
|
@@ -35,10 +39,10 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
35
39
|
let ruleExists: boolean = false;
|
|
36
40
|
|
|
37
41
|
try {
|
|
38
|
-
const lifecycleConfig: any = await s3.getBucketLifecycleConfiguration({ Bucket: this.options.s3Bucket })
|
|
42
|
+
const lifecycleConfig: any = await s3.getBucketLifecycleConfiguration({ Bucket: this.options.s3Bucket });
|
|
39
43
|
ruleExists = lifecycleConfig.Rules.some((rule: any) => rule.ID === CLEANUP_RULE_ID);
|
|
40
44
|
} catch (e: any) {
|
|
41
|
-
if (e.
|
|
45
|
+
if (e.name !== 'NoSuchLifecycleConfiguration') {
|
|
42
46
|
throw e;
|
|
43
47
|
} else {
|
|
44
48
|
ruleExists = false;
|
|
@@ -54,7 +58,7 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
54
58
|
Rules: [
|
|
55
59
|
{
|
|
56
60
|
ID: CLEANUP_RULE_ID,
|
|
57
|
-
Status:
|
|
61
|
+
Status: ExpirationStatus.Enabled,
|
|
58
62
|
Filter: {
|
|
59
63
|
Tag: {
|
|
60
64
|
Key: ADMINFORTH_NOT_YET_USED_TAG,
|
|
@@ -69,19 +73,19 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
69
73
|
}
|
|
70
74
|
};
|
|
71
75
|
|
|
72
|
-
await s3.putBucketLifecycleConfiguration(params)
|
|
76
|
+
await s3.putBucketLifecycleConfiguration(params);
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
async genPreviewUrl(record: any, s3:
|
|
80
|
+
async genPreviewUrl(record: any, s3: S3) {
|
|
77
81
|
if (this.options.preview?.previewUrl) {
|
|
78
82
|
record[`previewUrl_${this.pluginInstanceId}`] = this.options.preview.previewUrl({ s3Path: record[this.options.pathColumnName] });
|
|
79
83
|
return;
|
|
80
84
|
}
|
|
81
|
-
const previewUrl = await
|
|
85
|
+
const previewUrl = await await getSignedUrl(s3, new GetObjectCommand({
|
|
82
86
|
Bucket: this.options.s3Bucket,
|
|
83
87
|
Key: record[this.options.pathColumnName],
|
|
84
|
-
});
|
|
88
|
+
}));
|
|
85
89
|
|
|
86
90
|
record[`previewUrl_${this.pluginInstanceId}`] = previewUrl;
|
|
87
91
|
}
|
|
@@ -111,22 +115,8 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
111
115
|
file: this.componentPath('uploader.vue'),
|
|
112
116
|
meta: pluginFrontendOptions,
|
|
113
117
|
},
|
|
114
|
-
show: {
|
|
115
|
-
file: this.componentPath('preview.vue'),
|
|
116
|
-
meta: pluginFrontendOptions,
|
|
117
|
-
},
|
|
118
|
-
...(
|
|
119
|
-
this.options.preview?.showInList ? {
|
|
120
|
-
list: {
|
|
121
|
-
file: this.componentPath('preview.vue'),
|
|
122
|
-
meta: pluginFrontendOptions,
|
|
123
|
-
}
|
|
124
|
-
} : {}
|
|
125
|
-
),
|
|
126
118
|
},
|
|
127
|
-
showIn: ['edit', 'create',
|
|
128
|
-
AdminForthResourcePages.list
|
|
129
|
-
] : [])],
|
|
119
|
+
showIn: ['edit', 'create'],
|
|
130
120
|
};
|
|
131
121
|
|
|
132
122
|
|
|
@@ -135,6 +125,24 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
135
125
|
if (pathColumnIndex === -1) {
|
|
136
126
|
throw new Error(`Column with name "${pathColumnName}" not found in resource "${resourceConfig.name}"`);
|
|
137
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
|
+
}
|
|
138
146
|
|
|
139
147
|
// insert virtual column after path column if it is not already there
|
|
140
148
|
const virtualColumnIndex = resourceConfig.columns.findIndex((column: any) => column.name === virtualColumn.name);
|
|
@@ -165,20 +173,26 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
165
173
|
|
|
166
174
|
// in afterSave hook, aremove tag adminforth-not-yet-used from the file
|
|
167
175
|
resourceConfig.hooks.create.afterSave.push(async ({ record }: { record: any }) => {
|
|
176
|
+
process.env.HEAVY_DEBUG && console.log('💾💾 after save ', record?.id);
|
|
177
|
+
|
|
168
178
|
if (record[pathColumnName]) {
|
|
169
|
-
const s3 = new
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
179
|
+
const s3 = new S3({
|
|
180
|
+
credentials: {
|
|
181
|
+
accessKeyId: this.options.s3AccessKeyId,
|
|
182
|
+
secretAccessKey: this.options.s3SecretAccessKey,
|
|
183
|
+
},
|
|
174
184
|
|
|
185
|
+
region: this.options.s3Region,
|
|
186
|
+
});
|
|
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.
|
|
175
189
|
await s3.putObjectTagging({
|
|
176
190
|
Bucket: this.options.s3Bucket,
|
|
177
191
|
Key: record[pathColumnName],
|
|
178
192
|
Tagging: {
|
|
179
193
|
TagSet: []
|
|
180
194
|
}
|
|
181
|
-
})
|
|
195
|
+
});
|
|
182
196
|
}
|
|
183
197
|
return { ok: true };
|
|
184
198
|
});
|
|
@@ -193,10 +207,13 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
193
207
|
return { ok: true };
|
|
194
208
|
}
|
|
195
209
|
if (record[pathColumnName]) {
|
|
196
|
-
const s3 = new
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
210
|
+
const s3 = new S3({
|
|
211
|
+
credentials: {
|
|
212
|
+
accessKeyId: this.options.s3AccessKeyId,
|
|
213
|
+
secretAccessKey: this.options.s3SecretAccessKey,
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
region: this.options.s3Region,
|
|
200
217
|
});
|
|
201
218
|
|
|
202
219
|
await this.genPreviewUrl(record, s3);
|
|
@@ -209,10 +226,13 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
209
226
|
|
|
210
227
|
if (this.options.preview?.showInList) {
|
|
211
228
|
resourceConfig.hooks.list.afterDatasourceResponse.push(async ({ response }: { response: any }) => {
|
|
212
|
-
const s3 = new
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
229
|
+
const s3 = new S3({
|
|
230
|
+
credentials: {
|
|
231
|
+
accessKeyId: this.options.s3AccessKeyId,
|
|
232
|
+
secretAccessKey: this.options.s3SecretAccessKey,
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
region: this.options.s3Region,
|
|
216
236
|
});
|
|
217
237
|
|
|
218
238
|
await Promise.all(response.map(async (record: any) => {
|
|
@@ -229,24 +249,32 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
229
249
|
// add delete hook which sets tag adminforth-candidate-for-cleanup to true
|
|
230
250
|
resourceConfig.hooks.delete.afterSave.push(async ({ record }: { record: any }) => {
|
|
231
251
|
if (record[pathColumnName]) {
|
|
232
|
-
const s3 = new
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
252
|
+
const s3 = new S3({
|
|
253
|
+
credentials: {
|
|
254
|
+
accessKeyId: this.options.s3AccessKeyId,
|
|
255
|
+
secretAccessKey: this.options.s3SecretAccessKey,
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
region: this.options.s3Region,
|
|
236
259
|
});
|
|
237
260
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
+
}
|
|
250
278
|
}
|
|
251
279
|
return { ok: true };
|
|
252
280
|
});
|
|
@@ -268,36 +296,45 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
268
296
|
resourceConfig.hooks.edit.afterSave.push(async ({ record, oldRecord }: { record: any, oldRecord: any }) => {
|
|
269
297
|
|
|
270
298
|
if (record[virtualColumn.name] || record[virtualColumn.name] === null) {
|
|
271
|
-
const s3 = new
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
299
|
+
const s3 = new S3({
|
|
300
|
+
credentials: {
|
|
301
|
+
accessKeyId: this.options.s3AccessKeyId,
|
|
302
|
+
secretAccessKey: this.options.s3SecretAccessKey,
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
region: this.options.s3Region,
|
|
275
306
|
});
|
|
276
307
|
|
|
277
308
|
if (oldRecord[pathColumnName]) {
|
|
278
309
|
// put tag to delete old file
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
+
}
|
|
291
327
|
}
|
|
292
328
|
if (record[virtualColumn.name] !== null) {
|
|
293
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.
|
|
294
331
|
await s3.putObjectTagging({
|
|
295
332
|
Bucket: this.options.s3Bucket,
|
|
296
333
|
Key: record[pathColumnName],
|
|
297
334
|
Tagging: {
|
|
298
335
|
TagSet: []
|
|
299
336
|
}
|
|
300
|
-
})
|
|
337
|
+
});
|
|
301
338
|
}
|
|
302
339
|
}
|
|
303
340
|
return { ok: true };
|
|
@@ -319,33 +356,56 @@ export default class UploadPlugin extends AdminForthPlugin {
|
|
|
319
356
|
const { originalFilename, contentType, size, originalExtension } = body;
|
|
320
357
|
|
|
321
358
|
if (this.options.allowedFileExtensions && !this.options.allowedFileExtensions.includes(originalExtension)) {
|
|
322
|
-
|
|
359
|
+
return {
|
|
360
|
+
error: `File extension "${originalExtension}" is not allowed, allowed extensions are: ${this.options.allowedFileExtensions.join(', ')}`
|
|
361
|
+
};
|
|
323
362
|
}
|
|
324
363
|
|
|
325
364
|
const s3Path: string = this.options.s3Path({ originalFilename, originalExtension, contentType });
|
|
326
365
|
if (s3Path.startsWith('/')) {
|
|
327
366
|
throw new Error('s3Path should not start with /, please adjust s3path function to not return / at the start of the path');
|
|
328
367
|
}
|
|
329
|
-
const s3 = new
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
368
|
+
const s3 = new S3({
|
|
369
|
+
credentials: {
|
|
370
|
+
accessKeyId: this.options.s3AccessKeyId,
|
|
371
|
+
secretAccessKey: this.options.s3SecretAccessKey,
|
|
372
|
+
},
|
|
373
|
+
|
|
374
|
+
region: this.options.s3Region,
|
|
333
375
|
});
|
|
334
376
|
|
|
377
|
+
const tagline = `${ADMINFORTH_NOT_YET_USED_TAG}=true`;
|
|
335
378
|
const params = {
|
|
336
379
|
Bucket: this.options.s3Bucket,
|
|
337
380
|
Key: s3Path,
|
|
338
381
|
ContentType: contentType,
|
|
339
|
-
ACL: this.options.s3ACL || 'private',
|
|
340
|
-
Tagging:
|
|
382
|
+
ACL: (this.options.s3ACL || 'private') as ObjectCannedACL,
|
|
383
|
+
Tagging: tagline,
|
|
341
384
|
};
|
|
342
385
|
|
|
343
|
-
const uploadUrl = await
|
|
386
|
+
const uploadUrl = await await getSignedUrl(s3, new PutObjectCommand(params), {
|
|
387
|
+
expiresIn: 1800,
|
|
388
|
+
unhoistableHeaders: new Set(['x-amz-tagging']),
|
|
389
|
+
});
|
|
344
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
|
+
}
|
|
345
403
|
|
|
346
404
|
return {
|
|
347
405
|
uploadUrl,
|
|
348
406
|
s3Path,
|
|
407
|
+
tagline,
|
|
408
|
+
previewUrl,
|
|
349
409
|
};
|
|
350
410
|
}
|
|
351
411
|
});
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adminforth/upload",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
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
|
},
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"author": "",
|
|
14
14
|
"license": "ISC",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"aws-sdk": "^
|
|
16
|
+
"@aws-sdk/client-s3": "^3.629.0",
|
|
17
|
+
"@aws-sdk/s3-request-presigner": "^3.629.0"
|
|
17
18
|
}
|
|
18
19
|
}
|
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.
|