@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,299 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { publishMedia } from './diagnostics.js';
|
|
3
|
+
import { MimeNotAllowedError, UploadNotSupportedError, UploadPartOutOfRangeError, UploadPartSizeError, UploadPartsIncompleteError, UploadSessionExpiredError, UploadSessionNotFoundError, } from './errors.js';
|
|
4
|
+
import { isMultipartCapable } from './multipart.js';
|
|
5
|
+
/**
|
|
6
|
+
* Default part size: 20 MiB. Between S3's 5 MiB minimum and a reasonable payload per browser `PUT`;
|
|
7
|
+
* with S3's 10,000-part cap it covers files up to ~195 GiB. Larger than the proxy default (8 MiB)
|
|
8
|
+
* on purpose — direct parts don't occupy app memory, so the only cost of a bigger part is a bigger
|
|
9
|
+
* retry unit.
|
|
10
|
+
*/
|
|
11
|
+
export const DEFAULT_DIRECT_PART_SIZE = 20 * 1024 * 1024;
|
|
12
|
+
/** S3's minimum part size (every part but the last). Enforced at `initiate` — see {@link UploadPartSizeError}. */
|
|
13
|
+
export const MIN_DIRECT_PART_SIZE = 5 * 1024 * 1024;
|
|
14
|
+
/** S3's cap on parts per multipart upload. */
|
|
15
|
+
export const MAX_DIRECT_PARTS = 10_000;
|
|
16
|
+
/**
|
|
17
|
+
* Default presigned part-URL lifetime (1 hour). Deliberately not sized to "the whole upload on a
|
|
18
|
+
* slow connection": {@link DirectUploadManager.status} re-issues fresh URLs for whatever is still
|
|
19
|
+
* pending, so an expired URL costs one status round-trip, not the upload.
|
|
20
|
+
*/
|
|
21
|
+
const DEFAULT_PRESIGN_TTL_SECONDS = 3600;
|
|
22
|
+
/** Session-metadata key marking a session as a direct upload and carrying its agreed part size. */
|
|
23
|
+
const PART_SIZE_METADATA_KEY = 'direct:partSize';
|
|
24
|
+
/**
|
|
25
|
+
* Session-backed direct-to-S3 multipart uploads: the browser `PUT`s every byte straight to storage
|
|
26
|
+
* through presigned part URLs, and the app only coordinates — open, presign, collect ETags, close.
|
|
27
|
+
* The app's bandwidth cost is a handful of JSON round-trips regardless of file size, which is the
|
|
28
|
+
* entire point for video-sized files: the TUS path streams every chunk THROUGH the app (2× the
|
|
29
|
+
* bytes: client→app, app→S3), where this path streams none.
|
|
30
|
+
*
|
|
31
|
+
* What this adds over the raw primitives on {@link UploadManager} (`initiateDirect` & friends) is
|
|
32
|
+
* the **persisted session**: `uploadId`, the agreed `partSize` and every confirmed part ETag live
|
|
33
|
+
* in an {@link UploadSessionStore} — the same SPI the TUS engine persists its offsets in — instead
|
|
34
|
+
* of in the client's memory. A page reload no longer orphans the upload: the client asks
|
|
35
|
+
* {@link status} and gets back the confirmed ETags plus fresh URLs for what is missing.
|
|
36
|
+
*
|
|
37
|
+
* Framework-free, like {@link ResumableUploadManager}: the `DirectUploadHandler` maps it onto HTTP
|
|
38
|
+
* and the AdonisJS provider mounts that. `MediaManager.completeDirectUploadToLibrary` bridges a
|
|
39
|
+
* finished session into the media library through `attachExisting` — where the collection's
|
|
40
|
+
* whitelist checks the REAL bytes, exactly like every other upload path.
|
|
41
|
+
*/
|
|
42
|
+
export class DirectUploadManager {
|
|
43
|
+
storage;
|
|
44
|
+
sessions;
|
|
45
|
+
collections;
|
|
46
|
+
partSize;
|
|
47
|
+
presignTtlSeconds;
|
|
48
|
+
ttlSeconds;
|
|
49
|
+
newId;
|
|
50
|
+
now;
|
|
51
|
+
emitDiagnostics;
|
|
52
|
+
constructor(options) {
|
|
53
|
+
this.storage = options.storage;
|
|
54
|
+
this.sessions = options.sessions;
|
|
55
|
+
this.collections = options.collections;
|
|
56
|
+
this.partSize = options.partSize ?? DEFAULT_DIRECT_PART_SIZE;
|
|
57
|
+
this.presignTtlSeconds = options.presignTtlSeconds ?? DEFAULT_PRESIGN_TTL_SECONDS;
|
|
58
|
+
this.ttlSeconds = options.sessionTtlSeconds;
|
|
59
|
+
this.newId = options.idGenerator ?? (() => randomUUID());
|
|
60
|
+
this.now = options.clock ?? (() => new Date());
|
|
61
|
+
this.emitDiagnostics = options.emitDiagnostics ?? true;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Open a direct upload: validate everything that can be validated without bytes, start the native
|
|
65
|
+
* multipart upload, persist the session, and presign every part URL in one batch (presigning is
|
|
66
|
+
* local computation — see `sigv4.ts` — so a 10,000-part batch costs no S3 round-trips).
|
|
67
|
+
*/
|
|
68
|
+
async initiate(input) {
|
|
69
|
+
const diskName = input.disk ?? this.storage.defaultDisk;
|
|
70
|
+
const disk = this.storage.disk(diskName);
|
|
71
|
+
if (!isMultipartCapable(disk))
|
|
72
|
+
throw new UploadNotSupportedError(diskName);
|
|
73
|
+
if (typeof this.sessions.addPart !== 'function' ||
|
|
74
|
+
typeof this.sessions.listParts !== 'function') {
|
|
75
|
+
throw new UploadNotSupportedError(diskName, 'direct upload part recording');
|
|
76
|
+
}
|
|
77
|
+
if (!Number.isInteger(input.size) || input.size <= 0) {
|
|
78
|
+
throw new RangeError(`Direct upload size must be a positive integer, got ${input.size}`);
|
|
79
|
+
}
|
|
80
|
+
const partSize = input.partSize ?? this.partSize;
|
|
81
|
+
if (!Number.isInteger(partSize) || partSize < MIN_DIRECT_PART_SIZE) {
|
|
82
|
+
throw new UploadPartSizeError(`${partSize} bytes is below S3's ${MIN_DIRECT_PART_SIZE}-byte minimum`);
|
|
83
|
+
}
|
|
84
|
+
const totalParts = Math.max(1, Math.ceil(input.size / partSize));
|
|
85
|
+
if (totalParts > MAX_DIRECT_PARTS) {
|
|
86
|
+
throw new UploadPartSizeError(`${input.size} bytes at ${partSize} bytes/part needs ${totalParts} parts, over S3's cap of ${MAX_DIRECT_PARTS} — increase partSize`);
|
|
87
|
+
}
|
|
88
|
+
// The cheapest possible rejection, before the multipart upload even opens. Only the DECLARED
|
|
89
|
+
// type can be checked here (there are no bytes yet); the collection config stays the single
|
|
90
|
+
// source of truth, and attach-time validation of the real content remains the final barrier.
|
|
91
|
+
if (input.collection !== undefined && this.collections !== undefined) {
|
|
92
|
+
const accepts = this.collections.get(input.collection).acceptsMimeTypes;
|
|
93
|
+
if (accepts && input.contentType !== undefined && !accepts.includes(input.contentType)) {
|
|
94
|
+
throw new MimeNotAllowedError(input.collection, input.contentType);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const putOptions = input.contentType !== undefined || input.visibility !== undefined
|
|
98
|
+
? {
|
|
99
|
+
...(input.contentType !== undefined ? { contentType: input.contentType } : {}),
|
|
100
|
+
...(input.visibility !== undefined ? { visibility: input.visibility } : {}),
|
|
101
|
+
}
|
|
102
|
+
: undefined;
|
|
103
|
+
const { uploadId } = await disk.createMultipartUpload(input.key, putOptions);
|
|
104
|
+
const createdAt = this.now();
|
|
105
|
+
const session = await this.sessions.create({
|
|
106
|
+
id: this.newId(),
|
|
107
|
+
disk: diskName,
|
|
108
|
+
key: input.key,
|
|
109
|
+
contentType: input.contentType,
|
|
110
|
+
size: input.size,
|
|
111
|
+
offset: 0,
|
|
112
|
+
parts: 0,
|
|
113
|
+
multipartUploadId: uploadId,
|
|
114
|
+
createdAt,
|
|
115
|
+
...(this.ttlSeconds !== undefined
|
|
116
|
+
? { expiresAt: new Date(createdAt.getTime() + this.ttlSeconds * 1000) }
|
|
117
|
+
: {}),
|
|
118
|
+
metadata: { ...input.metadata, [PART_SIZE_METADATA_KEY]: String(partSize) },
|
|
119
|
+
});
|
|
120
|
+
const parts = await this.#presignParts(disk, session, Array.from({ length: totalParts }, (_, index) => index + 1));
|
|
121
|
+
this.emit('upload.start', {
|
|
122
|
+
id: session.id,
|
|
123
|
+
disk: diskName,
|
|
124
|
+
key: input.key,
|
|
125
|
+
mode: 'direct',
|
|
126
|
+
size: input.size,
|
|
127
|
+
contentType: input.contentType,
|
|
128
|
+
});
|
|
129
|
+
return {
|
|
130
|
+
id: session.id,
|
|
131
|
+
key: session.key,
|
|
132
|
+
disk: diskName,
|
|
133
|
+
partSize,
|
|
134
|
+
size: input.size,
|
|
135
|
+
totalParts,
|
|
136
|
+
parts,
|
|
137
|
+
expiresAt: session.expiresAt,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Record one part the client finished `PUT`ing (its S3 `ETag` response header). This is what makes
|
|
142
|
+
* the session resumable: an ETag the server holds survives any client-side loss of state. Safe to
|
|
143
|
+
* retry — confirming the same part again overwrites its ETag.
|
|
144
|
+
*/
|
|
145
|
+
async confirmPart(id, part) {
|
|
146
|
+
const session = await this.#require(id);
|
|
147
|
+
const { partSize, totalParts } = this.#geometry(session);
|
|
148
|
+
if (!Number.isInteger(part.partNumber) || part.partNumber < 1 || part.partNumber > totalParts) {
|
|
149
|
+
throw new UploadPartOutOfRangeError(part.partNumber, totalParts);
|
|
150
|
+
}
|
|
151
|
+
await this.sessions.addPart?.(id, { partNumber: part.partNumber, etag: part.etag });
|
|
152
|
+
// Recompute progress from what is actually recorded (parts confirm in any order and in
|
|
153
|
+
// parallel), so offset means "bytes safely on S3" rather than a linear cursor.
|
|
154
|
+
const confirmed = (await this.sessions.listParts?.(id)) ?? [];
|
|
155
|
+
session.parts = confirmed.length;
|
|
156
|
+
session.offset = confirmed.reduce((sum, recorded) => sum + this.#partBytes(session, partSize, totalParts, recorded.partNumber), 0);
|
|
157
|
+
await this.sessions.update(session);
|
|
158
|
+
this.emit('upload.progress', {
|
|
159
|
+
id: session.id,
|
|
160
|
+
offset: session.offset,
|
|
161
|
+
parts: session.parts,
|
|
162
|
+
size: session.size,
|
|
163
|
+
});
|
|
164
|
+
return { offset: session.offset, completedParts: confirmed.length };
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Where the upload stands, framed for resumption: the ETags already confirmed and a FRESH
|
|
168
|
+
* presigned URL for every part still missing. After a page reload the client calls this, slices
|
|
169
|
+
* the file with the returned `partSize`, uploads only `pendingParts`, and completes.
|
|
170
|
+
*/
|
|
171
|
+
async status(id) {
|
|
172
|
+
const session = await this.#require(id);
|
|
173
|
+
const disk = this.storage.disk(session.disk);
|
|
174
|
+
if (!isMultipartCapable(disk))
|
|
175
|
+
throw new UploadNotSupportedError(session.disk);
|
|
176
|
+
const { partSize, totalParts } = this.#geometry(session);
|
|
177
|
+
const completedParts = [...((await this.sessions.listParts?.(id)) ?? [])].sort((a, b) => a.partNumber - b.partNumber);
|
|
178
|
+
const confirmedNumbers = new Set(completedParts.map((part) => part.partNumber));
|
|
179
|
+
const pendingNumbers = Array.from({ length: totalParts }, (_, index) => index + 1).filter((partNumber) => !confirmedNumbers.has(partNumber));
|
|
180
|
+
return {
|
|
181
|
+
id: session.id,
|
|
182
|
+
key: session.key,
|
|
183
|
+
disk: session.disk,
|
|
184
|
+
partSize,
|
|
185
|
+
size: session.size,
|
|
186
|
+
totalParts,
|
|
187
|
+
contentType: session.contentType,
|
|
188
|
+
completedParts,
|
|
189
|
+
pendingParts: await this.#presignParts(disk, session, pendingNumbers),
|
|
190
|
+
expiresAt: session.expiresAt,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Assemble the parts into the final object and close the session. `parts` supplements what the
|
|
195
|
+
* session already holds (a client may hand over ETags it never got to confirm — e.g. it raced
|
|
196
|
+
* completion against its last confirms); on overlap the caller's ETag wins. Every part number in
|
|
197
|
+
* `1..totalParts` must be accounted for, or this throws {@link UploadPartsIncompleteError} naming
|
|
198
|
+
* the gaps — BEFORE the S3 round-trip that would report an opaque `InvalidPart`.
|
|
199
|
+
*
|
|
200
|
+
* This is the raw primitive (the object exists, no media record). To land in the library, use
|
|
201
|
+
* `MediaManager.completeDirectUploadToLibrary`, which chains `attachExisting` — collection
|
|
202
|
+
* whitelist against the real bytes included.
|
|
203
|
+
*/
|
|
204
|
+
async complete(id, parts = []) {
|
|
205
|
+
const session = await this.#require(id);
|
|
206
|
+
const disk = this.storage.disk(session.disk);
|
|
207
|
+
if (!isMultipartCapable(disk))
|
|
208
|
+
throw new UploadNotSupportedError(session.disk);
|
|
209
|
+
const { totalParts } = this.#geometry(session);
|
|
210
|
+
const byNumber = new Map();
|
|
211
|
+
for (const recorded of (await this.sessions.listParts?.(id)) ?? []) {
|
|
212
|
+
byNumber.set(recorded.partNumber, recorded);
|
|
213
|
+
}
|
|
214
|
+
for (const given of parts)
|
|
215
|
+
byNumber.set(given.partNumber, given);
|
|
216
|
+
const missing = Array.from({ length: totalParts }, (_, index) => index + 1).filter((partNumber) => !byNumber.has(partNumber));
|
|
217
|
+
if (missing.length > 0)
|
|
218
|
+
throw new UploadPartsIncompleteError(id, missing);
|
|
219
|
+
const assembled = [...byNumber.values()].sort((a, b) => a.partNumber - b.partNumber);
|
|
220
|
+
await disk.completeMultipartUpload(session.key, session.multipartUploadId, assembled);
|
|
221
|
+
await this.sessions.delete(id);
|
|
222
|
+
this.emit('upload.complete', { id: session.id, disk: session.disk, key: session.key });
|
|
223
|
+
return { key: session.key, disk: session.disk, size: session.size };
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Discard an in-flight upload: abort the native multipart upload (S3 frees the stored parts and
|
|
227
|
+
* stops charging for them) and drop the session. The S3 abort is best-effort — a bucket lifecycle
|
|
228
|
+
* rule may have already reaped the upload (`NoSuchUpload`), and a session that can no longer be
|
|
229
|
+
* aborted remotely must still be deletable locally.
|
|
230
|
+
*/
|
|
231
|
+
async abort(id) {
|
|
232
|
+
const session = await this.sessions.get(id);
|
|
233
|
+
if (!session)
|
|
234
|
+
return;
|
|
235
|
+
const disk = this.storage.disk(session.disk);
|
|
236
|
+
if (session.multipartUploadId && isMultipartCapable(disk)) {
|
|
237
|
+
await disk.abortMultipartUpload(session.key, session.multipartUploadId).catch(() => { });
|
|
238
|
+
}
|
|
239
|
+
await this.sessions.delete(id);
|
|
240
|
+
this.emit('upload.abort', { id: session.id, disk: session.disk, key: session.key });
|
|
241
|
+
}
|
|
242
|
+
/** In-progress direct sessions, optionally filtered. Empty when the store cannot enumerate. */
|
|
243
|
+
async list(filter) {
|
|
244
|
+
const sessions = (await this.sessions.list?.(filter)) ?? [];
|
|
245
|
+
return sessions.filter((session) => session.metadata?.[PART_SIZE_METADATA_KEY] !== undefined);
|
|
246
|
+
}
|
|
247
|
+
/** Presign a `PUT` URL for each given part number, in one local batch. */
|
|
248
|
+
async #presignParts(disk, session, partNumbers) {
|
|
249
|
+
const urls = [];
|
|
250
|
+
for (const partNumber of partNumbers) {
|
|
251
|
+
urls.push({
|
|
252
|
+
partNumber,
|
|
253
|
+
url: await disk.presignUploadPart(session.key, session.multipartUploadId, partNumber, this.presignTtlSeconds),
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
return urls;
|
|
257
|
+
}
|
|
258
|
+
/** The agreed slicing for a session, recovered from its persisted metadata. */
|
|
259
|
+
#geometry(session) {
|
|
260
|
+
const partSize = Number(session.metadata?.[PART_SIZE_METADATA_KEY]);
|
|
261
|
+
const size = session.size;
|
|
262
|
+
return { partSize, totalParts: Math.max(1, Math.ceil(size / partSize)) };
|
|
263
|
+
}
|
|
264
|
+
/** Byte length of one part: `partSize`, except the last part which takes the remainder. */
|
|
265
|
+
#partBytes(session, partSize, totalParts, partNumber) {
|
|
266
|
+
const size = session.size;
|
|
267
|
+
return partNumber === totalParts ? size - (totalParts - 1) * partSize : partSize;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Load a session, requiring it to be a DIRECT one: the store may be shared with the TUS engine
|
|
271
|
+
* (the Lucid driver even shares tables), so a session lacking the direct part-size marker — or a
|
|
272
|
+
* declared size, or a native upload id — is "not found" here, never misinterpreted. An expired
|
|
273
|
+
* session is treated as gone: native upload aborted, record deleted,
|
|
274
|
+
* {@link UploadSessionExpiredError} thrown.
|
|
275
|
+
*/
|
|
276
|
+
async #require(id) {
|
|
277
|
+
const session = await this.sessions.get(id);
|
|
278
|
+
if (!session ||
|
|
279
|
+
session.multipartUploadId === undefined ||
|
|
280
|
+
session.size === undefined ||
|
|
281
|
+
!Number.isFinite(Number(session.metadata?.[PART_SIZE_METADATA_KEY]))) {
|
|
282
|
+
throw new UploadSessionNotFoundError(id);
|
|
283
|
+
}
|
|
284
|
+
if (session.expiresAt && session.expiresAt.getTime() <= this.now().getTime()) {
|
|
285
|
+
const disk = this.storage.disk(session.disk);
|
|
286
|
+
if (isMultipartCapable(disk)) {
|
|
287
|
+
await disk.abortMultipartUpload(session.key, session.multipartUploadId).catch(() => { });
|
|
288
|
+
}
|
|
289
|
+
await this.sessions.delete(id);
|
|
290
|
+
throw new UploadSessionExpiredError(id);
|
|
291
|
+
}
|
|
292
|
+
return session;
|
|
293
|
+
}
|
|
294
|
+
emit(event, payload) {
|
|
295
|
+
if (this.emitDiagnostics)
|
|
296
|
+
publishMedia(event, payload);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=direct_upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"direct_upload.js","sourceRoot":"","sources":["../../src/direct_upload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { DirectUploadManager } from './direct_upload.js';
|
|
2
|
+
import type { AttachExistingInput } from './media_library.js';
|
|
3
|
+
import type { MediaRecord } from './media_record.js';
|
|
4
|
+
import type { DirectUploadPolicy, MultipartPart } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* A web-framework-neutral direct-upload request, one variant per lifecycle step (the provider maps
|
|
7
|
+
* an AdonisJS `HttpContext` to this; any other framework maps its own request the same way).
|
|
8
|
+
*/
|
|
9
|
+
export type DirectUploadRequest = {
|
|
10
|
+
action: 'initiate';
|
|
11
|
+
/** Name of the file being uploaded — feeds `keyFor`, never used as a key verbatim. */
|
|
12
|
+
fileName: string;
|
|
13
|
+
/** Total size in bytes. */
|
|
14
|
+
size: number;
|
|
15
|
+
contentType?: string | undefined;
|
|
16
|
+
/** Extra metadata persisted on the session. */
|
|
17
|
+
metadata?: Record<string, string> | undefined;
|
|
18
|
+
} | {
|
|
19
|
+
action: 'status';
|
|
20
|
+
uploadId: string;
|
|
21
|
+
} | {
|
|
22
|
+
action: 'confirm-part';
|
|
23
|
+
uploadId: string;
|
|
24
|
+
partNumber: number;
|
|
25
|
+
etag: string;
|
|
26
|
+
} | {
|
|
27
|
+
action: 'complete';
|
|
28
|
+
uploadId: string;
|
|
29
|
+
parts?: MultipartPart[] | undefined;
|
|
30
|
+
} | {
|
|
31
|
+
action: 'abort';
|
|
32
|
+
uploadId: string;
|
|
33
|
+
};
|
|
34
|
+
/** A web-framework-neutral response: a status code and a JSON-serializable body. */
|
|
35
|
+
export interface DirectUploadResponse {
|
|
36
|
+
status: number;
|
|
37
|
+
body?: unknown;
|
|
38
|
+
}
|
|
39
|
+
export interface DirectUploadHandlerOptions {
|
|
40
|
+
manager: DirectUploadManager;
|
|
41
|
+
/** Disk uploads land on. Defaults to the manager's default disk. */
|
|
42
|
+
disk?: string | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Collection this endpoint uploads into. Forwarded to the manager's `initiate`, whose MIME gate
|
|
45
|
+
* (against the registry the manager holds) rejects a wrong declared type with `415` before the
|
|
46
|
+
* multipart upload opens. The collection config stays the single source of truth.
|
|
47
|
+
*/
|
|
48
|
+
collection?: string | undefined;
|
|
49
|
+
/** Reject initiations whose `size` exceeds this many bytes (`413`). */
|
|
50
|
+
maxSize?: number | undefined;
|
|
51
|
+
/** Compute the final object key. Default: `uploads/<token>/<fileName>`. */
|
|
52
|
+
keyFor?: ((fileName: string, token: string, metadata: Record<string, string>) => string) | undefined;
|
|
53
|
+
idGenerator?: (() => string) | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* App-injected strategy that owns the variant decisions (key resolution, what the upload becomes
|
|
56
|
+
* on completion, error→HTTP mapping). When present, `initiate`/`complete`/`abort` route through
|
|
57
|
+
* it; when absent, the handler keeps its built-in key/complete behavior. See {@link DirectUploadPolicy}.
|
|
58
|
+
*/
|
|
59
|
+
policy?: DirectUploadPolicy<unknown, unknown> | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Adopt an assembled object into the media library — wired to `MediaManager.completeDirectUploadToLibrary`
|
|
62
|
+
* by the provider. The policy's `complete` path calls it with the resolution's `sessionId`/`target`
|
|
63
|
+
* (plus the caller-supplied parts) and returns the resulting record. Unused without a {@link policy}.
|
|
64
|
+
*/
|
|
65
|
+
adopt?: ((sessionId: string, target: Omit<AttachExistingInput, 'key' | 'disk' | 'size'>, parts?: MultipartPart[] | undefined) => Promise<MediaRecord>) | undefined;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* HTTP half of the session-backed direct upload flow, mirroring {@link TusUploadHandler}: the core
|
|
69
|
+
* ({@link DirectUploadManager}) never sees HTTP, this class never sees a framework, and a thin
|
|
70
|
+
* AdonisJS route adapter maps `HttpContext` to {@link DirectUploadRequest}/{@link DirectUploadResponse}.
|
|
71
|
+
*
|
|
72
|
+
* The endpoint surface it expects to be mounted on (see the provider's `uploads.direct.routes`):
|
|
73
|
+
*
|
|
74
|
+
* - `POST /` → `initiate` — `201` + session id, part size, presigned URLs
|
|
75
|
+
* - `GET /:id` → `status` — confirmed ETags + fresh URLs for pending parts
|
|
76
|
+
* - `POST /:id/parts/:partNumber` → `confirm-part` — record one uploaded part's ETag
|
|
77
|
+
* - `POST /:id/complete` → `complete` — assemble; `409` names any missing parts
|
|
78
|
+
* - `DELETE /:id` → `abort`
|
|
79
|
+
*
|
|
80
|
+
* **This handler performs NO authorization** — the same split as `TusUploadHandler` and
|
|
81
|
+
* `MediaDeliveryHandler`. Who may upload, and into whose namespace the key resolves, are questions
|
|
82
|
+
* only the app can answer: guard the route, and resolve the key server-side via {@link keyFor}
|
|
83
|
+
* (the client sends a `fileName`, never a key).
|
|
84
|
+
*/
|
|
85
|
+
export declare class DirectUploadHandler {
|
|
86
|
+
private readonly manager;
|
|
87
|
+
private readonly disk;
|
|
88
|
+
private readonly collection;
|
|
89
|
+
private readonly maxSize;
|
|
90
|
+
private readonly keyFor;
|
|
91
|
+
private readonly newId;
|
|
92
|
+
private readonly policy;
|
|
93
|
+
private readonly adopt;
|
|
94
|
+
constructor(options: DirectUploadHandlerOptions);
|
|
95
|
+
handle(req: DirectUploadRequest, ctx?: unknown): Promise<DirectUploadResponse>;
|
|
96
|
+
/** The `201` body for a created session — shared by the built-in and policy initiate paths. */
|
|
97
|
+
private initiateBody;
|
|
98
|
+
/**
|
|
99
|
+
* Policy-driven `initiate`: the policy decides the key/collection/metadata (and may attach a
|
|
100
|
+
* `response` merged onto the `201`), the manager opens the upload. If anything throws after the
|
|
101
|
+
* decision, the decision's `rollback` runs and the policy's `mapError` gets first refusal on the
|
|
102
|
+
* HTTP mapping (with the decision in the info); an unmapped error falls through to {@link mapError}.
|
|
103
|
+
*/
|
|
104
|
+
private initiatePolicy;
|
|
105
|
+
/**
|
|
106
|
+
* Policy-driven `complete`: `resolveComplete` maps the session onto a library `target`, `adopt`
|
|
107
|
+
* assembles + adopts the object, and the `200` body is whatever `onComplete` returns. Parts-shape
|
|
108
|
+
* validation is deliberately skipped — the policy owns what a completion means. `mapError` (with
|
|
109
|
+
* the resolution in the info) gets first refusal on failures; else {@link mapError}.
|
|
110
|
+
*/
|
|
111
|
+
private completePolicy;
|
|
112
|
+
/** Policy-driven `abort`: the policy cleans up app-side state, then the session is dropped. */
|
|
113
|
+
private abortPolicy;
|
|
114
|
+
/** Map manager errors onto HTTP statuses; anything unexpected keeps propagating. */
|
|
115
|
+
private mapError;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=direct_upload_handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"direct_upload_handler.d.ts","sourceRoot":"","sources":["../../src/direct_upload_handler.ts"],"names":[],"mappings":"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"}
|