@pi-r/aws 0.10.2 → 0.11.0

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/client/index.d.ts CHANGED
@@ -7,7 +7,7 @@ import type { BucketCannedACL } from '@pi-r/aws-lib';
7
7
  import type { AWSDatabaseCredential, AWSDatabaseQuery, AWSStorageCredential, ConfigureBucketOptions } from '../types';
8
8
 
9
9
  import type { AttributeValue, DocumentClient } from 'aws-sdk/clients/dynamodb';
10
- import type { CreateBucketRequest, ListObjectsRequest as IListObjectsRequest } from 'aws-sdk/clients/s3';
10
+ import type { CopyObjectRequest, CreateBucketRequest, ListObjectsRequest as IListObjectsRequest } from 'aws-sdk/clients/s3';
11
11
 
12
12
  declare namespace AWS {
13
13
  type ListObjectsRequest = IListObjectsRequest & RecursiveAction;
@@ -23,6 +23,7 @@ declare namespace AWS {
23
23
  function deleteObjects(this: IModule, credential: AWSStorageCredential, Bucket: string, service?: string, sdk?: string): Promise<void>;
24
24
  function deleteObjectsV2(this: IModule, credential: AWSStorageCredential, Bucket: string, recursive?: boolean, service?: string, sdk?: string): Promise<void>;
25
25
  function deleteObjectsV3(this: IModule, credential: AWSStorageCredential, Bucket: string, options?: ListObjectsRequest, service?: string, sdk?: string): Promise<void>;
26
+ function copyObject(this: IModule, credential: AWSStorageCredential, BucketSource: string, KeySource: string, Bucket: string, Key: string, options?: CopyObjectRequest, service?: string, sdk?: string): Promise<void>;
26
27
  function executeQuery(this: ICloud, credential: AWSDatabaseCredential, data: AWSDatabaseQuery, sessionKey?: string): Promise<QueryResult>;
27
28
  function executeBatchQuery(this: ICloud, credential: AWSDatabaseCredential, batch: AWSDatabaseQuery[], sessionKey?: string): Promise<BatchQueryResult>;
28
29
  function parseAttributeValue(value: unknown): AttributeValue;
package/client/index.js CHANGED
@@ -12,6 +12,7 @@ exports.setBucketWebsite = setBucketWebsite;
12
12
  exports.deleteObjects = deleteObjects;
13
13
  exports.deleteObjectsV2 = deleteObjectsV2;
14
14
  exports.deleteObjectsV3 = deleteObjectsV3;
15
+ exports.copyObject = copyObject;
15
16
  exports.executeQuery = executeQuery;
16
17
  exports.executeBatchQuery = executeBatchQuery;
17
18
  exports.parseAttributeValue = parseAttributeValue;
@@ -35,17 +36,17 @@ async function setCannedAcl(S3, Bucket, ACL, service = "aws", recursive) {
35
36
  }
36
37
  const callback = (err) => {
37
38
  if (!err) {
38
- this.formatMessage(64, service, ['Grant ' + ACL, Bucket], '', { ...recursive ? Cloud.LOG_CLOUD_DELAYED : Cloud.LOG_CLOUD_COMMAND });
39
+ this.formatMessage(64, service, ['Grant ' + ACL, Bucket], '', Cloud.optionsLogMessage(recursive ? 'DELAYED' : 'COMMAND'));
39
40
  }
40
41
  else if (!recursive) {
41
- if (err instanceof Error && err.code === 'OperationAborted') {
42
+ if ((0, types_1.isErrorCode)(err, 'OperationAborted')) {
42
43
  setTimeout(() => {
43
44
  void setCannedAcl.call(this, S3, Bucket, ACL, service, true);
44
45
  }, 60000);
45
- this.formatMessage(64, service, [`Grant ${ACL} (delayed)`, Bucket], err, { ...Cloud.LOG_CLOUD_DELAYED });
46
+ this.formatMessage(64, service, [`Grant ${ACL} (delayed)`, Bucket], err, Cloud.optionsLogMessage('DELAYED'));
46
47
  }
47
48
  else {
48
- this.formatMessage(64, service, ['Unable to grant ' + ACL, Bucket], err, { ...Cloud.LOG_CLOUD_WARN });
49
+ this.formatMessage(64, service, ['Unable to grant ' + ACL, Bucket], err, Cloud.optionsLogMessage('WARN'));
49
50
  }
50
51
  }
51
52
  };
@@ -149,7 +150,7 @@ async function createBucketV2(credential, Bucket, ACL, options, service = "aws",
149
150
  }
150
151
  return S3.createBucket(input).promise()
151
152
  .then(async () => {
152
- this.formatMessage(64, service, ["Bucket created", Bucket], '', { ...Cloud.LOG_CLOUD_COMMAND });
153
+ this.formatMessage(64, service, ["Bucket created", Bucket], '', Cloud.optionsLogMessage('COMMAND'));
153
154
  if (ACL) {
154
155
  await setCannedAcl.call(this, S3, Bucket, ACL, service);
155
156
  }
@@ -164,7 +165,7 @@ async function createBucketV2(credential, Bucket, ACL, options, service = "aws",
164
165
  }
165
166
  return true;
166
167
  default:
167
- this.formatFail(64, service, ["Unable to create bucket", Bucket], err, { ...Cloud.LOG_CLOUD_FAIL });
168
+ this.formatFail(64, service, ["Unable to create bucket", Bucket], err, Cloud.optionsLogMessage('FAIL'));
168
169
  return false;
169
170
  }
170
171
  });
@@ -180,12 +181,12 @@ async function setBucketPolicy(credential, Bucket, options, service = "aws", sdk
180
181
  }
181
182
  return ('PublicAccessBlockConfiguration' in options ? S3.putPublicAccessBlock(options) : 'Policy' in options && (0, types_1.isString)(options.Policy) && !ibm ? S3.putBucketPolicy(options) : S3.putBucketAcl(options)).promise()
182
183
  .then(() => {
183
- this.formatMessage(64, service, ["Bucket policy configured", Bucket], '', { ...Cloud.LOG_CLOUD_COMMAND });
184
+ this.formatMessage(64, service, ["Bucket policy configured", Bucket], '', Cloud.optionsLogMessage('COMMAND'));
184
185
  return true;
185
186
  })
186
187
  .catch((err) => {
187
188
  if (!isNoSuchBucket(err)) {
188
- this.formatFail(64, service, ["Unable to update bucket policy", Bucket], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: false });
189
+ this.formatFail(64, service, ["Unable to update bucket policy", Bucket], err, Cloud.optionsLogMessage('FAIL', { fatal: false }));
189
190
  }
190
191
  return false;
191
192
  });
@@ -196,21 +197,25 @@ async function setBucketTagging(credential, Bucket, options, service = "aws", sd
196
197
  }
197
198
  const S3 = createStorageClient.call(this, credential, service, sdk);
198
199
  const deleting = options.Tagging.TagSet.length === 0;
199
- const command = () => {
200
- this.formatMessage(64, service, [deleting ? "Tags deleted" : "Tags created", Bucket], null, { ...Cloud.LOG_CLOUD_COMMAND });
200
+ const success = () => {
201
+ this.formatMessage(64, service, [deleting ? "Tags deleted" : "Tags created", Bucket], null, Cloud.optionsLogMessage('COMMAND'));
201
202
  return true;
202
203
  };
203
- const error = (err) => {
204
+ const failed = (err) => {
204
205
  if (!isNoSuchBucket(err)) {
205
- this.formatFail(64, service, ["Unable to update bucket tagging", Bucket], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: false });
206
+ this.formatFail(64, service, ["Unable to update bucket tagging", Bucket], err, Cloud.optionsLogMessage('FAIL', { fatal: false }));
206
207
  }
207
208
  return false;
208
209
  };
209
210
  if (deleting) {
210
- return S3.deleteBucketTagging({ Bucket, ExpectedBucketOwner: options.ExpectedBucketOwner }).promise().then(command).catch((err) => error(err));
211
+ return S3.deleteBucketTagging({ Bucket, ExpectedBucketOwner: options.ExpectedBucketOwner }).promise()
212
+ .then(success)
213
+ .catch(failed);
211
214
  }
212
215
  options.Bucket = Bucket;
213
- return S3.putBucketTagging(options).promise().then(command).catch(error);
216
+ return S3.putBucketTagging(options).promise()
217
+ .then(success)
218
+ .catch(failed);
214
219
  }
215
220
  async function setBucketWebsite(credential, Bucket, options, service = "aws", sdk = "aws-sdk/clients/s3") {
216
221
  const S3 = createStorageClient.call(this, credential, service, sdk);
@@ -224,12 +229,12 @@ async function setBucketWebsite(credential, Bucket, options, service = "aws", sd
224
229
  }
225
230
  return S3.putBucketWebsite({ Bucket, WebsiteConfiguration }).promise()
226
231
  .then(() => {
227
- this.formatMessage(64, service, ["Bucket website configured", Bucket], WebsiteConfiguration, { ...Cloud.LOG_CLOUD_COMMAND });
232
+ this.formatMessage(64, service, ["Bucket website configured", Bucket], WebsiteConfiguration, Cloud.optionsLogMessage('COMMAND'));
228
233
  return true;
229
234
  })
230
235
  .catch((err) => {
231
236
  if (!isNoSuchBucket(err)) {
232
- this.formatFail(64, service, ["Unable to set bucket website", Bucket], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: false });
237
+ this.formatFail(64, service, ["Unable to set bucket website", Bucket], err, Cloud.optionsLogMessage('FAIL', { fatal: false }));
233
238
  }
234
239
  return false;
235
240
  });
@@ -247,7 +252,7 @@ async function deleteObjectsV3(credential, Bucket, options = {}, service = "aws"
247
252
  recursive = options.recursive;
248
253
  delete options.recursive;
249
254
  }
250
- options.Bucket ||= Bucket;
255
+ options.Bucket = Bucket;
251
256
  const S3 = createStorageClient.call(this, credential, service, sdk);
252
257
  return S3.listObjects(options).promise()
253
258
  .then(async ({ Contents }) => {
@@ -261,20 +266,35 @@ async function deleteObjectsV3(credential, Bucket, options = {}, service = "aws"
261
266
  const Deleted = data.Deleted;
262
267
  if ((0, types_1.isArray)(Deleted)) {
263
268
  const files = Deleted.length + ' files';
264
- this.formatMessage(64, service, ["Bucket emptied" + ` (${recursive ? 'recursive' : files})`, Bucket], recursive ? files : '', { ...Cloud.LOG_CLOUD_COMMAND });
269
+ this.formatMessage(64, service, ["Bucket emptied" + ` (${recursive ? 'recursive' : files})`, Bucket], recursive ? files : '', Cloud.optionsLogMessage('COMMAND'));
265
270
  }
266
271
  })
267
272
  .catch((err) => {
268
- this.formatFail(64, service, ["Unable to empty bucket", Bucket], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: false });
273
+ this.formatFail(64, service, ["Unable to empty bucket", Bucket], err, Cloud.optionsLogMessage('FAIL', { fatal: false }));
269
274
  });
270
275
  }
271
276
  })
272
277
  .catch((err) => {
273
278
  if (!isNoSuchBucket(err)) {
274
- this.formatFail(64, service, ["Unable to list bucket", Bucket], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: false });
279
+ this.formatFail(64, service, ["Unable to list bucket", Bucket], err, Cloud.optionsLogMessage('FAIL', { fatal: false }));
275
280
  }
276
281
  });
277
282
  }
283
+ async function copyObject(credential, BucketSource, KeySource, Bucket, Key, options = {}, service = "aws", sdk = "aws-sdk/clients/s3") {
284
+ const S3 = createStorageClient.call(this, credential, service, sdk);
285
+ const index = Key.lastIndexOf('/');
286
+ if (index !== -1) {
287
+ await S3.putObject({ Bucket, Key: Key.substring(0, index + 1), Body: Buffer.from(''), ContentLength: 0 }).promise().catch(() => { });
288
+ }
289
+ const CopySource = BucketSource + '/' + KeySource;
290
+ return S3.copyObject({ ...options, Bucket, Key, CopySource }).promise()
291
+ .then(() => {
292
+ this.formatMessage(64, service, ["Copy success", Bucket], CopySource, Cloud.optionsLogMessage('COMMAND'));
293
+ })
294
+ .catch((err) => {
295
+ this.formatFail(64, service, ["Copy failed", Bucket + '/' + Key], err, Cloud.optionsLogMessage('FAIL', { fatal: false }));
296
+ });
297
+ }
278
298
  async function executeQuery(credential, data, sessionKey) {
279
299
  return (await executeBatchQuery.call(this, credential, [data], sessionKey))[0] || [];
280
300
  }
@@ -419,7 +439,7 @@ function parseAttributeValue(value) {
419
439
  return { NULL: true };
420
440
  }
421
441
  function isNoSuchBucket(err) {
422
- return err instanceof Error && err.code === 'NoSuchBucket';
442
+ return (0, types_1.isErrorCode)(err, 'NoSuchBucket');
423
443
  }
424
444
  exports.CLOUD_UPLOAD_STREAM = true;
425
445
  exports.CLOUD_UPLOAD_CHUNK = true;
package/download/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  const Cloud = require("@e-mc/cloud");
3
3
  const types_1 = require("@e-mc/types");
4
+ const util_1 = require("@e-mc/cloud/util");
4
5
  const client_1 = require("@pi-r/aws");
5
6
  function download(credential, service = "aws", sdk = "aws-sdk/clients/s3") {
6
7
  const s3 = client_1.createStorageClient.call(this, credential, service, sdk);
@@ -11,21 +12,30 @@ function download(credential, service = "aws", sdk = "aws-sdk/clients/s3") {
11
12
  callback((0, types_1.errorValue)('Missing property', !Bucket ? 'Bucket' : 'Key'));
12
13
  return;
13
14
  }
14
- const location = Cloud.joinPath(Bucket, Key);
15
- s3.getObject({ ...target.options, Bucket, Key, VersionId: target.versionId }, (err, result) => {
15
+ s3.getObject({ ...target.options, Bucket, Key, VersionId: target.versionId }, async (err, result) => {
16
16
  if (!err) {
17
17
  callback(null, result.Body);
18
- let deleteObject = target.deleteObject;
19
- if (deleteObject) {
20
- if (!(0, types_1.isPlainObject)(deleteObject)) {
21
- deleteObject = undefined;
18
+ const copyTo = (0, util_1.intoArray)(target.copyObject);
19
+ if (copyTo) {
20
+ const tasks = [];
21
+ for (const { bucket, pathname, filename, options } of copyTo) {
22
+ const keyObject = filename ? Cloud.joinPath(pathname, filename, true) : Key;
23
+ tasks.push(client_1.copyObject
24
+ .call(this, credential, Bucket, Key, bucket, keyObject, options, service, sdk)
25
+ .catch((0, util_1.createErrorHandler)(this, service, Bucket)));
22
26
  }
23
- s3.deleteObject({ ...deleteObject, Bucket, Key, VersionId: target.versionId }, error => {
27
+ await Promise.all(tasks);
28
+ }
29
+ const deleteObject = target.deleteObject;
30
+ if (deleteObject) {
31
+ const location = Cloud.joinPath(Bucket, Key);
32
+ const deleteOptions = (0, types_1.isPlainObject)(deleteObject) ? deleteObject : undefined;
33
+ s3.deleteObject({ ...deleteOptions, Bucket, Key, VersionId: target.versionId }, error => {
24
34
  if (!error) {
25
- this.formatMessage(64, service, "Delete success", location, { ...Cloud.LOG_CLOUD_DELETE });
35
+ this.formatMessage(64, service, "Delete success", location, Cloud.optionsLogMessage('DELETE'));
26
36
  }
27
37
  else {
28
- this.formatFail(64, service, ["Delete failed", location], error, { ...Cloud.LOG_CLOUD_FAIL, fatal: !!target.active });
38
+ this.formatFail(64, service, ["Delete failed", location], error, Cloud.optionsLogMessage('FAIL', { fatal: !!target.active }));
29
39
  }
30
40
  });
31
41
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-r/aws",
3
- "version": "0.10.2",
3
+ "version": "0.11.0",
4
4
  "description": "AWS V2 cloud functions for E-mc.",
5
5
  "main": "client/index.js",
6
6
  "types": "client/index.d.ts",
@@ -20,10 +20,10 @@
20
20
  "license": "MIT",
21
21
  "homepage": "https://github.com/anpham6/pi-r#readme",
22
22
  "dependencies": {
23
- "@e-mc/cloud": "^0.12.5",
24
- "@e-mc/module": "^0.12.5",
25
- "@e-mc/types": "^0.12.5",
26
- "@pi-r/aws-lib": "^0.10.2",
23
+ "@e-mc/cloud": "^0.13.0",
24
+ "@e-mc/module": "^0.13.0",
25
+ "@e-mc/types": "^0.13.0",
26
+ "@pi-r/aws-lib": "^0.11.0",
27
27
  "aws-sdk": "^2.1692.0"
28
28
  }
29
29
  }
package/upload/index.js CHANGED
@@ -15,29 +15,22 @@ function upload(credential, service = "aws", sdk = "aws-sdk/clients/s3") {
15
15
  const s3 = client_1.createStorageClient.call(this, credential, service, sdk);
16
16
  return async (data, callback) => {
17
17
  const { bucket: Bucket, localUri } = data;
18
- const { pathname = '', flags = 0, fileGroup, contentType, metadata, tags: Tags, endpoint, active, publicRead, acl, admin = {}, overwrite, options } = data.upload;
18
+ const { pathname, flags = 0, fileGroup, contentType, metadata, tags: Tags, endpoint, active, publicRead, acl, admin = {}, overwrite, options } = data.upload;
19
19
  let filename = data.upload.filename || path.basename(localUri), bucketKey;
20
- const cleanup = () => {
20
+ const complete = (err, url) => {
21
21
  BUCKET_SESSION.delete(service + Bucket);
22
22
  if (bucketKey) {
23
23
  delete BUCKET_RESPONSE[bucketKey];
24
24
  }
25
+ callback(err, url);
25
26
  };
26
- const errorResponse = (err) => {
27
- cleanup();
28
- callback(err);
29
- };
30
- const addLog = (err) => {
31
- if (err instanceof Error) {
32
- this.addLog(this.statusType.WARN, err, service, Bucket);
33
- }
34
- };
27
+ const addLog = (0, util_1.createErrorHandler)(this, Bucket, service);
35
28
  const configBucket = admin.configBucket;
36
29
  if (!BUCKET_SESSION.has(service + Bucket)) {
37
30
  const bucketAcl = admin.publicRead ? "public-read" : admin.acl;
38
31
  const response = BUCKET_RESPONSE[bucketKey = (0, aws_lib_1.getBucketKey)(credential, Bucket, bucketAcl, service, sdk)] ||= client_1.createBucketV2.call(this, credential, Bucket, bucketAcl, configBucket?.create, service, sdk);
39
32
  if (!await response) {
40
- errorResponse(null);
33
+ complete(null);
41
34
  return;
42
35
  }
43
36
  BUCKET_SESSION.add(service + Bucket);
@@ -52,7 +45,7 @@ function upload(credential, service = "aws", sdk = "aws-sdk/clients/s3") {
52
45
  if ((0, types_1.isPlainObject)(DefaultRetention)) {
53
46
  s3.putObjectLockConfiguration({ Bucket, ObjectLockConfiguration: { ObjectLockEnabled: 'Enabled', Rule: { DefaultRetention } }, ExpectedBucketOwner, RequestPayer: options?.RequestPayer }, err => {
54
47
  if (!err) {
55
- this.formatMessage(64, service, ["Bucket configured" + ' (Retention Policy)', Bucket], (0, aws_lib_1.formatDefaultRetention)(DefaultRetention), { ...Cloud.LOG_CLOUD_COMMAND });
48
+ this.formatMessage(64, service, ["Bucket configured" + ' (Retention Policy)', Bucket], (0, aws_lib_1.formatDefaultRetention)(DefaultRetention), Cloud.optionsLogMessage('COMMAND'));
56
49
  }
57
50
  else {
58
51
  addLog(err);
@@ -116,7 +109,7 @@ function upload(credential, service = "aws", sdk = "aws-sdk/clients/s3") {
116
109
  break;
117
110
  }
118
111
  }
119
- exists = await s3.headObject({ Bucket, Key: pathname ? Cloud.joinPath(pathname, filename) : filename }).promise()
112
+ exists = await s3.headObject({ Bucket, Key: Cloud.joinPath(pathname, filename, true) }).promise()
120
113
  .then(() => true)
121
114
  .catch((err) => {
122
115
  if (err instanceof Error && err.code !== 'NotFound') {
@@ -126,11 +119,11 @@ function upload(credential, service = "aws", sdk = "aws-sdk/clients/s3") {
126
119
  });
127
120
  } while (exists && ++i);
128
121
  if (i > 0) {
129
- this.formatMessage(64, service, ["File renamed", current], filename, { ...Cloud.LOG_CLOUD_WARN });
122
+ this.formatMessage(64, service, ["File renamed", current], filename, Cloud.optionsLogMessage('WARN'));
130
123
  }
131
124
  }
132
125
  if (pathname) {
133
- await s3.putObject({ Bucket, Key: pathname, Body: Buffer.from(''), ContentLength: 0 }).promise().catch(() => { });
126
+ await s3.putObject({ Bucket, Key: pathname.endsWith('/') ? pathname : pathname + '/', Body: Buffer.from(''), ContentLength: 0 }).promise().catch(() => { });
134
127
  }
135
128
  const partSize = flags & 4 ? (0, types_1.alignSize)(data.upload.chunkSize, 1024) : 0;
136
129
  const Key = [filename];
@@ -148,7 +141,7 @@ function upload(credential, service = "aws", sdk = "aws-sdk/clients/s3") {
148
141
  }
149
142
  }
150
143
  catch (err) {
151
- errorResponse(err);
144
+ complete(err);
152
145
  return;
153
146
  }
154
147
  }
@@ -170,11 +163,11 @@ function upload(credential, service = "aws", sdk = "aws-sdk/clients/s3") {
170
163
  const first = i === 0;
171
164
  if (this.aborted) {
172
165
  if (first) {
173
- errorResponse((0, types_1.createAbortError)());
166
+ complete((0, types_1.createAbortError)());
174
167
  }
175
168
  return;
176
169
  }
177
- const params = { ...options, Bucket, Key: pathname + Key[i], Body: Stream[i] || Body[i] };
170
+ const params = { ...options, Bucket, Key: Cloud.joinPath(pathname, Key[i], true), Body: Stream[i] || Body[i] };
178
171
  const readable = publicRead || active && publicRead !== false && !acl;
179
172
  let tags, length = -1;
180
173
  if (first) {
@@ -214,7 +207,7 @@ function upload(credential, service = "aws", sdk = "aws-sdk/clients/s3") {
214
207
  new aws.S3.ManagedUpload({ service: s3, params, tags, partSize: getPartSize(), queueSize: partSize > 0 ? data.upload.chunkLimit : undefined }).send((err, result) => {
215
208
  if (err) {
216
209
  if (first) {
217
- errorResponse(err);
210
+ complete(err);
218
211
  }
219
212
  else {
220
213
  addLog(err);
@@ -222,20 +215,30 @@ function upload(credential, service = "aws", sdk = "aws-sdk/clients/s3") {
222
215
  return;
223
216
  }
224
217
  const url = endpoint ? Cloud.joinPath(endpoint, result.Key) : result.Location;
225
- this.formatMessage(64, service, "Upload success", url, { ...Cloud.LOG_CLOUD_UPLOAD });
218
+ this.formatMessage(64, service, "Upload success", url, Cloud.optionsLogMessage('UPLOAD'));
226
219
  if (first) {
227
220
  if (length === 0) {
228
221
  s3.deleteObjectTagging({ Bucket, Key: result.Key, ExpectedBucketOwner: params.ExpectedBucketOwner }, error => {
229
222
  if (!error) {
230
- this.formatMessage(64, service, ["Tags deleted", Bucket], result.Key, { ...Cloud.LOG_CLOUD_COMMAND });
223
+ this.formatMessage(64, service, ["Tags deleted", Bucket], result.Key, Cloud.optionsLogMessage('COMMAND'));
231
224
  }
232
225
  else {
233
226
  addLog(error);
234
227
  }
235
228
  });
236
229
  }
237
- cleanup();
238
- callback(null, url);
230
+ const copyTo = (0, util_1.intoArray)(data.upload.copyObject);
231
+ if (copyTo) {
232
+ const tasks = [];
233
+ for (const { bucket: bucketName, pathname: pathObject = pathname, filename: fileObject, options: copyOptions } of copyTo) {
234
+ const keyObject = fileObject ? Cloud.joinPath(pathObject, fileObject, true) : result.Key;
235
+ tasks.push(client_1.copyObject
236
+ .call(this, credential, Bucket, result.Key, bucketName, keyObject, copyOptions, service, sdk)
237
+ .catch(addLog));
238
+ }
239
+ void Promise.all(tasks);
240
+ }
241
+ complete(null, url);
239
242
  }
240
243
  });
241
244
  }