@lenne.tech/nest-server 11.42.12 → 11.42.13
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/.claude/rules/testing.md +3 -3
- package/FRAMEWORK-API.md +1 -1
- package/dist/core/modules/error-code/error-codes.d.ts +45 -0
- package/dist/core/modules/error-code/error-codes.js +40 -0
- package/dist/core/modules/error-code/error-codes.js.map +1 -1
- package/dist/core/modules/file/core-file.service.d.ts +1 -0
- package/dist/core/modules/file/core-file.service.js +27 -0
- package/dist/core/modules/file/core-file.service.js.map +1 -1
- package/dist/core/modules/tenant/core-tenant.guard.js +7 -7
- package/dist/core/modules/tenant/core-tenant.guard.js.map +1 -1
- package/dist/core/modules/tus/core-tus.service.d.ts +1 -0
- package/dist/core/modules/tus/core-tus.service.js +10 -4
- package/dist/core/modules/tus/core-tus.service.js.map +1 -1
- package/dist/core/modules/tus/tus.constants.d.ts +1 -0
- package/dist/core/modules/tus/tus.constants.js +2 -1
- package/dist/core/modules/tus/tus.constants.js.map +1 -1
- package/dist/server/modules/error-code/error-codes.d.ts +5 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/REQUEST-LIFECYCLE.md +1 -1
- package/docs/error-codes.md +70 -0
- package/migration-guides/11.42.12-to-11.42.13.md +154 -0
- package/package.json +2 -2
- package/src/core/modules/error-code/error-codes.ts +49 -0
- package/src/core/modules/file/README.md +1 -0
- package/src/core/modules/file/core-file.service.ts +39 -0
- package/src/core/modules/tenant/INTEGRATION-CHECKLIST.md +2 -2
- package/src/core/modules/tenant/README.md +17 -1
- package/src/core/modules/tenant/core-tenant.guard.ts +7 -7
- package/src/core/modules/tus/INTEGRATION-CHECKLIST.md +2 -1
- package/src/core/modules/tus/README.md +42 -3
- package/src/core/modules/tus/core-tus.service.ts +32 -6
- package/src/core/modules/tus/tus.constants.ts +11 -0
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
FilesystemFileHelper,
|
|
22
22
|
} from '../file/filesystem-file.helper';
|
|
23
23
|
import { S3_FILES_COLLECTION, S3FileHelper } from '../file/s3-file.helper';
|
|
24
|
-
import { TUS_OWNER_METADATA_KEY, TUS_TENANT_METADATA_KEY } from './tus.constants';
|
|
24
|
+
import { TUS_FILE_ID_HEADER, TUS_OWNER_METADATA_KEY, TUS_TENANT_METADATA_KEY } from './tus.constants';
|
|
25
25
|
import { TusRedisLocker } from './tus-redis-locker';
|
|
26
26
|
import {
|
|
27
27
|
DEFAULT_TUS_ALLOWED_HEADERS,
|
|
@@ -86,6 +86,17 @@ export class CoreTusService implements OnModuleDestroy, OnModuleInit {
|
|
|
86
86
|
private files: mongo.GridFSBucket;
|
|
87
87
|
private cleanupInterval: NodeJS.Timeout | null = null;
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* The ID of the stored file per finished upload, recorded by {@link CoreTusService.onUploadComplete}
|
|
91
|
+
* and read by `onUploadFinish` for the `X-File-Id` response header.
|
|
92
|
+
*
|
|
93
|
+
* Recorded on the side rather than returned, so the hook keeps its `Promise<void>` signature: a
|
|
94
|
+
* project override in either documented shape — `await super.onUploadComplete(upload)` or
|
|
95
|
+
* `return super.onUploadComplete(upload)` — still compiles AND still yields the header. Keyed by
|
|
96
|
+
* the `Upload` object `onUploadFinish` hands to the hook, so nothing outlives the request.
|
|
97
|
+
*/
|
|
98
|
+
private readonly storedFileIds = new WeakMap<Upload, string>();
|
|
99
|
+
|
|
89
100
|
/** Datastore of the TUS server; set when uploads are staged in S3 */
|
|
90
101
|
protected s3Store: any = null;
|
|
91
102
|
|
|
@@ -183,10 +194,15 @@ export class CoreTusService implements OnModuleDestroy, OnModuleInit {
|
|
|
183
194
|
}
|
|
184
195
|
|
|
185
196
|
/**
|
|
186
|
-
* Handle upload completion - migrate
|
|
197
|
+
* Handle upload completion - migrate the finished upload into the configured file storage
|
|
187
198
|
*
|
|
188
199
|
* This method can be overridden in extending services to customize
|
|
189
200
|
* what happens after an upload completes.
|
|
201
|
+
*
|
|
202
|
+
* Records the ID of the stored file, which the completing response sends to the client as
|
|
203
|
+
* `X-File-Id` (`TUS_FILE_ID_HEADER`). An override must call `super.onUploadComplete(upload)` with
|
|
204
|
+
* the `upload` it received; one that does not stores nothing here and sends no header — the client
|
|
205
|
+
* then knows the TUS URL alone, whose last segment is the upload id, not the file id.
|
|
190
206
|
*/
|
|
191
207
|
protected async onUploadComplete(upload: Upload): Promise<void> {
|
|
192
208
|
try {
|
|
@@ -231,6 +247,7 @@ export class CoreTusService implements OnModuleDestroy, OnModuleInit {
|
|
|
231
247
|
`Upload ${upload.id} copied inside S3 to ${fileInfo._id} (filename: ${filename}) — no bytes through this process`,
|
|
232
248
|
);
|
|
233
249
|
await this.deleteStagedUpload(upload.id);
|
|
250
|
+
this.storedFileIds.set(upload, String(fileInfo._id));
|
|
234
251
|
return;
|
|
235
252
|
}
|
|
236
253
|
|
|
@@ -239,15 +256,16 @@ export class CoreTusService implements OnModuleDestroy, OnModuleInit {
|
|
|
239
256
|
return;
|
|
240
257
|
}
|
|
241
258
|
|
|
259
|
+
let fileInfo: { _id: unknown };
|
|
242
260
|
if (this.fileStorageDriver === 'filesystem') {
|
|
243
|
-
|
|
261
|
+
fileInfo = await FilesystemFileHelper.writeFile(
|
|
244
262
|
this.fileStorageDir,
|
|
245
263
|
this.connection.db.collection(FILESYSTEM_FILES_COLLECTION),
|
|
246
264
|
{ body: readStream, contentType, filename, metadata: fileMetadata },
|
|
247
265
|
);
|
|
248
266
|
this.logger.debug(`Upload ${upload.id} migrated to the filesystem as ${fileInfo._id} (filename: ${filename})`);
|
|
249
267
|
} else if (this.s3FileStorage) {
|
|
250
|
-
|
|
268
|
+
fileInfo = await S3FileHelper.writeFile(
|
|
251
269
|
this.options.s3Service,
|
|
252
270
|
this.connection.db.collection(S3_FILES_COLLECTION),
|
|
253
271
|
// Streamed with the length TUS already knows, so a multi-GB resumable upload is never
|
|
@@ -256,7 +274,7 @@ export class CoreTusService implements OnModuleDestroy, OnModuleInit {
|
|
|
256
274
|
);
|
|
257
275
|
this.logger.debug(`Upload ${upload.id} migrated to S3 as ${fileInfo._id} (filename: ${filename})`);
|
|
258
276
|
} else {
|
|
259
|
-
|
|
277
|
+
fileInfo = await GridFSHelper.writeFileFromStream(this.files, readStream, {
|
|
260
278
|
contentType,
|
|
261
279
|
filename,
|
|
262
280
|
metadata: fileMetadata,
|
|
@@ -266,6 +284,7 @@ export class CoreTusService implements OnModuleDestroy, OnModuleInit {
|
|
|
266
284
|
|
|
267
285
|
// Clean up the staged upload
|
|
268
286
|
await this.deleteStagedUpload(upload.id);
|
|
287
|
+
this.storedFileIds.set(upload, String(fileInfo._id));
|
|
269
288
|
} catch (error) {
|
|
270
289
|
this.logger.error(`Failed to migrate upload ${upload.id}: ${error.message}`);
|
|
271
290
|
throw error;
|
|
@@ -837,6 +856,9 @@ export class CoreTusService implements OnModuleDestroy, OnModuleInit {
|
|
|
837
856
|
// which a browser refuses on a credentialed (cookie) request — see resolveCorsOrigins().
|
|
838
857
|
...(cors ? { allowedCredentials: true, allowedOrigins: cors } : {}),
|
|
839
858
|
datastore,
|
|
859
|
+
// Appended to the tus protocol headers in `Access-Control-Expose-Headers`: a browser on a
|
|
860
|
+
// separate app origin cannot read a response header that is not listed there.
|
|
861
|
+
exposedHeaders: [TUS_FILE_ID_HEADER],
|
|
840
862
|
...(locker ? { locker } : {}),
|
|
841
863
|
maxSize: this.config.maxSize,
|
|
842
864
|
// Every request that names an upload — HEAD (offset), PATCH (append bytes), DELETE
|
|
@@ -864,10 +886,14 @@ export class CoreTusService implements OnModuleDestroy, OnModuleInit {
|
|
|
864
886
|
const relative = this.config.relativeLocation === true || (server as any)?.options?.relativeLocation === true;
|
|
865
887
|
return relative ? url : `${proto}://${host}${url}`;
|
|
866
888
|
},
|
|
889
|
+
// The response that completes the upload names the stored file. Without it the client knows
|
|
890
|
+
// only the TUS URL, whose last segment is the upload id — a staging key `/files/id/` answers
|
|
891
|
+
// with 404. An override of onUploadComplete() that never calls super records nothing: no header.
|
|
867
892
|
onUploadFinish: async (_req, upload) => {
|
|
868
893
|
try {
|
|
869
894
|
await this.onUploadComplete(upload);
|
|
870
|
-
|
|
895
|
+
const fileId = this.storedFileIds.get(upload);
|
|
896
|
+
return fileId ? { headers: { [TUS_FILE_ID_HEADER]: fileId } } : {};
|
|
871
897
|
} catch (error) {
|
|
872
898
|
// Do NOT swallow this. `onUploadComplete` is what moves the finished upload into its
|
|
873
899
|
// permanent store; returning `{}` after it failed answers the client with a 204 that
|
|
@@ -51,3 +51,14 @@ export const TUS_OWNER_METADATA_KEY = 'ltOwnerId';
|
|
|
51
51
|
* resolves to a tenant they are an active member of; otherwise it is `null`.
|
|
52
52
|
*/
|
|
53
53
|
export const TUS_TENANT_METADATA_KEY = 'ltTenantId';
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Response header naming the ID of the stored file, sent on the request that COMPLETES a tus upload
|
|
57
|
+
* (the last PATCH, or the POST of a creation-with-upload).
|
|
58
|
+
*
|
|
59
|
+
* Without it a client knows only the TUS URL, whose last segment is the tus UPLOAD id — a key of the
|
|
60
|
+
* staging store, which `/files/id/<id>` answers with 404. The header is also listed in
|
|
61
|
+
* `Access-Control-Expose-Headers`, because a browser on a separate app origin cannot read it
|
|
62
|
+
* otherwise.
|
|
63
|
+
*/
|
|
64
|
+
export const TUS_FILE_ID_HEADER = 'X-File-Id';
|