@adonis-agora/media 0.8.2 → 0.10.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/CHANGELOG.md +88 -0
- package/README.md +2 -2
- package/dist/providers/media_provider.d.ts.map +1 -1
- package/dist/providers/media_provider.js +138 -0
- package/dist/providers/media_provider.js.map +1 -1
- package/dist/src/content_type.d.ts +6 -1
- package/dist/src/content_type.d.ts.map +1 -1
- package/dist/src/content_type.js +102 -7
- package/dist/src/content_type.js.map +1 -1
- package/dist/src/define_config.d.ts +104 -5
- package/dist/src/define_config.d.ts.map +1 -1
- package/dist/src/define_config.js +7 -2
- package/dist/src/define_config.js.map +1 -1
- package/dist/src/diagnostics.d.ts +7 -0
- package/dist/src/diagnostics.d.ts.map +1 -1
- package/dist/src/diagnostics.js +20 -0
- package/dist/src/diagnostics.js.map +1 -1
- package/dist/src/direct_upload.d.ts +177 -0
- package/dist/src/direct_upload.d.ts.map +1 -0
- package/dist/src/direct_upload.js +299 -0
- package/dist/src/direct_upload.js.map +1 -0
- package/dist/src/direct_upload_handler.d.ts +117 -0
- package/dist/src/direct_upload_handler.d.ts.map +1 -0
- package/dist/src/direct_upload_handler.js +277 -0
- package/dist/src/direct_upload_handler.js.map +1 -0
- package/dist/src/disks/factory.d.ts +11 -3
- package/dist/src/disks/factory.d.ts.map +1 -1
- package/dist/src/disks/factory.js +5 -3
- package/dist/src/disks/factory.js.map +1 -1
- package/dist/src/disks/s3.d.ts +10 -0
- package/dist/src/disks/s3.d.ts.map +1 -1
- package/dist/src/disks/s3.js +96 -22
- package/dist/src/disks/s3.js.map +1 -1
- package/dist/src/disks/sigv4.d.ts +58 -0
- package/dist/src/disks/sigv4.d.ts.map +1 -0
- package/dist/src/disks/sigv4.js +87 -0
- package/dist/src/disks/sigv4.js.map +1 -0
- package/dist/src/disks/xml_fallback.d.ts +3 -3
- package/dist/src/disks/xml_fallback.js +3 -3
- package/dist/src/errors.d.ts +104 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +145 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/hls/delivery.d.ts +112 -0
- package/dist/src/hls/delivery.d.ts.map +1 -0
- package/dist/src/hls/delivery.js +118 -0
- package/dist/src/hls/delivery.js.map +1 -0
- package/dist/src/hls/playlist.d.ts +67 -0
- package/dist/src/hls/playlist.d.ts.map +1 -0
- package/dist/src/hls/playlist.js +126 -0
- package/dist/src/hls/playlist.js.map +1 -0
- package/dist/src/index.d.ts +19 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +13 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/media_collection.d.ts +8 -1
- package/dist/src/media_collection.d.ts.map +1 -1
- package/dist/src/media_collection.js +23 -1
- package/dist/src/media_collection.js.map +1 -1
- package/dist/src/media_library.d.ts +32 -2
- package/dist/src/media_library.d.ts.map +1 -1
- package/dist/src/media_library.js +218 -11
- package/dist/src/media_library.js.map +1 -1
- package/dist/src/media_manager.d.ts +37 -2
- package/dist/src/media_manager.d.ts.map +1 -1
- package/dist/src/media_manager.js +54 -1
- package/dist/src/media_manager.js.map +1 -1
- package/dist/src/media_record.d.ts +22 -2
- package/dist/src/media_record.d.ts.map +1 -1
- package/dist/src/testing/fake_transformer.d.ts +32 -0
- package/dist/src/testing/fake_transformer.d.ts.map +1 -0
- package/dist/src/testing/fake_transformer.js +32 -0
- package/dist/src/testing/fake_transformer.js.map +1 -0
- package/dist/src/testing/index.d.ts +2 -0
- package/dist/src/testing/index.d.ts.map +1 -1
- package/dist/src/testing/index.js +1 -0
- package/dist/src/testing/index.js.map +1 -1
- package/dist/src/transformer.d.ts +111 -0
- package/dist/src/transformer.d.ts.map +1 -0
- package/dist/src/transformer.js +2 -0
- package/dist/src/transformer.js.map +1 -0
- package/dist/src/transformers/factory.d.ts +30 -0
- package/dist/src/transformers/factory.d.ts.map +1 -0
- package/dist/src/transformers/factory.js +34 -0
- package/dist/src/transformers/factory.js.map +1 -0
- package/dist/src/transformers/hls.d.ts +110 -0
- package/dist/src/transformers/hls.d.ts.map +1 -0
- package/dist/src/transformers/hls.js +111 -0
- package/dist/src/transformers/hls.js.map +1 -0
- package/dist/src/transformers/mediabunny.d.ts +5 -0
- package/dist/src/transformers/mediabunny.d.ts.map +1 -0
- package/dist/src/transformers/mediabunny.js +150 -0
- package/dist/src/transformers/mediabunny.js.map +1 -0
- package/dist/src/transformers/mediabunny_loader.d.ts +15 -0
- package/dist/src/transformers/mediabunny_loader.d.ts.map +1 -0
- package/dist/src/transformers/mediabunny_loader.js +21 -0
- package/dist/src/transformers/mediabunny_loader.js.map +1 -0
- package/dist/src/transformers/probe.d.ts +56 -0
- package/dist/src/transformers/probe.d.ts.map +1 -0
- package/dist/src/transformers/probe.js +32 -0
- package/dist/src/transformers/probe.js.map +1 -0
- package/dist/src/transformers/source_file.d.ts +20 -0
- package/dist/src/transformers/source_file.d.ts.map +1 -0
- package/dist/src/transformers/source_file.js +30 -0
- package/dist/src/transformers/source_file.js.map +1 -0
- package/dist/src/types.d.ts +80 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/stubs/config/media.stub +12 -0
- package/package.json +11 -7
- package/LICENSE +0 -21
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { traceMedia } from './diagnostics.js';
|
|
3
|
+
import { MimeNotAllowedError, UploadNotSupportedError, UploadPartOutOfRangeError, UploadPartSizeError, UploadPartsIncompleteError, UploadSessionExpiredError, UploadSessionNotFoundError, } from './errors.js';
|
|
4
|
+
/**
|
|
5
|
+
* HTTP half of the session-backed direct upload flow, mirroring {@link TusUploadHandler}: the core
|
|
6
|
+
* ({@link DirectUploadManager}) never sees HTTP, this class never sees a framework, and a thin
|
|
7
|
+
* AdonisJS route adapter maps `HttpContext` to {@link DirectUploadRequest}/{@link DirectUploadResponse}.
|
|
8
|
+
*
|
|
9
|
+
* The endpoint surface it expects to be mounted on (see the provider's `uploads.direct.routes`):
|
|
10
|
+
*
|
|
11
|
+
* - `POST /` → `initiate` — `201` + session id, part size, presigned URLs
|
|
12
|
+
* - `GET /:id` → `status` — confirmed ETags + fresh URLs for pending parts
|
|
13
|
+
* - `POST /:id/parts/:partNumber` → `confirm-part` — record one uploaded part's ETag
|
|
14
|
+
* - `POST /:id/complete` → `complete` — assemble; `409` names any missing parts
|
|
15
|
+
* - `DELETE /:id` → `abort`
|
|
16
|
+
*
|
|
17
|
+
* **This handler performs NO authorization** — the same split as `TusUploadHandler` and
|
|
18
|
+
* `MediaDeliveryHandler`. Who may upload, and into whose namespace the key resolves, are questions
|
|
19
|
+
* only the app can answer: guard the route, and resolve the key server-side via {@link keyFor}
|
|
20
|
+
* (the client sends a `fileName`, never a key).
|
|
21
|
+
*/
|
|
22
|
+
export class DirectUploadHandler {
|
|
23
|
+
manager;
|
|
24
|
+
disk;
|
|
25
|
+
collection;
|
|
26
|
+
maxSize;
|
|
27
|
+
keyFor;
|
|
28
|
+
newId;
|
|
29
|
+
policy;
|
|
30
|
+
adopt;
|
|
31
|
+
constructor(options) {
|
|
32
|
+
this.manager = options.manager;
|
|
33
|
+
this.disk = options.disk;
|
|
34
|
+
this.collection = options.collection;
|
|
35
|
+
this.maxSize = options.maxSize;
|
|
36
|
+
this.keyFor = options.keyFor ?? ((fileName, token) => `uploads/${token}/${fileName}`);
|
|
37
|
+
this.newId = options.idGenerator ?? (() => randomUUID());
|
|
38
|
+
this.policy = options.policy;
|
|
39
|
+
this.adopt = options.adopt;
|
|
40
|
+
if (this.policy && !this.adopt) {
|
|
41
|
+
throw new TypeError('DirectUploadHandler: "adopt" is required when a "policy" is configured');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async handle(req, ctx) {
|
|
45
|
+
switch (req.action) {
|
|
46
|
+
case 'initiate': {
|
|
47
|
+
if (typeof req.fileName !== 'string' || req.fileName.length === 0) {
|
|
48
|
+
return { status: 400, body: { error: 'fileName is required' } };
|
|
49
|
+
}
|
|
50
|
+
if (!Number.isInteger(req.size) || req.size <= 0) {
|
|
51
|
+
return { status: 400, body: { error: 'size must be a positive integer' } };
|
|
52
|
+
}
|
|
53
|
+
if (this.maxSize !== undefined && req.size > this.maxSize) {
|
|
54
|
+
return {
|
|
55
|
+
status: 413,
|
|
56
|
+
body: { error: `Upload exceeds the maximum size of ${this.maxSize} bytes` },
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (this.policy)
|
|
60
|
+
return this.initiatePolicy(req, ctx);
|
|
61
|
+
const metadata = req.metadata ?? {};
|
|
62
|
+
try {
|
|
63
|
+
const created = await this.manager.initiate({
|
|
64
|
+
key: this.keyFor(req.fileName, this.newId(), metadata),
|
|
65
|
+
size: req.size,
|
|
66
|
+
contentType: req.contentType,
|
|
67
|
+
metadata,
|
|
68
|
+
...(this.disk !== undefined ? { disk: this.disk } : {}),
|
|
69
|
+
...(this.collection !== undefined ? { collection: this.collection } : {}),
|
|
70
|
+
});
|
|
71
|
+
return { status: 201, body: this.initiateBody(created) };
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
return this.mapError(err);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
case 'status': {
|
|
78
|
+
try {
|
|
79
|
+
const status = await this.manager.status(req.uploadId);
|
|
80
|
+
return {
|
|
81
|
+
status: 200,
|
|
82
|
+
body: {
|
|
83
|
+
id: status.id,
|
|
84
|
+
key: status.key,
|
|
85
|
+
disk: status.disk,
|
|
86
|
+
partSize: status.partSize,
|
|
87
|
+
size: status.size,
|
|
88
|
+
totalParts: status.totalParts,
|
|
89
|
+
...(status.contentType !== undefined ? { contentType: status.contentType } : {}),
|
|
90
|
+
completedParts: status.completedParts,
|
|
91
|
+
pendingParts: status.pendingParts,
|
|
92
|
+
...(status.expiresAt !== undefined
|
|
93
|
+
? { expiresAt: status.expiresAt.toISOString() }
|
|
94
|
+
: {}),
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
return this.mapError(err);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
case 'confirm-part': {
|
|
103
|
+
if (!Number.isInteger(req.partNumber) || req.partNumber < 1) {
|
|
104
|
+
return { status: 400, body: { error: 'partNumber must be a positive integer' } };
|
|
105
|
+
}
|
|
106
|
+
if (typeof req.etag !== 'string' || req.etag.length === 0) {
|
|
107
|
+
return { status: 400, body: { error: 'etag is required' } };
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
const progress = await this.manager.confirmPart(req.uploadId, {
|
|
111
|
+
partNumber: req.partNumber,
|
|
112
|
+
etag: req.etag,
|
|
113
|
+
});
|
|
114
|
+
return { status: 200, body: progress };
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
return this.mapError(err);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
case 'complete': {
|
|
121
|
+
if (this.policy)
|
|
122
|
+
return this.completePolicy(req, ctx);
|
|
123
|
+
const parts = req.parts ?? [];
|
|
124
|
+
if (!Array.isArray(parts) ||
|
|
125
|
+
parts.some((part) => !Number.isInteger(part?.partNumber) ||
|
|
126
|
+
part.partNumber < 1 ||
|
|
127
|
+
typeof part?.etag !== 'string' ||
|
|
128
|
+
part.etag.length === 0)) {
|
|
129
|
+
return {
|
|
130
|
+
status: 400,
|
|
131
|
+
body: { error: 'parts must be an array of { partNumber, etag }' },
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const done = await this.manager.complete(req.uploadId, parts);
|
|
136
|
+
return { status: 200, body: done };
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
return this.mapError(err);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
case 'abort': {
|
|
143
|
+
if (this.policy)
|
|
144
|
+
return this.abortPolicy(req, ctx);
|
|
145
|
+
await this.manager.abort(req.uploadId);
|
|
146
|
+
return { status: 204 };
|
|
147
|
+
}
|
|
148
|
+
default:
|
|
149
|
+
return { status: 400, body: { error: 'Unknown action' } };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/** The `201` body for a created session — shared by the built-in and policy initiate paths. */
|
|
153
|
+
initiateBody(created) {
|
|
154
|
+
return {
|
|
155
|
+
id: created.id,
|
|
156
|
+
key: created.key,
|
|
157
|
+
disk: created.disk,
|
|
158
|
+
partSize: created.partSize,
|
|
159
|
+
size: created.size,
|
|
160
|
+
totalParts: created.totalParts,
|
|
161
|
+
parts: created.parts,
|
|
162
|
+
...(created.expiresAt !== undefined ? { expiresAt: created.expiresAt.toISOString() } : {}),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Policy-driven `initiate`: the policy decides the key/collection/metadata (and may attach a
|
|
167
|
+
* `response` merged onto the `201`), the manager opens the upload. If anything throws after the
|
|
168
|
+
* decision, the decision's `rollback` runs and the policy's `mapError` gets first refusal on the
|
|
169
|
+
* HTTP mapping (with the decision in the info); an unmapped error falls through to {@link mapError}.
|
|
170
|
+
*/
|
|
171
|
+
async initiatePolicy(req, ctx) {
|
|
172
|
+
const policy = this.policy;
|
|
173
|
+
let decision;
|
|
174
|
+
try {
|
|
175
|
+
decision = await traceMedia('upload.policy.on_initiate', () => policy.onInitiate(ctx, {
|
|
176
|
+
fileName: req.fileName,
|
|
177
|
+
size: req.size,
|
|
178
|
+
contentType: req.contentType,
|
|
179
|
+
metadata: req.metadata,
|
|
180
|
+
}));
|
|
181
|
+
const disk = decision.disk ?? this.disk;
|
|
182
|
+
const collection = decision.collection ?? this.collection;
|
|
183
|
+
const session = await this.manager.initiate({
|
|
184
|
+
key: decision.key,
|
|
185
|
+
size: req.size,
|
|
186
|
+
contentType: req.contentType,
|
|
187
|
+
metadata: decision.metadata ?? {},
|
|
188
|
+
...(disk !== undefined ? { disk } : {}),
|
|
189
|
+
...(collection !== undefined ? { collection } : {}),
|
|
190
|
+
...(decision.visibility !== undefined ? { visibility: decision.visibility } : {}),
|
|
191
|
+
...(decision.partSize !== undefined ? { partSize: decision.partSize } : {}),
|
|
192
|
+
});
|
|
193
|
+
await traceMedia('upload.policy.on_initiated', () => policy.onInitiated?.(ctx, { decision: decision, session }));
|
|
194
|
+
return {
|
|
195
|
+
status: 201,
|
|
196
|
+
body: { ...this.initiateBody(session), ...(decision.response ?? {}) },
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
try {
|
|
201
|
+
await decision?.rollback?.();
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// a failing rollback must not mask the primary error or preempt mapError
|
|
205
|
+
}
|
|
206
|
+
const mapped = await policy.mapError?.(ctx, err, { phase: 'initiate', decision });
|
|
207
|
+
if (mapped !== undefined)
|
|
208
|
+
return mapped;
|
|
209
|
+
return this.mapError(err);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Policy-driven `complete`: `resolveComplete` maps the session onto a library `target`, `adopt`
|
|
214
|
+
* assembles + adopts the object, and the `200` body is whatever `onComplete` returns. Parts-shape
|
|
215
|
+
* validation is deliberately skipped — the policy owns what a completion means. `mapError` (with
|
|
216
|
+
* the resolution in the info) gets first refusal on failures; else {@link mapError}.
|
|
217
|
+
*/
|
|
218
|
+
async completePolicy(req, ctx) {
|
|
219
|
+
const policy = this.policy;
|
|
220
|
+
let resolution;
|
|
221
|
+
try {
|
|
222
|
+
resolution = await traceMedia('upload.policy.resolve_complete', () => policy.resolveComplete(ctx, { id: req.uploadId, parts: req.parts }));
|
|
223
|
+
const record = await this.adopt(resolution.sessionId, resolution.target, req.parts);
|
|
224
|
+
const body = await traceMedia('upload.policy.on_complete', () => policy.onComplete(ctx, { record, resolution: resolution }));
|
|
225
|
+
return { status: 200, body };
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
const mapped = await policy.mapError?.(ctx, err, { phase: 'complete', resolution });
|
|
229
|
+
if (mapped !== undefined)
|
|
230
|
+
return mapped;
|
|
231
|
+
return this.mapError(err);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/** Policy-driven `abort`: the policy cleans up app-side state, then the session is dropped. */
|
|
235
|
+
async abortPolicy(req, ctx) {
|
|
236
|
+
const policy = this.policy;
|
|
237
|
+
try {
|
|
238
|
+
await traceMedia('upload.policy.on_abort', () => policy.onAbort?.(ctx, { id: req.uploadId }));
|
|
239
|
+
await this.manager.abort(req.uploadId);
|
|
240
|
+
return { status: 204 };
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
const mapped = await policy.mapError?.(ctx, err, { phase: 'abort' });
|
|
244
|
+
if (mapped !== undefined)
|
|
245
|
+
return mapped;
|
|
246
|
+
return this.mapError(err);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/** Map manager errors onto HTTP statuses; anything unexpected keeps propagating. */
|
|
250
|
+
mapError(err) {
|
|
251
|
+
if (err instanceof UploadSessionNotFoundError) {
|
|
252
|
+
return { status: 404, body: { error: err.message, code: err.code } };
|
|
253
|
+
}
|
|
254
|
+
if (err instanceof UploadSessionExpiredError) {
|
|
255
|
+
return { status: 410, body: { error: err.message, code: err.code } };
|
|
256
|
+
}
|
|
257
|
+
if (err instanceof MimeNotAllowedError) {
|
|
258
|
+
return { status: 415, body: { error: err.message, code: err.code } };
|
|
259
|
+
}
|
|
260
|
+
// Not-yet-uploaded parts are client state out of sync with the session, not a bad request.
|
|
261
|
+
if (err instanceof UploadPartsIncompleteError) {
|
|
262
|
+
return {
|
|
263
|
+
status: 409,
|
|
264
|
+
body: { error: err.message, code: err.code, missingParts: [...err.missingParts] },
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
if (err instanceof UploadPartOutOfRangeError ||
|
|
268
|
+
err instanceof UploadPartSizeError ||
|
|
269
|
+
err instanceof UploadNotSupportedError ||
|
|
270
|
+
err instanceof RangeError) {
|
|
271
|
+
const code = err.code;
|
|
272
|
+
return { status: 400, body: { error: err.message, ...(code ? { code } : {}) } };
|
|
273
|
+
}
|
|
274
|
+
throw err;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=direct_upload_handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"direct_upload_handler.js","sourceRoot":"","sources":["../../src/direct_upload_handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -24,6 +24,13 @@ export interface S3DiskConfig {
|
|
|
24
24
|
credentials?: S3Credentials;
|
|
25
25
|
/** Custom endpoint for S3-compatible services (MinIO, R2, DigitalOcean Spaces, …). */
|
|
26
26
|
endpoint?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Endpoint presigned URLs are signed against, when it differs from `endpoint`. Presigned URLs are
|
|
29
|
+
* consumed by the BROWSER (direct-upload part `PUT`s, signed `GET`s) and SigV4 bakes the host into
|
|
30
|
+
* the signature — so when `endpoint` is an internal FQDN the internet cannot resolve (MinIO on a
|
|
31
|
+
* private network), set the public one here. Server-side operations keep using `endpoint`.
|
|
32
|
+
*/
|
|
33
|
+
publicEndpoint?: string;
|
|
27
34
|
/** Use path-style addressing (`endpoint/bucket/key`) — required by most S3-compatible services. */
|
|
28
35
|
forcePathStyle?: boolean;
|
|
29
36
|
/** Prefix prepended to every key (e.g. `uploads`). */
|
|
@@ -51,11 +58,12 @@ export interface S3DiskConfig {
|
|
|
51
58
|
* ```
|
|
52
59
|
*
|
|
53
60
|
* Each factory returns a {@link DiskFactory} — a lazy thunk. Calling it in the config file costs
|
|
54
|
-
* nothing; `@aws-sdk/client-s3`
|
|
55
|
-
*
|
|
61
|
+
* nothing; `@aws-sdk/client-s3` is only imported when the provider builds the selected disk at
|
|
62
|
+
* boot, keeping the AWS SDK an optional peer. (Presigning needs no SDK at all — it is hand-rolled
|
|
63
|
+
* SigV4, see `sigv4.ts`.)
|
|
56
64
|
*/
|
|
57
65
|
export declare const disks: {
|
|
58
|
-
/** S3 (or S3-compatible) disk — needs the optional `@aws-sdk/client-s3`
|
|
66
|
+
/** S3 (or S3-compatible) disk — needs the optional `@aws-sdk/client-s3` peer. */
|
|
59
67
|
s3(config: S3DiskConfig): DiskFactory;
|
|
60
68
|
};
|
|
61
69
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/disks/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAE9C,yGAAyG;AACzG,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,sGAAsG;AACtG,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,aAAa,CAAC;IAC5B,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mGAAmG;IACnG,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;CACnC;AAED
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/disks/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAE9C,yGAAyG;AACzG,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,sGAAsG;AACtG,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,aAAa,CAAC;IAC5B,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mGAAmG;IACnG,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,KAAK;IAChB,iFAAiF;eACtE,YAAY,GAAG,WAAW;CAuBtC,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,SAAS,GACjD,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAQ/B"}
|
|
@@ -13,11 +13,12 @@
|
|
|
13
13
|
* ```
|
|
14
14
|
*
|
|
15
15
|
* Each factory returns a {@link DiskFactory} — a lazy thunk. Calling it in the config file costs
|
|
16
|
-
* nothing; `@aws-sdk/client-s3`
|
|
17
|
-
*
|
|
16
|
+
* nothing; `@aws-sdk/client-s3` is only imported when the provider builds the selected disk at
|
|
17
|
+
* boot, keeping the AWS SDK an optional peer. (Presigning needs no SDK at all — it is hand-rolled
|
|
18
|
+
* SigV4, see `sigv4.ts`.)
|
|
18
19
|
*/
|
|
19
20
|
export const disks = {
|
|
20
|
-
/** S3 (or S3-compatible) disk — needs the optional `@aws-sdk/client-s3`
|
|
21
|
+
/** S3 (or S3-compatible) disk — needs the optional `@aws-sdk/client-s3` peer. */
|
|
21
22
|
s3(config) {
|
|
22
23
|
return async () => {
|
|
23
24
|
const { S3Client } = await import('@aws-sdk/client-s3');
|
|
@@ -35,6 +36,7 @@ export const disks = {
|
|
|
35
36
|
...(config.publicBaseUrl !== undefined ? { publicBaseUrl: config.publicBaseUrl } : {}),
|
|
36
37
|
...(config.region !== undefined ? { region: config.region } : {}),
|
|
37
38
|
...(config.endpoint !== undefined ? { endpoint: config.endpoint } : {}),
|
|
39
|
+
...(config.publicEndpoint !== undefined ? { publicEndpoint: config.publicEndpoint } : {}),
|
|
38
40
|
...(config.forcePathStyle !== undefined ? { forcePathStyle: config.forcePathStyle } : {}),
|
|
39
41
|
...(config.visibility !== undefined ? { visibility: config.visibility } : {}),
|
|
40
42
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/disks/factory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/disks/factory.ts"],"names":[],"mappings":"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"}
|
package/dist/src/disks/s3.d.ts
CHANGED
|
@@ -19,6 +19,14 @@ export interface S3DiskOptions {
|
|
|
19
19
|
region?: string;
|
|
20
20
|
/** Custom endpoint (S3-compatible services). Used for URL synthesis when no `publicBaseUrl` is set. */
|
|
21
21
|
endpoint?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Endpoint presigned URLs are signed against, when it differs from `endpoint`. SigV4 embeds the
|
|
24
|
+
* `Host` in the signature, and presigned URLs are consumed by the BROWSER (a part `PUT` during a
|
|
25
|
+
* direct upload, a `GET` while streaming) — so when the app reaches storage through an internal
|
|
26
|
+
* FQDN the internet cannot resolve (MinIO behind a private network), set this to the public one.
|
|
27
|
+
* Server-side operations keep using `endpoint`; only what leaves the server is signed for here.
|
|
28
|
+
*/
|
|
29
|
+
publicEndpoint?: string;
|
|
22
30
|
/** Emit path-style URLs (`endpoint/bucket/key`) instead of virtual-hosted-style. */
|
|
23
31
|
forcePathStyle?: boolean;
|
|
24
32
|
/**
|
|
@@ -39,6 +47,7 @@ export interface S3DiskOptions {
|
|
|
39
47
|
* plus a {@link DiskCapabilities} descriptor), detected structurally via `isExtendedDisk`.
|
|
40
48
|
*/
|
|
41
49
|
export declare class S3Disk implements Disk, MultipartUploadDisk, ExtendedDisk {
|
|
50
|
+
#private;
|
|
42
51
|
/** Coarse feature descriptor — S3 supports presign, native multipart, and prefix listing. */
|
|
43
52
|
readonly capabilities: DiskCapabilities;
|
|
44
53
|
private readonly client;
|
|
@@ -47,6 +56,7 @@ export declare class S3Disk implements Disk, MultipartUploadDisk, ExtendedDisk {
|
|
|
47
56
|
private readonly publicBaseUrl;
|
|
48
57
|
private readonly region;
|
|
49
58
|
private readonly endpoint;
|
|
59
|
+
private readonly publicEndpoint;
|
|
50
60
|
private readonly forcePathStyle;
|
|
51
61
|
private readonly visibility;
|
|
52
62
|
constructor(options: S3DiskOptions);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../../src/disks/s3.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAWL,KAAK,QAAQ,EAEd,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../../src/disks/s3.ts"],"names":[],"mappings":"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"}
|
package/dist/src/disks/s3.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AbortMultipartUploadCommand, CompleteMultipartUploadCommand, CopyObjectCommand, CreateMultipartUploadCommand, DeleteObjectCommand, DeleteObjectsCommand, GetObjectCommand, HeadObjectCommand, ListObjectsV2Command, PutObjectCommand, UploadPartCommand, } from '@aws-sdk/client-s3';
|
|
2
|
-
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
3
2
|
import { hardenBodyStream } from './harden_body_stream.js';
|
|
3
|
+
import { presignS3Url } from './sigv4.js';
|
|
4
4
|
import { extractListObjectsV2FromXml, isXmlEntityDeserializationError } from './xml_fallback.js';
|
|
5
5
|
/** Default presigned-URL lifetime when a caller passes no `expiresIn` (30 minutes). */
|
|
6
6
|
const DEFAULT_SIGNED_URL_TTL_SECONDS = 1800;
|
|
@@ -48,6 +48,7 @@ export class S3Disk {
|
|
|
48
48
|
publicBaseUrl;
|
|
49
49
|
region;
|
|
50
50
|
endpoint;
|
|
51
|
+
publicEndpoint;
|
|
51
52
|
forcePathStyle;
|
|
52
53
|
visibility;
|
|
53
54
|
constructor(options) {
|
|
@@ -57,6 +58,7 @@ export class S3Disk {
|
|
|
57
58
|
this.publicBaseUrl = options.publicBaseUrl;
|
|
58
59
|
this.region = options.region;
|
|
59
60
|
this.endpoint = options.endpoint;
|
|
61
|
+
this.publicEndpoint = options.publicEndpoint;
|
|
60
62
|
this.forcePathStyle = options.forcePathStyle ?? false;
|
|
61
63
|
this.visibility = options.visibility ?? 'private';
|
|
62
64
|
this.capabilities = {
|
|
@@ -165,12 +167,80 @@ export class S3Disk {
|
|
|
165
167
|
return this.visibility;
|
|
166
168
|
}
|
|
167
169
|
async getSignedUrl(key, options) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
170
|
+
const query = {};
|
|
171
|
+
if (options?.contentType !== undefined)
|
|
172
|
+
query['response-content-type'] = options.contentType;
|
|
173
|
+
if (options?.contentDisposition !== undefined) {
|
|
174
|
+
query['response-content-disposition'] = options.contentDisposition;
|
|
175
|
+
}
|
|
176
|
+
return this.#presign('GET', this.key(key), query, toExpiresInSeconds(options?.expiresIn), {
|
|
177
|
+
browserFacing: true,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
// --- Hand-rolled SigV4 presigning ---------------------------------------------------------
|
|
181
|
+
/**
|
|
182
|
+
* Credentials + region for signing, resolved from the SDK client's own config so every credential
|
|
183
|
+
* source the client supports (static, env, shared config, IAM role) keeps working. Only the
|
|
184
|
+
* *signature computation* is hand-rolled — see {@link presignS3Url} for why (it is pure local
|
|
185
|
+
* crypto, and owning it is what lets a URL be signed for {@link S3DiskOptions.publicEndpoint}
|
|
186
|
+
* without constructing a second client per endpoint).
|
|
187
|
+
*/
|
|
188
|
+
async #signingContext() {
|
|
189
|
+
const config = this.client.config;
|
|
190
|
+
const credentials = typeof config.credentials === 'function' ? await config.credentials() : config.credentials;
|
|
191
|
+
let region = this.region;
|
|
192
|
+
if (region === undefined) {
|
|
193
|
+
try {
|
|
194
|
+
region = typeof config.region === 'function' ? await config.region() : config.region;
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
// A client constructed without a region (S3-compatible services often don't need one).
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return { credentials, region: region ?? 'us-east-1' };
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* The scheme/host/path a presigned URL is computed for. `browserFacing` URLs prefer
|
|
204
|
+
* {@link S3DiskOptions.publicEndpoint}: the signature bakes the `Host` in, and these URLs are
|
|
205
|
+
* consumed outside the server's network. Server-side presigns (the list XML fallback) always
|
|
206
|
+
* target the internal endpoint.
|
|
207
|
+
*/
|
|
208
|
+
#presignTarget(fullKey, options) {
|
|
209
|
+
const bucket = options.bucket ?? this.bucket;
|
|
210
|
+
const base = (options.browserFacing ? this.publicEndpoint : undefined) ?? this.endpoint;
|
|
211
|
+
if (base !== undefined) {
|
|
212
|
+
const url = new URL(base);
|
|
213
|
+
const basePath = url.pathname.replace(/\/+$/, '');
|
|
214
|
+
const protocol = url.protocol === 'http:' ? 'http:' : 'https:';
|
|
215
|
+
if (this.forcePathStyle) {
|
|
216
|
+
return {
|
|
217
|
+
protocol,
|
|
218
|
+
host: url.host,
|
|
219
|
+
path: `${basePath}/${bucket}${fullKey ? `/${fullKey}` : ''}`,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
return { protocol, host: `${bucket}.${url.host}`, path: `${basePath}/${fullKey}` };
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
protocol: 'https:',
|
|
226
|
+
host: `${bucket}.s3.${options.region}.amazonaws.com`,
|
|
227
|
+
path: `/${fullKey}`,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/** Presign one request against this disk. `fullKey` is already prefixed; `''` targets the bucket. */
|
|
231
|
+
async #presign(method, fullKey, query, expiresInSeconds, options) {
|
|
232
|
+
const { credentials, region } = await this.#signingContext();
|
|
233
|
+
const target = this.#presignTarget(fullKey, { ...options, region });
|
|
234
|
+
return presignS3Url({
|
|
235
|
+
method,
|
|
236
|
+
protocol: target.protocol,
|
|
237
|
+
host: target.host,
|
|
238
|
+
path: target.path,
|
|
239
|
+
query,
|
|
240
|
+
credentials,
|
|
241
|
+
region,
|
|
242
|
+
expiresInSeconds,
|
|
243
|
+
});
|
|
174
244
|
}
|
|
175
245
|
async getMetaData(key) {
|
|
176
246
|
try {
|
|
@@ -282,15 +352,22 @@ export class S3Disk {
|
|
|
282
352
|
if (!isXmlEntityDeserializationError(err))
|
|
283
353
|
throw err;
|
|
284
354
|
// fast-xml-parser rejected valid entity refs in the ListObjectsV2 XML — presign the same
|
|
285
|
-
// request and fetch the raw body, then parse it by hand
|
|
286
|
-
//
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
355
|
+
// request (hand-rolled SigV4, see sigv4.ts) and fetch the raw body, then parse it by hand
|
|
356
|
+
// into the same shape. This is a SERVER-side fetch, so it deliberately targets the internal
|
|
357
|
+
// endpoint even when a `publicEndpoint` is configured (see xml_fallback.ts).
|
|
358
|
+
const listQuery = {
|
|
359
|
+
'list-type': '2',
|
|
360
|
+
prefix: fullPrefix,
|
|
361
|
+
delimiter: options?.delimiter ?? '/',
|
|
362
|
+
};
|
|
363
|
+
if (options?.limit !== undefined)
|
|
364
|
+
listQuery['max-keys'] = String(options.limit);
|
|
365
|
+
if (options?.cursor !== undefined)
|
|
366
|
+
listQuery['continuation-token'] = options.cursor;
|
|
367
|
+
const signedUrl = await this.#presign('GET', '', listQuery, 60, {
|
|
368
|
+
browserFacing: false,
|
|
369
|
+
bucket,
|
|
370
|
+
});
|
|
294
371
|
const response = await fetch(signedUrl);
|
|
295
372
|
if (!response.ok) {
|
|
296
373
|
throw new Error(`S3Disk: signed list fallback failed: HTTP ${response.status} ${response.statusText}`);
|
|
@@ -337,12 +414,9 @@ export class S3Disk {
|
|
|
337
414
|
return { partNumber, etag: out.ETag };
|
|
338
415
|
}
|
|
339
416
|
async presignUploadPart(key, uploadId, partNumber, expiresInSeconds) {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
UploadId: uploadId,
|
|
344
|
-
PartNumber: partNumber,
|
|
345
|
-
}), { expiresIn: Math.max(1, Math.round(expiresInSeconds)) });
|
|
417
|
+
// Browser-facing: the client PUTs the part bytes to this URL, so it is signed against the
|
|
418
|
+
// public endpoint when one is configured.
|
|
419
|
+
return this.#presign('PUT', this.key(key), { uploadId, partNumber: String(partNumber) }, expiresInSeconds, { browserFacing: true });
|
|
346
420
|
}
|
|
347
421
|
async completeMultipartUpload(key, uploadId, parts) {
|
|
348
422
|
await this.client.send(new CompleteMultipartUploadCommand({
|