@nestm/storage 0.1.0-alpha.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.
Files changed (94) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/CONTRIBUTING.md +83 -0
  3. package/LICENSE +21 -0
  4. package/README.md +386 -0
  5. package/SECURITY.md +25 -0
  6. package/dist/files-sdk/files-sdk.driver.d.ts +36 -0
  7. package/dist/files-sdk/files-sdk.driver.d.ts.map +1 -0
  8. package/dist/files-sdk/files-sdk.driver.js +352 -0
  9. package/dist/files-sdk/files-sdk.driver.js.map +1 -0
  10. package/dist/files-sdk/index.d.ts +3 -0
  11. package/dist/files-sdk/index.d.ts.map +1 -0
  12. package/dist/files-sdk/index.js +2 -0
  13. package/dist/files-sdk/index.js.map +1 -0
  14. package/dist/gateway/index.d.ts +3 -0
  15. package/dist/gateway/index.d.ts.map +1 -0
  16. package/dist/gateway/index.js +3 -0
  17. package/dist/gateway/index.js.map +1 -0
  18. package/dist/gateway/storage-gateway-fastify-parser.d.ts +9 -0
  19. package/dist/gateway/storage-gateway-fastify-parser.d.ts.map +1 -0
  20. package/dist/gateway/storage-gateway-fastify-parser.js +50 -0
  21. package/dist/gateway/storage-gateway-fastify-parser.js.map +1 -0
  22. package/dist/gateway/storage-gateway.controller.d.ts +31 -0
  23. package/dist/gateway/storage-gateway.controller.d.ts.map +1 -0
  24. package/dist/gateway/storage-gateway.controller.js +601 -0
  25. package/dist/gateway/storage-gateway.controller.js.map +1 -0
  26. package/dist/gateway/storage-gateway.guard.d.ts +7 -0
  27. package/dist/gateway/storage-gateway.guard.d.ts.map +1 -0
  28. package/dist/gateway/storage-gateway.guard.js +40 -0
  29. package/dist/gateway/storage-gateway.guard.js.map +1 -0
  30. package/dist/gateway/storage-gateway.module.d.ts +6 -0
  31. package/dist/gateway/storage-gateway.module.d.ts.map +1 -0
  32. package/dist/gateway/storage-gateway.module.js +98 -0
  33. package/dist/gateway/storage-gateway.module.js.map +1 -0
  34. package/dist/gateway/storage-gateway.tokens.d.ts +3 -0
  35. package/dist/gateway/storage-gateway.tokens.d.ts.map +1 -0
  36. package/dist/gateway/storage-gateway.tokens.js +3 -0
  37. package/dist/gateway/storage-gateway.tokens.js.map +1 -0
  38. package/dist/gateway/storage-gateway.types.d.ts +65 -0
  39. package/dist/gateway/storage-gateway.types.d.ts.map +1 -0
  40. package/dist/gateway/storage-gateway.types.js +13 -0
  41. package/dist/gateway/storage-gateway.types.js.map +1 -0
  42. package/dist/index.d.ts +11 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +8 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/inject-storage.decorator.d.ts +2 -0
  47. package/dist/inject-storage.decorator.d.ts.map +1 -0
  48. package/dist/inject-storage.decorator.js +6 -0
  49. package/dist/inject-storage.decorator.js.map +1 -0
  50. package/dist/internal/settle-many.d.ts +7 -0
  51. package/dist/internal/settle-many.d.ts.map +1 -0
  52. package/dist/internal/settle-many.js +62 -0
  53. package/dist/internal/settle-many.js.map +1 -0
  54. package/dist/storage-module.options.d.ts +62 -0
  55. package/dist/storage-module.options.d.ts.map +1 -0
  56. package/dist/storage-module.options.js +2 -0
  57. package/dist/storage-module.options.js.map +1 -0
  58. package/dist/storage-upload-control.d.ts +22 -0
  59. package/dist/storage-upload-control.d.ts.map +1 -0
  60. package/dist/storage-upload-control.js +139 -0
  61. package/dist/storage-upload-control.js.map +1 -0
  62. package/dist/storage.client.d.ts +51 -0
  63. package/dist/storage.client.d.ts.map +1 -0
  64. package/dist/storage.client.js +395 -0
  65. package/dist/storage.client.js.map +1 -0
  66. package/dist/storage.driver.d.ts +18 -0
  67. package/dist/storage.driver.d.ts.map +1 -0
  68. package/dist/storage.driver.js +2 -0
  69. package/dist/storage.driver.js.map +1 -0
  70. package/dist/storage.error.d.ts +39 -0
  71. package/dist/storage.error.d.ts.map +1 -0
  72. package/dist/storage.error.js +49 -0
  73. package/dist/storage.error.js.map +1 -0
  74. package/dist/storage.module.d.ts +11 -0
  75. package/dist/storage.module.d.ts.map +1 -0
  76. package/dist/storage.module.js +170 -0
  77. package/dist/storage.module.js.map +1 -0
  78. package/dist/storage.service.d.ts +12 -0
  79. package/dist/storage.service.d.ts.map +1 -0
  80. package/dist/storage.service.js +246 -0
  81. package/dist/storage.service.js.map +1 -0
  82. package/dist/storage.tokens.d.ts +6 -0
  83. package/dist/storage.tokens.d.ts.map +1 -0
  84. package/dist/storage.tokens.js +13 -0
  85. package/dist/storage.tokens.js.map +1 -0
  86. package/dist/storage.types.d.ts +244 -0
  87. package/dist/storage.types.d.ts.map +1 -0
  88. package/dist/storage.types.js +2 -0
  89. package/dist/storage.types.js.map +1 -0
  90. package/dist/testing/index.d.ts +9 -0
  91. package/dist/testing/index.d.ts.map +1 -0
  92. package/dist/testing/index.js +10 -0
  93. package/dist/testing/index.js.map +1 -0
  94. package/package.json +112 -0
@@ -0,0 +1,601 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { BadRequestException, Body, Controller, Delete, Get, Head, HttpException, HttpStatus, Inject, Post, Put, Query, Req, Res, SetMetadata, UseGuards, } from '@nestjs/common';
14
+ import { ServerResponse } from 'node:http';
15
+ import { Readable } from 'node:stream';
16
+ import { pipeline } from 'node:stream/promises';
17
+ import { StorageError, StorageErrorCode } from '../storage.error.js';
18
+ import { StorageService } from '../storage.service.js';
19
+ import { StorageGatewayGuard } from './storage-gateway.guard.js';
20
+ import { FASTIFY_STORAGE_UPLOAD_CONFIG } from './storage-gateway-fastify-parser.js';
21
+ import { STORAGE_GATEWAY_OPTIONS } from './storage-gateway.tokens.js';
22
+ import { StorageGatewayOperation, } from './storage-gateway.types.js';
23
+ function recordOf(value, label) {
24
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
25
+ throw new BadRequestException(`${label} must be a JSON object.`);
26
+ }
27
+ return value;
28
+ }
29
+ function requiredString(record, field) {
30
+ const value = record[field];
31
+ if (typeof value !== 'string' || value.length === 0) {
32
+ throw new BadRequestException(`${field} must be a non-empty string.`);
33
+ }
34
+ return value;
35
+ }
36
+ function optionalString(record, field) {
37
+ const value = record[field];
38
+ if (value === undefined) {
39
+ return undefined;
40
+ }
41
+ if (typeof value !== 'string') {
42
+ throw new BadRequestException(`${field} must be a string.`);
43
+ }
44
+ return value;
45
+ }
46
+ function optionalNumber(record, field) {
47
+ const value = record[field];
48
+ if (value === undefined) {
49
+ return undefined;
50
+ }
51
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
52
+ throw new BadRequestException(`${field} must be a finite number.`);
53
+ }
54
+ return value;
55
+ }
56
+ function optionalPositiveInteger(record, field) {
57
+ const value = optionalNumber(record, field);
58
+ if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) {
59
+ throw new BadRequestException(`${field} must be a positive safe integer.`);
60
+ }
61
+ return value;
62
+ }
63
+ function optionalNonNegativeInteger(record, field) {
64
+ const value = optionalNumber(record, field);
65
+ if (value !== undefined && (!Number.isSafeInteger(value) || value < 0)) {
66
+ throw new BadRequestException(`${field} must be a non-negative safe integer.`);
67
+ }
68
+ return value;
69
+ }
70
+ function queryInteger(value, field) {
71
+ if (value === undefined) {
72
+ return undefined;
73
+ }
74
+ if (!/^[0-9]+$/u.test(value)) {
75
+ throw new BadRequestException(`${field} must be a non-negative integer.`);
76
+ }
77
+ const parsed = Number(value);
78
+ if (!Number.isSafeInteger(parsed)) {
79
+ throw new BadRequestException(`${field} must be a safe integer.`);
80
+ }
81
+ return parsed;
82
+ }
83
+ function rawResponseOf(response) {
84
+ if (response instanceof ServerResponse) {
85
+ return response;
86
+ }
87
+ if (typeof response === 'object' && response !== null) {
88
+ const wrapper = response;
89
+ wrapper.hijack?.();
90
+ if (wrapper.raw instanceof ServerResponse) {
91
+ return wrapper.raw;
92
+ }
93
+ }
94
+ throw new Error('Unsupported Nest HTTP response adapter.');
95
+ }
96
+ function requestWrapperOf(request) {
97
+ if (typeof request !== 'object' || request === null) {
98
+ throw new BadRequestException('Unsupported Nest HTTP request adapter.');
99
+ }
100
+ return request;
101
+ }
102
+ function requestHeader(request, name) {
103
+ const headers = requestWrapperOf(request).headers;
104
+ const value = headers?.[name];
105
+ return Array.isArray(value) ? value[0] : value;
106
+ }
107
+ function uploadSourceOf(request) {
108
+ const wrapper = requestWrapperOf(request);
109
+ if (wrapper.body instanceof Readable) {
110
+ return wrapper.body;
111
+ }
112
+ if (wrapper.body instanceof Uint8Array) {
113
+ return Readable.from([wrapper.body]);
114
+ }
115
+ if (request instanceof Readable && !request.readableEnded) {
116
+ return request;
117
+ }
118
+ if (wrapper.raw instanceof Readable && !wrapper.raw.readableEnded) {
119
+ return wrapper.raw;
120
+ }
121
+ throw new BadRequestException('Proxy uploads require an application/octet-stream request body.');
122
+ }
123
+ async function* enforceUploadLimit(source, maxBytes) {
124
+ let received = 0;
125
+ for await (const chunk of source) {
126
+ const bytes = typeof chunk === 'string'
127
+ ? new TextEncoder().encode(chunk)
128
+ : chunk instanceof Uint8Array
129
+ ? chunk
130
+ : undefined;
131
+ if (bytes === undefined) {
132
+ throw new BadRequestException('Upload streams must contain byte or string chunks.');
133
+ }
134
+ received += bytes.byteLength;
135
+ if (received > maxBytes) {
136
+ throw new StorageError(`Proxy upload exceeds the ${maxBytes}-byte gateway limit.`, {
137
+ code: StorageErrorCode.LIMIT_EXCEEDED,
138
+ permanent: true,
139
+ });
140
+ }
141
+ yield bytes;
142
+ }
143
+ }
144
+ function setObjectHeaders(response, object) {
145
+ response.setHeader('content-length', String(object.size));
146
+ response.setHeader('content-type', object.contentType);
147
+ if (object.etag !== undefined) {
148
+ response.setHeader('etag', object.etag);
149
+ }
150
+ if (object.lastModified !== undefined) {
151
+ response.setHeader('last-modified', object.lastModified.toUTCString());
152
+ }
153
+ }
154
+ const publicStorageMessages = {
155
+ [StorageErrorCode.NOT_FOUND]: 'Storage object was not found.',
156
+ [StorageErrorCode.UNAUTHORIZED]: 'Storage operation is not permitted.',
157
+ [StorageErrorCode.CONFLICT]: 'Storage object conflict.',
158
+ [StorageErrorCode.READ_ONLY]: 'Storage operation is not permitted.',
159
+ [StorageErrorCode.INVALID_ARGUMENT]: 'Invalid storage request.',
160
+ [StorageErrorCode.NOT_SUPPORTED]: 'Storage operation is not supported.',
161
+ [StorageErrorCode.ABORTED]: 'Storage operation was aborted.',
162
+ [StorageErrorCode.TIMEOUT]: 'Storage operation timed out.',
163
+ [StorageErrorCode.LIMIT_EXCEEDED]: 'Storage request limit exceeded.',
164
+ [StorageErrorCode.PROVIDER]: 'Storage provider operation failed.',
165
+ };
166
+ function storageHttpStatus(error) {
167
+ switch (error.code) {
168
+ case StorageErrorCode.NOT_FOUND:
169
+ return HttpStatus.NOT_FOUND;
170
+ case StorageErrorCode.UNAUTHORIZED:
171
+ case StorageErrorCode.READ_ONLY:
172
+ return HttpStatus.FORBIDDEN;
173
+ case StorageErrorCode.CONFLICT:
174
+ return HttpStatus.CONFLICT;
175
+ case StorageErrorCode.INVALID_ARGUMENT:
176
+ return HttpStatus.BAD_REQUEST;
177
+ case StorageErrorCode.NOT_SUPPORTED:
178
+ return HttpStatus.NOT_IMPLEMENTED;
179
+ case StorageErrorCode.LIMIT_EXCEEDED:
180
+ return HttpStatus.PAYLOAD_TOO_LARGE;
181
+ case StorageErrorCode.ABORTED:
182
+ return HttpStatus.REQUEST_TIMEOUT;
183
+ case StorageErrorCode.TIMEOUT:
184
+ return HttpStatus.GATEWAY_TIMEOUT;
185
+ case StorageErrorCode.PROVIDER:
186
+ return HttpStatus.BAD_GATEWAY;
187
+ }
188
+ }
189
+ function toHttpException(error) {
190
+ if (error instanceof HttpException) {
191
+ return error;
192
+ }
193
+ if (error instanceof StorageError) {
194
+ return new HttpException({
195
+ error: {
196
+ code: error.code,
197
+ message: publicStorageMessages[error.code],
198
+ },
199
+ }, storageHttpStatus(error), { cause: error });
200
+ }
201
+ return new HttpException({
202
+ error: {
203
+ code: StorageErrorCode.PROVIDER,
204
+ message: 'Storage gateway operation failed.',
205
+ },
206
+ }, HttpStatus.BAD_GATEWAY, { cause: error });
207
+ }
208
+ let StorageGatewayController = class StorageGatewayController {
209
+ storage;
210
+ options;
211
+ constructor(storage, options) {
212
+ this.storage = storage;
213
+ this.options = options;
214
+ }
215
+ async download(key, proxy, rangeStart, rangeEnd, disposition, response) {
216
+ await this.#run(async () => {
217
+ this.#assertAllowed(StorageGatewayOperation.DOWNLOAD);
218
+ const objectKey = this.#key(key);
219
+ const client = this.storage.use(this.options.store);
220
+ const forceProxy = proxy === 'true' || proxy === '1';
221
+ const canSign = this.options.operations.has(StorageGatewayOperation.SIGN_DOWNLOAD) &&
222
+ client.capabilities.signedDownload.supported;
223
+ if (!forceProxy && this.options.mode !== 'proxy' && canSign) {
224
+ try {
225
+ const url = await client.signDownload(objectKey, {
226
+ expiresIn: this.options.defaultSignedUrlExpiresIn,
227
+ ...(disposition !== undefined && {
228
+ responseContentDisposition: disposition,
229
+ }),
230
+ });
231
+ const raw = rawResponseOf(response);
232
+ raw.statusCode = HttpStatus.TEMPORARY_REDIRECT;
233
+ raw.setHeader('location', url);
234
+ raw.end();
235
+ return;
236
+ }
237
+ catch (error) {
238
+ if (this.options.mode !== 'hybrid' ||
239
+ !(error instanceof StorageError) ||
240
+ error.code !== StorageErrorCode.NOT_SUPPORTED) {
241
+ throw error;
242
+ }
243
+ }
244
+ }
245
+ if (this.options.mode === 'signed') {
246
+ throw new StorageError('Signed downloads are unavailable for this store.', {
247
+ code: StorageErrorCode.NOT_SUPPORTED,
248
+ key: objectKey,
249
+ permanent: true,
250
+ });
251
+ }
252
+ const start = queryInteger(rangeStart, 'rangeStart');
253
+ const end = queryInteger(rangeEnd, 'rangeEnd');
254
+ if (end !== undefined && start === undefined) {
255
+ throw new BadRequestException('rangeStart is required when rangeEnd is provided.');
256
+ }
257
+ const range = start === undefined
258
+ ? undefined
259
+ : { start, ...(end !== undefined && { end }) };
260
+ const object = await client.downloadStream(objectKey, {
261
+ ...(range !== undefined && { range }),
262
+ });
263
+ const raw = rawResponseOf(response);
264
+ raw.statusCode =
265
+ range === undefined ? HttpStatus.OK : HttpStatus.PARTIAL_CONTENT;
266
+ setObjectHeaders(raw, object);
267
+ const baseContentType = object.contentType
268
+ .split(';', 1)[0]
269
+ ?.trim()
270
+ .toLowerCase();
271
+ raw.setHeader('content-disposition', baseContentType !== undefined &&
272
+ this.options.proxyInlineContentTypes.has(baseContentType)
273
+ ? 'inline'
274
+ : 'attachment');
275
+ raw.setHeader('x-content-type-options', 'nosniff');
276
+ if (range !== undefined) {
277
+ raw.setHeader('content-range', `bytes ${range.start}-${range.start + object.size - 1}/*`);
278
+ }
279
+ if (client.capabilities.rangeRead) {
280
+ raw.setHeader('accept-ranges', 'bytes');
281
+ }
282
+ try {
283
+ await pipeline(Readable.fromWeb(object.body), raw);
284
+ }
285
+ catch (error) {
286
+ if (raw.headersSent) {
287
+ raw.destroy(error instanceof Error ? error : undefined);
288
+ return;
289
+ }
290
+ throw error;
291
+ }
292
+ });
293
+ }
294
+ async upload(key, request) {
295
+ return this.#run(async () => {
296
+ this.#assertAllowed(StorageGatewayOperation.UPLOAD);
297
+ if (this.options.mode === 'signed') {
298
+ throw new StorageError('Proxy uploads are disabled in signed gateway mode.', {
299
+ code: StorageErrorCode.NOT_SUPPORTED,
300
+ permanent: true,
301
+ });
302
+ }
303
+ const objectKey = this.#key(key);
304
+ const transportContentType = requestHeader(request, 'content-type')
305
+ ?.split(';', 1)[0]
306
+ ?.trim()
307
+ .toLowerCase();
308
+ if (transportContentType !== 'application/octet-stream') {
309
+ throw new BadRequestException('Proxy uploads require Content-Type: application/octet-stream.');
310
+ }
311
+ const lengthHeader = requestHeader(request, 'content-length');
312
+ const contentLength = lengthHeader === undefined
313
+ ? undefined
314
+ : queryInteger(lengthHeader, 'content-length');
315
+ if (contentLength !== undefined &&
316
+ contentLength > this.options.maxUploadBytes) {
317
+ throw new StorageError(`Proxy upload exceeds the ${this.options.maxUploadBytes}-byte gateway limit.`, {
318
+ code: StorageErrorCode.LIMIT_EXCEEDED,
319
+ key: objectKey,
320
+ permanent: true,
321
+ });
322
+ }
323
+ const source = uploadSourceOf(request);
324
+ const body = Readable.toWeb(Readable.from(enforceUploadLimit(source, this.options.maxUploadBytes)));
325
+ const contentType = requestHeader(request, 'x-storage-content-type') ??
326
+ 'application/octet-stream';
327
+ const result = await this.storage
328
+ .use(this.options.store)
329
+ .upload(objectKey, body, { contentType, multipart: true });
330
+ return { data: result };
331
+ });
332
+ }
333
+ async head(key, response) {
334
+ await this.#run(async () => {
335
+ this.#assertAllowed(StorageGatewayOperation.HEAD);
336
+ const metadata = await this.storage
337
+ .use(this.options.store)
338
+ .head(this.#key(key));
339
+ const raw = rawResponseOf(response);
340
+ raw.statusCode = HttpStatus.OK;
341
+ setObjectHeaders(raw, metadata);
342
+ raw.end();
343
+ });
344
+ }
345
+ async delete(key) {
346
+ return this.#run(async () => {
347
+ this.#assertAllowed(StorageGatewayOperation.DELETE);
348
+ await this.storage.use(this.options.store).delete(this.#key(key));
349
+ return { data: { deleted: true } };
350
+ });
351
+ }
352
+ async list(prefix, cursor, limit, delimiter) {
353
+ return this.#run(async () => {
354
+ this.#assertAllowed(StorageGatewayOperation.LIST);
355
+ const parsedLimit = queryInteger(limit, 'limit') ?? this.options.maxListResults;
356
+ if (parsedLimit === 0 || parsedLimit > this.options.maxListResults) {
357
+ throw new BadRequestException(`limit must be between 1 and ${this.options.maxListResults}.`);
358
+ }
359
+ const result = await this.storage.use(this.options.store).list({
360
+ ...(cursor !== undefined && { cursor }),
361
+ ...(delimiter !== undefined && { delimiter }),
362
+ limit: parsedLimit,
363
+ ...(prefix !== undefined && { prefix }),
364
+ });
365
+ return { data: result };
366
+ });
367
+ }
368
+ async search(pattern, prefix, limit, maxResults, match, caseInsensitive) {
369
+ return this.#run(async () => {
370
+ this.#assertAllowed(StorageGatewayOperation.SEARCH);
371
+ const searchPattern = this.#key(pattern, 'pattern');
372
+ if (match !== undefined &&
373
+ !['glob', 'regex', 'substring', 'exact'].includes(match)) {
374
+ throw new BadRequestException('Invalid search match mode.');
375
+ }
376
+ const parsedLimit = limit === undefined ? undefined : queryInteger(limit, 'limit');
377
+ if (parsedLimit !== undefined &&
378
+ (parsedLimit === 0 || parsedLimit > this.options.maxSearchResults)) {
379
+ throw new BadRequestException(`limit must be between 1 and ${this.options.maxSearchResults}.`);
380
+ }
381
+ const parsedMaxResults = queryInteger(maxResults, 'maxResults') ?? this.options.maxSearchResults;
382
+ if (parsedMaxResults === 0 ||
383
+ parsedMaxResults > this.options.maxSearchResults) {
384
+ throw new BadRequestException(`maxResults must be between 1 and ${this.options.maxSearchResults}.`);
385
+ }
386
+ const objects = [];
387
+ for await (const object of this.storage
388
+ .use(this.options.store)
389
+ .search(searchPattern, {
390
+ ...(caseInsensitive !== undefined && {
391
+ caseInsensitive: caseInsensitive === 'true' || caseInsensitive === '1',
392
+ }),
393
+ ...(parsedLimit !== undefined && { limit: parsedLimit }),
394
+ ...(match !== undefined && {
395
+ match: match,
396
+ }),
397
+ maxResults: parsedMaxResults,
398
+ ...(prefix !== undefined && { prefix }),
399
+ })) {
400
+ objects.push(object);
401
+ if (objects.length === parsedMaxResults) {
402
+ break;
403
+ }
404
+ }
405
+ return { data: objects };
406
+ });
407
+ }
408
+ async signDownload(body) {
409
+ return this.#run(async () => {
410
+ this.#assertAllowed(StorageGatewayOperation.SIGN_DOWNLOAD);
411
+ if (this.options.mode === 'proxy') {
412
+ throw new StorageError('Signed downloads are disabled in proxy gateway mode.', {
413
+ code: StorageErrorCode.NOT_SUPPORTED,
414
+ permanent: true,
415
+ });
416
+ }
417
+ const input = recordOf(body, 'body');
418
+ const responseContentDisposition = optionalString(input, 'responseContentDisposition');
419
+ const options = {
420
+ expiresIn: optionalPositiveInteger(input, 'expiresIn') ??
421
+ this.options.defaultSignedUrlExpiresIn,
422
+ ...(responseContentDisposition !== undefined && {
423
+ responseContentDisposition,
424
+ }),
425
+ };
426
+ const url = await this.storage
427
+ .use(this.options.store)
428
+ .signDownload(requiredString(input, 'key'), options);
429
+ return { data: { url } };
430
+ });
431
+ }
432
+ async signUpload(body) {
433
+ return this.#run(async () => {
434
+ this.#assertAllowed(StorageGatewayOperation.SIGN_UPLOAD);
435
+ if (this.options.mode === 'proxy') {
436
+ throw new StorageError('Signed uploads are disabled in proxy gateway mode.', {
437
+ code: StorageErrorCode.NOT_SUPPORTED,
438
+ permanent: true,
439
+ });
440
+ }
441
+ const input = recordOf(body, 'body');
442
+ const contentType = optionalString(input, 'contentType');
443
+ const maxSize = optionalPositiveInteger(input, 'maxSize');
444
+ const minSize = optionalNonNegativeInteger(input, 'minSize');
445
+ if (maxSize !== undefined && minSize !== undefined && minSize > maxSize) {
446
+ throw new BadRequestException('minSize cannot be greater than maxSize.');
447
+ }
448
+ const options = {
449
+ expiresIn: optionalPositiveInteger(input, 'expiresIn') ??
450
+ this.options.defaultSignedUrlExpiresIn,
451
+ ...(contentType !== undefined && { contentType }),
452
+ ...(maxSize !== undefined && { maxSize }),
453
+ ...(minSize !== undefined && { minSize }),
454
+ };
455
+ const result = await this.storage
456
+ .use(this.options.store)
457
+ .signUpload(requiredString(input, 'key'), options);
458
+ return { data: result };
459
+ });
460
+ }
461
+ async copy(body) {
462
+ return this.#run(async () => {
463
+ this.#assertAllowed(StorageGatewayOperation.COPY);
464
+ const input = recordOf(body, 'body');
465
+ await this.storage
466
+ .use(this.options.store)
467
+ .copy(requiredString(input, 'from'), requiredString(input, 'to'));
468
+ return { data: { copied: true } };
469
+ });
470
+ }
471
+ async move(body) {
472
+ return this.#run(async () => {
473
+ this.#assertAllowed(StorageGatewayOperation.MOVE);
474
+ const input = recordOf(body, 'body');
475
+ await this.storage
476
+ .use(this.options.store)
477
+ .move(requiredString(input, 'from'), requiredString(input, 'to'));
478
+ return { data: { moved: true } };
479
+ });
480
+ }
481
+ #assertAllowed(operation) {
482
+ if (!this.options.operations.has(operation)) {
483
+ throw new HttpException({
484
+ error: {
485
+ code: 'OPERATION_NOT_ALLOWED',
486
+ message: `Storage gateway operation "${operation}" is disabled.`,
487
+ },
488
+ }, HttpStatus.FORBIDDEN);
489
+ }
490
+ }
491
+ #key(value, label = 'key') {
492
+ if (value === undefined || value.length === 0) {
493
+ throw new BadRequestException(`${label} is required.`);
494
+ }
495
+ return value;
496
+ }
497
+ async #run(operation) {
498
+ try {
499
+ return await operation();
500
+ }
501
+ catch (error) {
502
+ throw toHttpException(error);
503
+ }
504
+ }
505
+ };
506
+ __decorate([
507
+ Get('object'),
508
+ __param(0, Query('key')),
509
+ __param(1, Query('proxy')),
510
+ __param(2, Query('rangeStart')),
511
+ __param(3, Query('rangeEnd')),
512
+ __param(4, Query('disposition')),
513
+ __param(5, Res()),
514
+ __metadata("design:type", Function),
515
+ __metadata("design:paramtypes", [Object, Object, Object, Object, Object, Object]),
516
+ __metadata("design:returntype", Promise)
517
+ ], StorageGatewayController.prototype, "download", null);
518
+ __decorate([
519
+ SetMetadata('__fastify_route_config__', {
520
+ [FASTIFY_STORAGE_UPLOAD_CONFIG]: true,
521
+ }),
522
+ Put('object'),
523
+ __param(0, Query('key')),
524
+ __param(1, Req()),
525
+ __metadata("design:type", Function),
526
+ __metadata("design:paramtypes", [Object, Object]),
527
+ __metadata("design:returntype", Promise)
528
+ ], StorageGatewayController.prototype, "upload", null);
529
+ __decorate([
530
+ Head('metadata'),
531
+ __param(0, Query('key')),
532
+ __param(1, Res()),
533
+ __metadata("design:type", Function),
534
+ __metadata("design:paramtypes", [Object, Object]),
535
+ __metadata("design:returntype", Promise)
536
+ ], StorageGatewayController.prototype, "head", null);
537
+ __decorate([
538
+ Delete('object'),
539
+ __param(0, Query('key')),
540
+ __metadata("design:type", Function),
541
+ __metadata("design:paramtypes", [Object]),
542
+ __metadata("design:returntype", Promise)
543
+ ], StorageGatewayController.prototype, "delete", null);
544
+ __decorate([
545
+ Get('list'),
546
+ __param(0, Query('prefix')),
547
+ __param(1, Query('cursor')),
548
+ __param(2, Query('limit')),
549
+ __param(3, Query('delimiter')),
550
+ __metadata("design:type", Function),
551
+ __metadata("design:paramtypes", [Object, Object, Object, Object]),
552
+ __metadata("design:returntype", Promise)
553
+ ], StorageGatewayController.prototype, "list", null);
554
+ __decorate([
555
+ Get('search'),
556
+ __param(0, Query('pattern')),
557
+ __param(1, Query('prefix')),
558
+ __param(2, Query('limit')),
559
+ __param(3, Query('maxResults')),
560
+ __param(4, Query('match')),
561
+ __param(5, Query('caseInsensitive')),
562
+ __metadata("design:type", Function),
563
+ __metadata("design:paramtypes", [Object, Object, Object, Object, Object, Object]),
564
+ __metadata("design:returntype", Promise)
565
+ ], StorageGatewayController.prototype, "search", null);
566
+ __decorate([
567
+ Post('sign-download'),
568
+ __param(0, Body()),
569
+ __metadata("design:type", Function),
570
+ __metadata("design:paramtypes", [Object]),
571
+ __metadata("design:returntype", Promise)
572
+ ], StorageGatewayController.prototype, "signDownload", null);
573
+ __decorate([
574
+ Post('sign-upload'),
575
+ __param(0, Body()),
576
+ __metadata("design:type", Function),
577
+ __metadata("design:paramtypes", [Object]),
578
+ __metadata("design:returntype", Promise)
579
+ ], StorageGatewayController.prototype, "signUpload", null);
580
+ __decorate([
581
+ Post('copy'),
582
+ __param(0, Body()),
583
+ __metadata("design:type", Function),
584
+ __metadata("design:paramtypes", [Object]),
585
+ __metadata("design:returntype", Promise)
586
+ ], StorageGatewayController.prototype, "copy", null);
587
+ __decorate([
588
+ Post('move'),
589
+ __param(0, Body()),
590
+ __metadata("design:type", Function),
591
+ __metadata("design:paramtypes", [Object]),
592
+ __metadata("design:returntype", Promise)
593
+ ], StorageGatewayController.prototype, "move", null);
594
+ StorageGatewayController = __decorate([
595
+ Controller('storage'),
596
+ UseGuards(StorageGatewayGuard),
597
+ __param(1, Inject(STORAGE_GATEWAY_OPTIONS)),
598
+ __metadata("design:paramtypes", [StorageService, Object])
599
+ ], StorageGatewayController);
600
+ export { StorageGatewayController };
601
+ //# sourceMappingURL=storage-gateway.controller.js.map