@nestm/storage 0.1.0-alpha.2 → 0.1.0-alpha.4
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/CHANGELOG.md +44 -0
- package/README.md +196 -15
- package/SECURITY.md +23 -0
- package/dist/files-sdk/files-sdk.driver.d.ts +31 -2
- package/dist/files-sdk/files-sdk.driver.d.ts.map +1 -1
- package/dist/files-sdk/files-sdk.driver.js +141 -20
- package/dist/files-sdk/files-sdk.driver.js.map +1 -1
- package/dist/files-sdk/fs/index.d.ts +21 -0
- package/dist/files-sdk/fs/index.d.ts.map +1 -0
- package/dist/files-sdk/fs/index.js +20 -0
- package/dist/files-sdk/fs/index.js.map +1 -0
- package/dist/files-sdk/index.d.ts +1 -1
- package/dist/files-sdk/index.d.ts.map +1 -1
- package/dist/files-sdk/index.js.map +1 -1
- package/dist/files-sdk/provider/index.d.ts +54 -0
- package/dist/files-sdk/provider/index.d.ts.map +1 -0
- package/dist/files-sdk/provider/index.js +80 -0
- package/dist/files-sdk/provider/index.js.map +1 -0
- package/dist/files-sdk/s3/index.d.ts +25 -0
- package/dist/files-sdk/s3/index.d.ts.map +1 -0
- package/dist/files-sdk/s3/index.js +119 -0
- package/dist/files-sdk/s3/index.js.map +1 -0
- package/dist/gateway/index.d.ts +1 -1
- package/dist/gateway/index.d.ts.map +1 -1
- package/dist/gateway/index.js.map +1 -1
- package/dist/gateway/storage-gateway-fastify-parser.d.ts.map +1 -1
- package/dist/gateway/storage-gateway-fastify-parser.js.map +1 -1
- package/dist/gateway/storage-gateway.controller.d.ts +12 -11
- package/dist/gateway/storage-gateway.controller.d.ts.map +1 -1
- package/dist/gateway/storage-gateway.controller.js +209 -65
- package/dist/gateway/storage-gateway.controller.js.map +1 -1
- package/dist/gateway/storage-gateway.guard.d.ts.map +1 -1
- package/dist/gateway/storage-gateway.guard.js.map +1 -1
- package/dist/gateway/storage-gateway.module.d.ts.map +1 -1
- package/dist/gateway/storage-gateway.module.js +60 -1
- package/dist/gateway/storage-gateway.module.js.map +1 -1
- package/dist/gateway/storage-gateway.tokens.d.ts +1 -0
- package/dist/gateway/storage-gateway.tokens.d.ts.map +1 -1
- package/dist/gateway/storage-gateway.tokens.js +1 -0
- package/dist/gateway/storage-gateway.tokens.js.map +1 -1
- package/dist/gateway/storage-gateway.types.d.ts +53 -10
- package/dist/gateway/storage-gateway.types.d.ts.map +1 -1
- package/dist/gateway/storage-gateway.types.js.map +1 -1
- package/dist/inject-storage.decorator.js.map +1 -1
- package/dist/storage-upload-control.d.ts.map +1 -1
- package/dist/storage.client.d.ts +8 -1
- package/dist/storage.client.d.ts.map +1 -1
- package/dist/storage.client.js +40 -0
- package/dist/storage.client.js.map +1 -1
- package/dist/storage.driver.d.ts +6 -1
- package/dist/storage.driver.d.ts.map +1 -1
- package/dist/storage.driver.js.map +1 -1
- package/dist/storage.error.d.ts +13 -10
- package/dist/storage.error.d.ts.map +1 -1
- package/dist/storage.error.js +33 -1
- package/dist/storage.error.js.map +1 -1
- package/dist/storage.module.d.ts.map +1 -1
- package/dist/storage.module.js.map +1 -1
- package/dist/storage.service.d.ts.map +1 -1
- package/dist/storage.service.js.map +1 -1
- package/dist/storage.tokens.js.map +1 -1
- package/dist/storage.types.d.ts +39 -1
- package/dist/storage.types.d.ts.map +1 -1
- package/dist/storage.types.js.map +1 -1
- package/dist/testing/index.js.map +1 -1
- package/package.json +38 -6
|
@@ -14,12 +14,83 @@ import { BadRequestException, Body, Controller, Delete, Get, Head, HttpException
|
|
|
14
14
|
import { ServerResponse } from 'node:http';
|
|
15
15
|
import { Readable } from 'node:stream';
|
|
16
16
|
import { pipeline } from 'node:stream/promises';
|
|
17
|
-
import { StorageError, StorageErrorCode } from '../storage.error.js';
|
|
17
|
+
import { StorageError, StorageErrorCode, isStorageError, } from '../storage.error.js';
|
|
18
18
|
import { StorageService } from '../storage.service.js';
|
|
19
19
|
import { StorageGatewayGuard } from './storage-gateway.guard.js';
|
|
20
20
|
import { FASTIFY_STORAGE_UPLOAD_CONFIG } from './storage-gateway-fastify-parser.js';
|
|
21
|
-
import { STORAGE_GATEWAY_OPTIONS } from './storage-gateway.tokens.js';
|
|
21
|
+
import { STORAGE_GATEWAY_KEY_POLICY, STORAGE_GATEWAY_OPTIONS, } from './storage-gateway.tokens.js';
|
|
22
22
|
import { StorageGatewayOperation, } from './storage-gateway.types.js';
|
|
23
|
+
const MAX_GATEWAY_PATH_LENGTH = 1024;
|
|
24
|
+
function parseGatewayPath(value, target) {
|
|
25
|
+
if (value === undefined) {
|
|
26
|
+
if (target === 'prefix') {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
throw new BadRequestException(`${target} is required.`);
|
|
30
|
+
}
|
|
31
|
+
if (value.length > MAX_GATEWAY_PATH_LENGTH) {
|
|
32
|
+
throw new BadRequestException(`${target} cannot exceed ${MAX_GATEWAY_PATH_LENGTH} characters.`);
|
|
33
|
+
}
|
|
34
|
+
if (value.length === 0) {
|
|
35
|
+
if (target === 'prefix') {
|
|
36
|
+
return Object.freeze({
|
|
37
|
+
segments: Object.freeze([]),
|
|
38
|
+
trailingSlash: false,
|
|
39
|
+
value,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
throw new BadRequestException(`${target} must be a non-empty string.`);
|
|
43
|
+
}
|
|
44
|
+
if ([...value].some((character) => {
|
|
45
|
+
const codePoint = character.codePointAt(0);
|
|
46
|
+
return (character === '\\' ||
|
|
47
|
+
codePoint === undefined ||
|
|
48
|
+
codePoint <= 0x1f ||
|
|
49
|
+
codePoint === 0x7f);
|
|
50
|
+
})) {
|
|
51
|
+
throw new BadRequestException(`${target} cannot contain control characters or backslashes.`);
|
|
52
|
+
}
|
|
53
|
+
if (target === 'pattern') {
|
|
54
|
+
return Object.freeze({
|
|
55
|
+
segments: Object.freeze(value.split('/')),
|
|
56
|
+
trailingSlash: value.endsWith('/'),
|
|
57
|
+
value,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (value.startsWith('/') || value.trim() !== value) {
|
|
61
|
+
throw new BadRequestException(`${target} must be a relative object path without surrounding whitespace.`);
|
|
62
|
+
}
|
|
63
|
+
const trailingSlash = value.endsWith('/');
|
|
64
|
+
const segments = value.split('/');
|
|
65
|
+
if (trailingSlash) {
|
|
66
|
+
segments.pop();
|
|
67
|
+
}
|
|
68
|
+
if (segments.some((segment) => segment.length === 0 || segment === '.' || segment === '..')) {
|
|
69
|
+
throw new BadRequestException(`${target} cannot contain empty, dot, or parent path segments.`);
|
|
70
|
+
}
|
|
71
|
+
return Object.freeze({
|
|
72
|
+
segments: Object.freeze(segments),
|
|
73
|
+
trailingSlash,
|
|
74
|
+
value,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function exactContentType(value, field) {
|
|
78
|
+
const normalized = value.toLowerCase();
|
|
79
|
+
if (value !== normalized ||
|
|
80
|
+
!/^[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*$/u.test(value)) {
|
|
81
|
+
throw new BadRequestException(`${field} must be a lowercase exact MIME type without parameters.`);
|
|
82
|
+
}
|
|
83
|
+
return normalized;
|
|
84
|
+
}
|
|
85
|
+
function safeContentDisposition(value) {
|
|
86
|
+
if (value === undefined) {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
if (value !== 'attachment' && value !== 'inline') {
|
|
90
|
+
throw new BadRequestException('responseContentDisposition must be either "attachment" or "inline".');
|
|
91
|
+
}
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
23
94
|
function recordOf(value, label) {
|
|
24
95
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
25
96
|
throw new BadRequestException(`${label} must be a JSON object.`);
|
|
@@ -190,7 +261,7 @@ function toHttpException(error) {
|
|
|
190
261
|
if (error instanceof HttpException) {
|
|
191
262
|
return error;
|
|
192
263
|
}
|
|
193
|
-
if (error
|
|
264
|
+
if (isStorageError(error)) {
|
|
194
265
|
return new HttpException({
|
|
195
266
|
error: {
|
|
196
267
|
code: error.code,
|
|
@@ -208,24 +279,28 @@ function toHttpException(error) {
|
|
|
208
279
|
let StorageGatewayController = class StorageGatewayController {
|
|
209
280
|
storage;
|
|
210
281
|
options;
|
|
211
|
-
|
|
282
|
+
keyPolicy;
|
|
283
|
+
constructor(storage, options, keyPolicy) {
|
|
212
284
|
this.storage = storage;
|
|
213
285
|
this.options = options;
|
|
286
|
+
this.keyPolicy = keyPolicy;
|
|
214
287
|
}
|
|
215
|
-
async download(key, proxy, rangeStart, rangeEnd, disposition, response) {
|
|
288
|
+
async download(key, proxy, rangeStart, rangeEnd, disposition, request, response) {
|
|
216
289
|
await this.#run(async () => {
|
|
217
290
|
this.#assertAllowed(StorageGatewayOperation.DOWNLOAD);
|
|
218
|
-
const
|
|
291
|
+
const responseContentDisposition = safeContentDisposition(disposition);
|
|
219
292
|
const client = this.storage.use(this.options.store);
|
|
220
293
|
const forceProxy = proxy === 'true' || proxy === '1';
|
|
221
294
|
const canSign = this.options.operations.has(StorageGatewayOperation.SIGN_DOWNLOAD) &&
|
|
222
|
-
client.capabilities.signedDownload.supported
|
|
295
|
+
client.capabilities.signedDownload.supported &&
|
|
296
|
+
client.capabilities.signedDownloadPolicy?.expiresIn === true;
|
|
223
297
|
if (!forceProxy && this.options.mode !== 'proxy' && canSign) {
|
|
224
298
|
try {
|
|
225
|
-
const
|
|
299
|
+
const signedObjectKey = await this.#resolvePath(request, StorageGatewayOperation.SIGN_DOWNLOAD, 'key', key);
|
|
300
|
+
const url = await client.signDownload(signedObjectKey, {
|
|
226
301
|
expiresIn: this.options.defaultSignedUrlExpiresIn,
|
|
227
|
-
...(
|
|
228
|
-
responseContentDisposition
|
|
302
|
+
...(responseContentDisposition !== undefined && {
|
|
303
|
+
responseContentDisposition,
|
|
229
304
|
}),
|
|
230
305
|
});
|
|
231
306
|
const raw = rawResponseOf(response);
|
|
@@ -236,7 +311,7 @@ let StorageGatewayController = class StorageGatewayController {
|
|
|
236
311
|
}
|
|
237
312
|
catch (error) {
|
|
238
313
|
if (this.options.mode !== 'hybrid' ||
|
|
239
|
-
!(error
|
|
314
|
+
!isStorageError(error) ||
|
|
240
315
|
error.code !== StorageErrorCode.NOT_SUPPORTED) {
|
|
241
316
|
throw error;
|
|
242
317
|
}
|
|
@@ -245,10 +320,10 @@ let StorageGatewayController = class StorageGatewayController {
|
|
|
245
320
|
if (this.options.mode === 'signed') {
|
|
246
321
|
throw new StorageError('Signed downloads are unavailable for this store.', {
|
|
247
322
|
code: StorageErrorCode.NOT_SUPPORTED,
|
|
248
|
-
key: objectKey,
|
|
249
323
|
permanent: true,
|
|
250
324
|
});
|
|
251
325
|
}
|
|
326
|
+
const objectKey = await this.#resolvePath(request, StorageGatewayOperation.DOWNLOAD, 'key', key);
|
|
252
327
|
const start = queryInteger(rangeStart, 'rangeStart');
|
|
253
328
|
const end = queryInteger(rangeEnd, 'rangeEnd');
|
|
254
329
|
if (end !== undefined && start === undefined) {
|
|
@@ -300,7 +375,7 @@ let StorageGatewayController = class StorageGatewayController {
|
|
|
300
375
|
permanent: true,
|
|
301
376
|
});
|
|
302
377
|
}
|
|
303
|
-
const objectKey = this.#
|
|
378
|
+
const objectKey = await this.#resolvePath(request, StorageGatewayOperation.UPLOAD, 'key', key);
|
|
304
379
|
const transportContentType = requestHeader(request, 'content-type')
|
|
305
380
|
?.split(';', 1)[0]
|
|
306
381
|
?.trim()
|
|
@@ -322,53 +397,57 @@ let StorageGatewayController = class StorageGatewayController {
|
|
|
322
397
|
}
|
|
323
398
|
const source = uploadSourceOf(request);
|
|
324
399
|
const body = Readable.toWeb(Readable.from(enforceUploadLimit(source, this.options.maxUploadBytes)));
|
|
325
|
-
const contentType = requestHeader(request, 'x-storage-content-type') ??
|
|
326
|
-
'application/octet-stream';
|
|
400
|
+
const contentType = exactContentType(requestHeader(request, 'x-storage-content-type') ??
|
|
401
|
+
'application/octet-stream', 'x-storage-content-type');
|
|
327
402
|
const result = await this.storage
|
|
328
403
|
.use(this.options.store)
|
|
329
404
|
.upload(objectKey, body, { contentType, multipart: true });
|
|
330
405
|
return { data: result };
|
|
331
406
|
});
|
|
332
407
|
}
|
|
333
|
-
async head(key, response) {
|
|
408
|
+
async head(key, request, response) {
|
|
334
409
|
await this.#run(async () => {
|
|
335
410
|
this.#assertAllowed(StorageGatewayOperation.HEAD);
|
|
411
|
+
const objectKey = await this.#resolvePath(request, StorageGatewayOperation.HEAD, 'key', key);
|
|
336
412
|
const metadata = await this.storage
|
|
337
413
|
.use(this.options.store)
|
|
338
|
-
.head(
|
|
414
|
+
.head(objectKey);
|
|
339
415
|
const raw = rawResponseOf(response);
|
|
340
416
|
raw.statusCode = HttpStatus.OK;
|
|
341
417
|
setObjectHeaders(raw, metadata);
|
|
342
418
|
raw.end();
|
|
343
419
|
});
|
|
344
420
|
}
|
|
345
|
-
async delete(key) {
|
|
421
|
+
async delete(key, request) {
|
|
346
422
|
return this.#run(async () => {
|
|
347
423
|
this.#assertAllowed(StorageGatewayOperation.DELETE);
|
|
348
|
-
await this
|
|
424
|
+
const objectKey = await this.#resolvePath(request, StorageGatewayOperation.DELETE, 'key', key);
|
|
425
|
+
await this.storage.use(this.options.store).delete(objectKey);
|
|
349
426
|
return { data: { deleted: true } };
|
|
350
427
|
});
|
|
351
428
|
}
|
|
352
|
-
async list(prefix, cursor, limit, delimiter) {
|
|
429
|
+
async list(prefix, cursor, limit, delimiter, request) {
|
|
353
430
|
return this.#run(async () => {
|
|
354
431
|
this.#assertAllowed(StorageGatewayOperation.LIST);
|
|
355
432
|
const parsedLimit = queryInteger(limit, 'limit') ?? this.options.maxListResults;
|
|
356
433
|
if (parsedLimit === 0 || parsedLimit > this.options.maxListResults) {
|
|
357
434
|
throw new BadRequestException(`limit must be between 1 and ${this.options.maxListResults}.`);
|
|
358
435
|
}
|
|
436
|
+
const resolvedPrefix = await this.#resolvePath(request, StorageGatewayOperation.LIST, 'prefix', prefix);
|
|
359
437
|
const result = await this.storage.use(this.options.store).list({
|
|
360
438
|
...(cursor !== undefined && { cursor }),
|
|
361
439
|
...(delimiter !== undefined && { delimiter }),
|
|
362
440
|
limit: parsedLimit,
|
|
363
|
-
|
|
441
|
+
prefix: resolvedPrefix,
|
|
364
442
|
});
|
|
365
443
|
return { data: result };
|
|
366
444
|
});
|
|
367
445
|
}
|
|
368
|
-
async search(pattern, prefix, limit, maxResults, match, caseInsensitive) {
|
|
446
|
+
async search(pattern, prefix, limit, maxResults, match, caseInsensitive, request) {
|
|
369
447
|
return this.#run(async () => {
|
|
370
448
|
this.#assertAllowed(StorageGatewayOperation.SEARCH);
|
|
371
|
-
const searchPattern = this.#
|
|
449
|
+
const searchPattern = await this.#resolvePath(request, StorageGatewayOperation.SEARCH, 'pattern', pattern);
|
|
450
|
+
const resolvedPrefix = await this.#resolvePath(request, StorageGatewayOperation.SEARCH, 'prefix', prefix);
|
|
372
451
|
if (match !== undefined &&
|
|
373
452
|
!['glob', 'regex', 'substring', 'exact'].includes(match)) {
|
|
374
453
|
throw new BadRequestException('Invalid search match mode.');
|
|
@@ -395,7 +474,7 @@ let StorageGatewayController = class StorageGatewayController {
|
|
|
395
474
|
match: match,
|
|
396
475
|
}),
|
|
397
476
|
maxResults: parsedMaxResults,
|
|
398
|
-
|
|
477
|
+
prefix: resolvedPrefix,
|
|
399
478
|
})) {
|
|
400
479
|
objects.push(object);
|
|
401
480
|
if (objects.length === parsedMaxResults) {
|
|
@@ -405,7 +484,7 @@ let StorageGatewayController = class StorageGatewayController {
|
|
|
405
484
|
return { data: objects };
|
|
406
485
|
});
|
|
407
486
|
}
|
|
408
|
-
async signDownload(body) {
|
|
487
|
+
async signDownload(body, request) {
|
|
409
488
|
return this.#run(async () => {
|
|
410
489
|
this.#assertAllowed(StorageGatewayOperation.SIGN_DOWNLOAD);
|
|
411
490
|
if (this.options.mode === 'proxy') {
|
|
@@ -415,21 +494,28 @@ let StorageGatewayController = class StorageGatewayController {
|
|
|
415
494
|
});
|
|
416
495
|
}
|
|
417
496
|
const input = recordOf(body, 'body');
|
|
418
|
-
const responseContentDisposition = optionalString(input, 'responseContentDisposition');
|
|
497
|
+
const responseContentDisposition = safeContentDisposition(optionalString(input, 'responseContentDisposition'));
|
|
419
498
|
const options = {
|
|
420
|
-
expiresIn:
|
|
421
|
-
this.options.defaultSignedUrlExpiresIn,
|
|
499
|
+
expiresIn: this.#signedExpiry(input),
|
|
422
500
|
...(responseContentDisposition !== undefined && {
|
|
423
501
|
responseContentDisposition,
|
|
424
502
|
}),
|
|
425
503
|
};
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
504
|
+
const objectKey = await this.#resolvePath(request, StorageGatewayOperation.SIGN_DOWNLOAD, 'key', requiredString(input, 'key'));
|
|
505
|
+
const client = this.storage.use(this.options.store);
|
|
506
|
+
if (client.capabilities.signedDownloadPolicy?.expiresIn !== true) {
|
|
507
|
+
throw new StorageError('This store cannot prove that signed-download expiry is provider-enforced.', {
|
|
508
|
+
code: StorageErrorCode.NOT_SUPPORTED,
|
|
509
|
+
key: objectKey,
|
|
510
|
+
operation: 'signDownload',
|
|
511
|
+
permanent: true,
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
const url = await client.signDownload(objectKey, options);
|
|
429
515
|
return { data: { url } };
|
|
430
516
|
});
|
|
431
517
|
}
|
|
432
|
-
async signUpload(body) {
|
|
518
|
+
async signUpload(body, request) {
|
|
433
519
|
return this.#run(async () => {
|
|
434
520
|
this.#assertAllowed(StorageGatewayOperation.SIGN_UPLOAD);
|
|
435
521
|
if (this.options.mode === 'proxy') {
|
|
@@ -439,42 +525,70 @@ let StorageGatewayController = class StorageGatewayController {
|
|
|
439
525
|
});
|
|
440
526
|
}
|
|
441
527
|
const input = recordOf(body, 'body');
|
|
442
|
-
const
|
|
443
|
-
|
|
528
|
+
const rawContentType = optionalString(input, 'contentType');
|
|
529
|
+
if (rawContentType === undefined) {
|
|
530
|
+
throw new BadRequestException('contentType is required for signed uploads.');
|
|
531
|
+
}
|
|
532
|
+
const contentType = exactContentType(rawContentType, 'contentType');
|
|
533
|
+
if (!this.options.signedUploadContentTypes.has(contentType)) {
|
|
534
|
+
throw new BadRequestException(`contentType "${contentType}" is not allowed for signed uploads.`);
|
|
535
|
+
}
|
|
536
|
+
const requestedMaxSize = optionalPositiveInteger(input, 'maxSize');
|
|
537
|
+
if (requestedMaxSize !== undefined &&
|
|
538
|
+
requestedMaxSize > this.options.maxSignedUploadBytes) {
|
|
539
|
+
throw new StorageError(`Signed upload exceeds the ${this.options.maxSignedUploadBytes}-byte gateway limit.`, {
|
|
540
|
+
code: StorageErrorCode.LIMIT_EXCEEDED,
|
|
541
|
+
permanent: true,
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
const maxSize = requestedMaxSize ?? this.options.maxSignedUploadBytes;
|
|
444
545
|
const minSize = optionalNonNegativeInteger(input, 'minSize');
|
|
445
546
|
if (maxSize !== undefined && minSize !== undefined && minSize > maxSize) {
|
|
446
547
|
throw new BadRequestException('minSize cannot be greater than maxSize.');
|
|
447
548
|
}
|
|
448
549
|
const options = {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
...(maxSize !== undefined && { maxSize }),
|
|
550
|
+
contentType,
|
|
551
|
+
expiresIn: this.#signedExpiry(input),
|
|
552
|
+
maxSize,
|
|
453
553
|
...(minSize !== undefined && { minSize }),
|
|
454
554
|
};
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
555
|
+
const objectKey = await this.#resolvePath(request, StorageGatewayOperation.SIGN_UPLOAD, 'key', requiredString(input, 'key'));
|
|
556
|
+
const client = this.storage.use(this.options.store);
|
|
557
|
+
const policyCapability = client.capabilities.signedUploadPolicy;
|
|
558
|
+
if (policyCapability?.contentType !== true ||
|
|
559
|
+
policyCapability.sizeRange !== true) {
|
|
560
|
+
throw new StorageError('This store cannot prove that signed-upload MIME and size constraints are provider-enforced.', {
|
|
561
|
+
code: StorageErrorCode.NOT_SUPPORTED,
|
|
562
|
+
key: objectKey,
|
|
563
|
+
operation: 'signUpload',
|
|
564
|
+
permanent: true,
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
const result = await client.signUpload(objectKey, options);
|
|
458
568
|
return { data: result };
|
|
459
569
|
});
|
|
460
570
|
}
|
|
461
|
-
async copy(body) {
|
|
571
|
+
async copy(body, request) {
|
|
462
572
|
return this.#run(async () => {
|
|
463
573
|
this.#assertAllowed(StorageGatewayOperation.COPY);
|
|
464
574
|
const input = recordOf(body, 'body');
|
|
465
|
-
await
|
|
466
|
-
.
|
|
467
|
-
|
|
575
|
+
const [from, to] = await Promise.all([
|
|
576
|
+
this.#resolvePath(request, StorageGatewayOperation.COPY, 'from', requiredString(input, 'from')),
|
|
577
|
+
this.#resolvePath(request, StorageGatewayOperation.COPY, 'to', requiredString(input, 'to')),
|
|
578
|
+
]);
|
|
579
|
+
await this.storage.use(this.options.store).copy(from, to);
|
|
468
580
|
return { data: { copied: true } };
|
|
469
581
|
});
|
|
470
582
|
}
|
|
471
|
-
async move(body) {
|
|
583
|
+
async move(body, request) {
|
|
472
584
|
return this.#run(async () => {
|
|
473
585
|
this.#assertAllowed(StorageGatewayOperation.MOVE);
|
|
474
586
|
const input = recordOf(body, 'body');
|
|
475
|
-
await
|
|
476
|
-
.
|
|
477
|
-
|
|
587
|
+
const [from, to] = await Promise.all([
|
|
588
|
+
this.#resolvePath(request, StorageGatewayOperation.MOVE, 'from', requiredString(input, 'from')),
|
|
589
|
+
this.#resolvePath(request, StorageGatewayOperation.MOVE, 'to', requiredString(input, 'to')),
|
|
590
|
+
]);
|
|
591
|
+
await this.storage.use(this.options.store).move(from, to);
|
|
478
592
|
return { data: { moved: true } };
|
|
479
593
|
});
|
|
480
594
|
}
|
|
@@ -488,11 +602,31 @@ let StorageGatewayController = class StorageGatewayController {
|
|
|
488
602
|
}, HttpStatus.FORBIDDEN);
|
|
489
603
|
}
|
|
490
604
|
}
|
|
491
|
-
#
|
|
492
|
-
|
|
493
|
-
|
|
605
|
+
async #resolvePath(request, operation, target, value) {
|
|
606
|
+
const input = parseGatewayPath(value, target);
|
|
607
|
+
const resolved = await this.keyPolicy.resolve({
|
|
608
|
+
input,
|
|
609
|
+
operation,
|
|
610
|
+
request,
|
|
611
|
+
target,
|
|
612
|
+
});
|
|
613
|
+
const parsed = parseGatewayPath(resolved, target);
|
|
614
|
+
if (parsed === undefined) {
|
|
615
|
+
throw new StorageError(`Storage gateway key policy did not resolve ${target}.`, {
|
|
616
|
+
code: StorageErrorCode.UNAUTHORIZED,
|
|
617
|
+
operation,
|
|
618
|
+
permanent: true,
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
return parsed.value;
|
|
622
|
+
}
|
|
623
|
+
#signedExpiry(input) {
|
|
624
|
+
const expiresIn = optionalPositiveInteger(input, 'expiresIn') ??
|
|
625
|
+
this.options.defaultSignedUrlExpiresIn;
|
|
626
|
+
if (expiresIn > this.options.maxSignedUrlExpiresIn) {
|
|
627
|
+
throw new BadRequestException(`expiresIn cannot exceed ${this.options.maxSignedUrlExpiresIn} seconds.`);
|
|
494
628
|
}
|
|
495
|
-
return
|
|
629
|
+
return expiresIn;
|
|
496
630
|
}
|
|
497
631
|
async #run(operation) {
|
|
498
632
|
try {
|
|
@@ -510,9 +644,10 @@ __decorate([
|
|
|
510
644
|
__param(2, Query('rangeStart')),
|
|
511
645
|
__param(3, Query('rangeEnd')),
|
|
512
646
|
__param(4, Query('disposition')),
|
|
513
|
-
__param(5,
|
|
647
|
+
__param(5, Req()),
|
|
648
|
+
__param(6, Res()),
|
|
514
649
|
__metadata("design:type", Function),
|
|
515
|
-
__metadata("design:paramtypes", [Object, Object, Object, Object, Object, Object]),
|
|
650
|
+
__metadata("design:paramtypes", [Object, Object, Object, Object, Object, Object, Object]),
|
|
516
651
|
__metadata("design:returntype", Promise)
|
|
517
652
|
], StorageGatewayController.prototype, "download", null);
|
|
518
653
|
__decorate([
|
|
@@ -529,16 +664,18 @@ __decorate([
|
|
|
529
664
|
__decorate([
|
|
530
665
|
Head('metadata'),
|
|
531
666
|
__param(0, Query('key')),
|
|
532
|
-
__param(1,
|
|
667
|
+
__param(1, Req()),
|
|
668
|
+
__param(2, Res()),
|
|
533
669
|
__metadata("design:type", Function),
|
|
534
|
-
__metadata("design:paramtypes", [Object, Object]),
|
|
670
|
+
__metadata("design:paramtypes", [Object, Object, Object]),
|
|
535
671
|
__metadata("design:returntype", Promise)
|
|
536
672
|
], StorageGatewayController.prototype, "head", null);
|
|
537
673
|
__decorate([
|
|
538
674
|
Delete('object'),
|
|
539
675
|
__param(0, Query('key')),
|
|
676
|
+
__param(1, Req()),
|
|
540
677
|
__metadata("design:type", Function),
|
|
541
|
-
__metadata("design:paramtypes", [Object]),
|
|
678
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
542
679
|
__metadata("design:returntype", Promise)
|
|
543
680
|
], StorageGatewayController.prototype, "delete", null);
|
|
544
681
|
__decorate([
|
|
@@ -547,8 +684,9 @@ __decorate([
|
|
|
547
684
|
__param(1, Query('cursor')),
|
|
548
685
|
__param(2, Query('limit')),
|
|
549
686
|
__param(3, Query('delimiter')),
|
|
687
|
+
__param(4, Req()),
|
|
550
688
|
__metadata("design:type", Function),
|
|
551
|
-
__metadata("design:paramtypes", [Object, Object, Object, Object]),
|
|
689
|
+
__metadata("design:paramtypes", [Object, Object, Object, Object, Object]),
|
|
552
690
|
__metadata("design:returntype", Promise)
|
|
553
691
|
], StorageGatewayController.prototype, "list", null);
|
|
554
692
|
__decorate([
|
|
@@ -559,43 +697,49 @@ __decorate([
|
|
|
559
697
|
__param(3, Query('maxResults')),
|
|
560
698
|
__param(4, Query('match')),
|
|
561
699
|
__param(5, Query('caseInsensitive')),
|
|
700
|
+
__param(6, Req()),
|
|
562
701
|
__metadata("design:type", Function),
|
|
563
|
-
__metadata("design:paramtypes", [Object, Object, Object, Object, Object, Object]),
|
|
702
|
+
__metadata("design:paramtypes", [Object, Object, Object, Object, Object, Object, Object]),
|
|
564
703
|
__metadata("design:returntype", Promise)
|
|
565
704
|
], StorageGatewayController.prototype, "search", null);
|
|
566
705
|
__decorate([
|
|
567
706
|
Post('sign-download'),
|
|
568
707
|
__param(0, Body()),
|
|
708
|
+
__param(1, Req()),
|
|
569
709
|
__metadata("design:type", Function),
|
|
570
|
-
__metadata("design:paramtypes", [Object]),
|
|
710
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
571
711
|
__metadata("design:returntype", Promise)
|
|
572
712
|
], StorageGatewayController.prototype, "signDownload", null);
|
|
573
713
|
__decorate([
|
|
574
714
|
Post('sign-upload'),
|
|
575
715
|
__param(0, Body()),
|
|
716
|
+
__param(1, Req()),
|
|
576
717
|
__metadata("design:type", Function),
|
|
577
|
-
__metadata("design:paramtypes", [Object]),
|
|
718
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
578
719
|
__metadata("design:returntype", Promise)
|
|
579
720
|
], StorageGatewayController.prototype, "signUpload", null);
|
|
580
721
|
__decorate([
|
|
581
722
|
Post('copy'),
|
|
582
723
|
__param(0, Body()),
|
|
724
|
+
__param(1, Req()),
|
|
583
725
|
__metadata("design:type", Function),
|
|
584
|
-
__metadata("design:paramtypes", [Object]),
|
|
726
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
585
727
|
__metadata("design:returntype", Promise)
|
|
586
728
|
], StorageGatewayController.prototype, "copy", null);
|
|
587
729
|
__decorate([
|
|
588
730
|
Post('move'),
|
|
589
731
|
__param(0, Body()),
|
|
732
|
+
__param(1, Req()),
|
|
590
733
|
__metadata("design:type", Function),
|
|
591
|
-
__metadata("design:paramtypes", [Object]),
|
|
734
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
592
735
|
__metadata("design:returntype", Promise)
|
|
593
736
|
], StorageGatewayController.prototype, "move", null);
|
|
594
737
|
StorageGatewayController = __decorate([
|
|
595
738
|
Controller('storage'),
|
|
596
739
|
UseGuards(StorageGatewayGuard),
|
|
597
740
|
__param(1, Inject(STORAGE_GATEWAY_OPTIONS)),
|
|
598
|
-
|
|
741
|
+
__param(2, Inject(STORAGE_GATEWAY_KEY_POLICY)),
|
|
742
|
+
__metadata("design:paramtypes", [StorageService, Object, Object])
|
|
599
743
|
], StorageGatewayController);
|
|
600
744
|
export { StorageGatewayController };
|
|
601
745
|
//# sourceMappingURL=storage-gateway.controller.js.map
|