@nestjs/platform-fastify 12.0.3 → 12.1.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/Readme.md +4 -0
- package/adapters/fastify-adapter.d.ts +48 -5
- package/adapters/fastify-adapter.js +137 -9
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/interfaces/external/fastify-multipart-options.interface.d.ts +53 -0
- package/interfaces/external/fastify-multipart-options.interface.js +1 -0
- package/interfaces/external/index.d.ts +1 -0
- package/interfaces/external/index.js +1 -0
- package/multipart/files.constants.d.ts +1 -0
- package/multipart/files.constants.js +1 -0
- package/multipart/index.d.ts +4 -0
- package/multipart/index.js +4 -0
- package/multipart/interceptors/any-files.interceptor.d.ts +12 -0
- package/multipart/interceptors/any-files.interceptor.js +13 -0
- package/multipart/interceptors/file-fields.interceptor.d.ts +13 -0
- package/multipart/interceptors/file-fields.interceptor.js +14 -0
- package/multipart/interceptors/file-stream.interceptor.d.ts +31 -0
- package/multipart/interceptors/file-stream.interceptor.js +32 -0
- package/multipart/interceptors/file.interceptor.d.ts +17 -0
- package/multipart/interceptors/file.interceptor.js +18 -0
- package/multipart/interceptors/files.interceptor.d.ts +14 -0
- package/multipart/interceptors/files.interceptor.js +15 -0
- package/multipart/interceptors/index.d.ts +6 -0
- package/multipart/interceptors/index.js +6 -0
- package/multipart/interceptors/no-files.interceptor.d.ts +12 -0
- package/multipart/interceptors/no-files.interceptor.js +13 -0
- package/multipart/interfaces/files-upload-module.interface.d.ts +18 -0
- package/multipart/interfaces/files-upload-module.interface.js +1 -0
- package/multipart/interfaces/index.d.ts +3 -0
- package/multipart/interfaces/index.js +3 -0
- package/multipart/interfaces/multipart-file.interface.d.ts +70 -0
- package/multipart/interfaces/multipart-file.interface.js +1 -0
- package/multipart/interfaces/multipart-options.interface.d.ts +77 -0
- package/multipart/interfaces/multipart-options.interface.js +1 -0
- package/multipart/multipart/append-field.util.d.ts +6 -0
- package/multipart/multipart/append-field.util.js +112 -0
- package/multipart/multipart/multipart-interceptor.factory.d.ts +13 -0
- package/multipart/multipart/multipart-interceptor.factory.js +130 -0
- package/multipart/multipart/multipart.constants.d.ts +55 -0
- package/multipart/multipart/multipart.constants.js +74 -0
- package/multipart/multipart/multipart.error.d.ts +10 -0
- package/multipart/multipart/multipart.error.js +15 -0
- package/multipart/multipart/multipart.parser.d.ts +39 -0
- package/multipart/multipart/multipart.parser.js +369 -0
- package/multipart/multipart/multipart.utils.d.ts +28 -0
- package/multipart/multipart/multipart.utils.js +54 -0
- package/multipart/multipart.constants.d.ts +1 -0
- package/multipart/multipart.constants.js +1 -0
- package/multipart/multipart.module.d.ts +18 -0
- package/multipart/multipart.module.js +75 -0
- package/multipart/storage/disk.storage.d.ts +24 -0
- package/multipart/storage/disk.storage.js +58 -0
- package/multipart/storage/index.d.ts +2 -0
- package/multipart/storage/index.js +2 -0
- package/multipart/storage/memory.storage.d.ts +8 -0
- package/multipart/storage/memory.storage.js +23 -0
- package/package.json +8 -3
package/Readme.md
CHANGED
|
@@ -47,6 +47,10 @@ For questions and support please use the official [Discord channel](https://disc
|
|
|
47
47
|
|
|
48
48
|
Please make sure to read the [Issue Reporting Checklist](https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md#-submitting-an-issue) before opening an issue. Issues not conforming to the guidelines may be closed immediately.
|
|
49
49
|
|
|
50
|
+
## Observability
|
|
51
|
+
|
|
52
|
+
[NestJS Observe](https://observe.nestjs.com) is the official observability platform for Nest applications. Install the `@nestjs/observe` SDK, pass an API key, and requests, background jobs, errors, logs, and distributed traces start streaming to a dashboard - no manual span wiring and no collector to run. Because the SDK hooks into Nest's own request lifecycle, a trace reads like a call graph of your controllers and providers instead of a bare HTTP route. Free for up to 300,000 events a month, and there is a [live demo](https://www.observe-demo.nestjs.com/dashboard) with no signup.
|
|
53
|
+
|
|
50
54
|
## Consulting
|
|
51
55
|
|
|
52
56
|
With official support, you can get expert help straight from the Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://enterprise.nestjs.com).
|
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
import { FastifyCorsOptions } from '@fastify/cors';
|
|
2
|
-
import { Logger, type RequestMethod, type VersioningOptions } from '@nestjs/common';
|
|
2
|
+
import { Logger, type NestApplicationOptions, type RequestMethod, type VersioningOptions } from '@nestjs/common';
|
|
3
3
|
import { FastifyBaseLogger, FastifyBodyParser, FastifyInstance, FastifyPluginAsync, FastifyPluginCallback, FastifyRegister, FastifyReply, FastifyRequest, FastifyServerOptions, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerBase, RawServerDefault, RequestGenericInterface, RouteGenericInterface } from 'fastify';
|
|
4
4
|
import * as http from 'http';
|
|
5
5
|
import * as http2 from 'http2';
|
|
6
6
|
import * as https from 'https';
|
|
7
7
|
import { InjectOptions, Chain as LightMyRequestChain, Response as LightMyRequestResponse } from 'light-my-request';
|
|
8
|
-
import { type VersionValue } from '@nestjs/common/internal';
|
|
8
|
+
import { type SecurityRequestHook, type VersionValue } from '@nestjs/common/internal';
|
|
9
9
|
import { AbstractHttpAdapter } from '@nestjs/core';
|
|
10
|
-
import { FastifyStaticOptions, FastifyViewOptions } from '../interfaces/external/index.js';
|
|
10
|
+
import { FastifyMultipartOptions, FastifyStaticOptions, FastifyViewOptions } from '../interfaces/external/index.js';
|
|
11
11
|
import { NestFastifyBodyParserOptions } from '../interfaces/index.js';
|
|
12
12
|
type FastifyAdapterBaseOptions<Server extends RawServerBase = RawServerDefault, Logger extends FastifyBaseLogger = FastifyBaseLogger> = FastifyServerOptions<Server, Logger> & {
|
|
13
13
|
skipMiddie?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Controls the "@fastify/multipart" plugin (an optional peer dependency),
|
|
16
|
+
* which the upload interceptors from "@nestjs/platform-fastify/multipart"
|
|
17
|
+
* require.
|
|
18
|
+
*
|
|
19
|
+
* - unset (default): the adapter registers the plugin when an upload
|
|
20
|
+
* interceptor is used, unless it is already registered.
|
|
21
|
+
* - an object: plugin options (e.g. `limits`); the plugin is registered
|
|
22
|
+
* when the application initializes.
|
|
23
|
+
* - `true`: the same, with the plugin defaults.
|
|
24
|
+
* - `false`: the adapter never registers the plugin.
|
|
25
|
+
*/
|
|
26
|
+
multipart?: boolean | FastifyMultipartOptions;
|
|
14
27
|
};
|
|
15
28
|
type FastifyHttp2SecureOptions<Server extends http2.Http2SecureServer, Logger extends FastifyBaseLogger = FastifyBaseLogger> = FastifyAdapterBaseOptions<Server, Logger> & {
|
|
16
29
|
http2: true;
|
|
@@ -46,10 +59,15 @@ export declare class FastifyAdapter<TServer extends RawServerBase = RawServerDef
|
|
|
46
59
|
protected readonly logger: Logger;
|
|
47
60
|
protected readonly instance: TInstance;
|
|
48
61
|
protected _pathPrefix?: string;
|
|
62
|
+
private readonly openConnections;
|
|
63
|
+
private isClosing;
|
|
49
64
|
private _isParserRegistered;
|
|
50
65
|
private onRequestHook?;
|
|
51
66
|
private onResponseHook?;
|
|
52
67
|
private isMiddieRegistered;
|
|
68
|
+
private multipartMode;
|
|
69
|
+
private multipartOptions;
|
|
70
|
+
private isMultipartRequested;
|
|
53
71
|
private pendingMiddlewares;
|
|
54
72
|
private versioningOptions?;
|
|
55
73
|
private readonly versionConstraint;
|
|
@@ -89,10 +107,21 @@ export declare class FastifyAdapter<TServer extends RawServerBase = RawServerDef
|
|
|
89
107
|
getHttpServer<T = TServer>(): T;
|
|
90
108
|
getInstance<T = TInstance>(): T;
|
|
91
109
|
register<TRegister extends Parameters<FastifyRegister<FastifyInstance<TServer, TRawRequest, TRawResponse>>>>(plugin: TRegister['0'], opts?: TRegister['1']): any;
|
|
110
|
+
/**
|
|
111
|
+
* Registers the "@fastify/multipart" plugin, unless the adapter's
|
|
112
|
+
* `multipart` option is `false`, or the plugin has been registered
|
|
113
|
+
* already by the time it loads. Idempotent. Called by the upload
|
|
114
|
+
* interceptors from "@nestjs/platform-fastify/multipart".
|
|
115
|
+
*
|
|
116
|
+
* The plugin is loaded lazily, and a missing package fails the
|
|
117
|
+
* application's startup (`ready()`) with an error naming the package,
|
|
118
|
+
* rather than exiting the process.
|
|
119
|
+
*/
|
|
120
|
+
useMultipart(): void;
|
|
92
121
|
inject(): LightMyRequestChain;
|
|
93
122
|
inject(opts: InjectOptions | string): Promise<LightMyRequestResponse>;
|
|
94
|
-
close(): Promise<
|
|
95
|
-
initHttpServer(): void;
|
|
123
|
+
close(): Promise<void>;
|
|
124
|
+
initHttpServer(options?: NestApplicationOptions): void;
|
|
96
125
|
useStaticAssets(options: FastifyStaticOptions): Promise<any>;
|
|
97
126
|
setViewEngine(options: FastifyViewOptions | string): Promise<any>;
|
|
98
127
|
isHeadersSent(response: TReply): boolean;
|
|
@@ -104,6 +133,18 @@ export declare class FastifyAdapter<TServer extends RawServerBase = RawServerDef
|
|
|
104
133
|
getRequestUrl(request: TRequest): string;
|
|
105
134
|
getRequestUrl(request: TRawRequest): string;
|
|
106
135
|
enableCors(options?: FastifyCorsOptions): void;
|
|
136
|
+
/**
|
|
137
|
+
* Runs the request hook of the built-in HTTP security features in an
|
|
138
|
+
* `onRequest` hook: before middie (Nest middleware), content-type parsing,
|
|
139
|
+
* guards and handlers, and also for unmatched routes. Headers set by the
|
|
140
|
+
* hook go to the Node.js response (`reply.raw`): Fastify merges them into
|
|
141
|
+
* every response it sends, errors and `404`s included, while
|
|
142
|
+
* `reply.header()` / `@Header()` values take precedence, and responses
|
|
143
|
+
* written to `reply.raw` directly (e.g. `@Sse()`) carry them too. A
|
|
144
|
+
* rejection goes to `done(error)`, i.e. to the Nest exception layer
|
|
145
|
+
* installed with `setErrorHandler()`.
|
|
146
|
+
*/
|
|
147
|
+
registerSecurityHook(hook: SecurityRequestHook<TRequest>): void;
|
|
107
148
|
registerParserMiddleware(prefix?: string, rawBody?: boolean): void;
|
|
108
149
|
useBodyParser(type: string | string[] | RegExp, rawBody: boolean, options?: NestFastifyBodyParserOptions, parser?: FastifyBodyParser<Buffer, TServer>): void;
|
|
109
150
|
createMiddlewareFactory(requestMethod: RequestMethod): Promise<(path: string, callback: Function) => any>;
|
|
@@ -144,5 +185,7 @@ export declare class FastifyAdapter<TServer extends RawServerBase = RawServerDef
|
|
|
144
185
|
* and leaves any other request target untouched.
|
|
145
186
|
*/
|
|
146
187
|
private getPathFromRequestTarget;
|
|
188
|
+
private trackOpenConnections;
|
|
189
|
+
private closeOpenConnections;
|
|
147
190
|
}
|
|
148
191
|
export {};
|
|
@@ -2,9 +2,10 @@ import { HttpException, HttpStatus, Logger, StreamableFile, VERSION_NEUTRAL, Ver
|
|
|
2
2
|
import { fastify, } from 'fastify';
|
|
3
3
|
import Reply from 'fastify/lib/reply.js';
|
|
4
4
|
import fastifySymbols from 'fastify/lib/symbols.js';
|
|
5
|
+
import * as net from 'net';
|
|
5
6
|
import { pathToRegexp } from 'path-to-regexp';
|
|
6
7
|
import middie from '@fastify/middie';
|
|
7
|
-
import { loadPackage, isNil, isString, isUndefined, } from '@nestjs/common/internal';
|
|
8
|
+
import { loadPackage, tryLoadPackage, isNil, isString, isUndefined, } from '@nestjs/common/internal';
|
|
8
9
|
import { AbstractHttpAdapter } from '@nestjs/core';
|
|
9
10
|
import { LegacyRouteConverter } from '@nestjs/core/internal';
|
|
10
11
|
const { kRouteContext } = fastifySymbols;
|
|
@@ -13,16 +14,29 @@ import { parse as querystringParse } from 'fast-querystring';
|
|
|
13
14
|
import urlSanitizer from 'find-my-way/lib/url-sanitizer.js';
|
|
14
15
|
import { FASTIFY_ROUTE_CONFIG_METADATA, FASTIFY_ROUTE_CONSTRAINTS_METADATA, FASTIFY_ROUTE_SCHEMA_METADATA, } from '../constants.js';
|
|
15
16
|
const { safeDecodeURI } = urlSanitizer;
|
|
17
|
+
const MISSING_MULTIPART_PACKAGE_MESSAGE = 'The "@fastify/multipart" package is missing. Please, make sure to install it (npm i @fastify/multipart) ' +
|
|
18
|
+
'to use the file upload interceptors from "@nestjs/platform-fastify/multipart".';
|
|
19
|
+
function isFastifyMultipartPlugin(plugin) {
|
|
20
|
+
const fn = plugin?.default ?? plugin;
|
|
21
|
+
return (typeof fn === 'function' &&
|
|
22
|
+
(fn[Symbol.for('plugin-meta')]?.name === '@fastify/multipart' ||
|
|
23
|
+
fn.name === 'fastifyMultipart'));
|
|
24
|
+
}
|
|
16
25
|
/**
|
|
17
26
|
* @publicApi
|
|
18
27
|
*/
|
|
19
28
|
export class FastifyAdapter extends AbstractHttpAdapter {
|
|
20
29
|
logger = new Logger(FastifyAdapter.name);
|
|
21
30
|
_pathPrefix;
|
|
31
|
+
openConnections = new Set();
|
|
32
|
+
isClosing = false;
|
|
22
33
|
_isParserRegistered;
|
|
23
34
|
onRequestHook;
|
|
24
35
|
onResponseHook;
|
|
25
36
|
isMiddieRegistered;
|
|
37
|
+
multipartMode = 'auto';
|
|
38
|
+
multipartOptions = {};
|
|
39
|
+
isMultipartRequested = false;
|
|
26
40
|
pendingMiddlewares = [];
|
|
27
41
|
versioningOptions;
|
|
28
42
|
versionConstraint = {
|
|
@@ -110,6 +124,15 @@ export class FastifyAdapter extends AbstractHttpAdapter {
|
|
|
110
124
|
if (instanceOrOptions?.skipMiddie) {
|
|
111
125
|
this.isMiddieRegistered = true;
|
|
112
126
|
}
|
|
127
|
+
const multipart = instanceOrOptions
|
|
128
|
+
?.multipart;
|
|
129
|
+
if (multipart === false) {
|
|
130
|
+
this.multipartMode = 'off';
|
|
131
|
+
}
|
|
132
|
+
else if (multipart) {
|
|
133
|
+
this.multipartMode = 'eager';
|
|
134
|
+
this.multipartOptions = multipart === true ? {} : { ...multipart };
|
|
135
|
+
}
|
|
113
136
|
this.instance.addHook('onRequest', (request, reply, done) => {
|
|
114
137
|
if (this.onRequestHook) {
|
|
115
138
|
this.onRequestHook(request, reply, done);
|
|
@@ -134,6 +157,9 @@ export class FastifyAdapter extends AbstractHttpAdapter {
|
|
|
134
157
|
this.onResponseHook = hook;
|
|
135
158
|
}
|
|
136
159
|
async init() {
|
|
160
|
+
if (this.multipartMode === 'eager') {
|
|
161
|
+
this.useMultipart();
|
|
162
|
+
}
|
|
137
163
|
if (this.isMiddieRegistered) {
|
|
138
164
|
return;
|
|
139
165
|
}
|
|
@@ -292,25 +318,79 @@ export class FastifyAdapter extends AbstractHttpAdapter {
|
|
|
292
318
|
return this.instance;
|
|
293
319
|
}
|
|
294
320
|
register(plugin, opts) {
|
|
321
|
+
if (this.multipartMode !== 'off' &&
|
|
322
|
+
isFastifyMultipartPlugin(plugin) &&
|
|
323
|
+
!this.instance.hasRequestDecorator('isMultipart')) {
|
|
324
|
+
// The adapter owns the "@fastify/multipart" registration, so that the
|
|
325
|
+
// plugin is registered once whether or not the user registers it too.
|
|
326
|
+
// The user's options apply over the adapter's.
|
|
327
|
+
this.multipartOptions = { ...this.multipartOptions, ...opts };
|
|
328
|
+
this.useMultipart();
|
|
329
|
+
return this.instance;
|
|
330
|
+
}
|
|
295
331
|
return this.instance.register(plugin, opts);
|
|
296
332
|
}
|
|
333
|
+
/**
|
|
334
|
+
* Registers the "@fastify/multipart" plugin, unless the adapter's
|
|
335
|
+
* `multipart` option is `false`, or the plugin has been registered
|
|
336
|
+
* already by the time it loads. Idempotent. Called by the upload
|
|
337
|
+
* interceptors from "@nestjs/platform-fastify/multipart".
|
|
338
|
+
*
|
|
339
|
+
* The plugin is loaded lazily, and a missing package fails the
|
|
340
|
+
* application's startup (`ready()`) with an error naming the package,
|
|
341
|
+
* rather than exiting the process.
|
|
342
|
+
*/
|
|
343
|
+
useMultipart() {
|
|
344
|
+
if (this.multipartMode === 'off' || this.isMultipartRequested) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
this.isMultipartRequested = true;
|
|
348
|
+
const registerMultipart = async (instance) => {
|
|
349
|
+
// Plugins load in registration order, so one the user registered
|
|
350
|
+
// before this point has loaded by now.
|
|
351
|
+
if (instance.hasRequestDecorator('isMultipart')) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const multipart = await tryLoadPackage('@fastify/multipart', () => import('@fastify/multipart'));
|
|
355
|
+
if (!multipart) {
|
|
356
|
+
throw new Error(MISSING_MULTIPART_PACKAGE_MESSAGE);
|
|
357
|
+
}
|
|
358
|
+
// Copied: the plugin writes its defaults into the options it receives.
|
|
359
|
+
const { limits, ...options } = this.multipartOptions;
|
|
360
|
+
await instance.register(multipart, {
|
|
361
|
+
...options,
|
|
362
|
+
...(limits && { limits: { ...limits } }),
|
|
363
|
+
});
|
|
364
|
+
};
|
|
365
|
+
// Like `fastify-plugin`: register on the root context, not a child one.
|
|
366
|
+
Object.assign(registerMultipart, {
|
|
367
|
+
[Symbol.for('skip-override')]: true,
|
|
368
|
+
[Symbol.for('fastify.display-name')]: 'nestjs-multipart',
|
|
369
|
+
});
|
|
370
|
+
this.instance.register(registerMultipart);
|
|
371
|
+
}
|
|
297
372
|
inject(opts) {
|
|
298
373
|
return this.instance.inject(opts);
|
|
299
374
|
}
|
|
300
375
|
async close() {
|
|
376
|
+
this.isClosing = true;
|
|
301
377
|
try {
|
|
302
|
-
|
|
378
|
+
this.closeOpenConnections();
|
|
303
379
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
380
|
+
finally {
|
|
381
|
+
await this.instance.close().catch(err => {
|
|
382
|
+
// Check if server is still running
|
|
383
|
+
if (err.code !== 'ERR_SERVER_NOT_RUNNING') {
|
|
384
|
+
throw err;
|
|
385
|
+
}
|
|
386
|
+
});
|
|
310
387
|
}
|
|
311
388
|
}
|
|
312
|
-
initHttpServer() {
|
|
389
|
+
initHttpServer(options = {}) {
|
|
313
390
|
this.httpServer = this.instance.server;
|
|
391
|
+
if (options?.forceCloseConnections) {
|
|
392
|
+
this.trackOpenConnections();
|
|
393
|
+
}
|
|
314
394
|
}
|
|
315
395
|
async useStaticAssets(options) {
|
|
316
396
|
return this.register(await loadPackage('@fastify/static', 'FastifyAdapter.useStaticAssets()', () => import('@fastify/static')), options);
|
|
@@ -356,6 +436,22 @@ export class FastifyAdapter extends AbstractHttpAdapter {
|
|
|
356
436
|
enableCors(options) {
|
|
357
437
|
this.register(import('@fastify/cors'), options);
|
|
358
438
|
}
|
|
439
|
+
/**
|
|
440
|
+
* Runs the request hook of the built-in HTTP security features in an
|
|
441
|
+
* `onRequest` hook: before middie (Nest middleware), content-type parsing,
|
|
442
|
+
* guards and handlers, and also for unmatched routes. Headers set by the
|
|
443
|
+
* hook go to the Node.js response (`reply.raw`): Fastify merges them into
|
|
444
|
+
* every response it sends, errors and `404`s included, while
|
|
445
|
+
* `reply.header()` / `@Header()` values take precedence, and responses
|
|
446
|
+
* written to `reply.raw` directly (e.g. `@Sse()`) carry them too. A
|
|
447
|
+
* rejection goes to `done(error)`, i.e. to the Nest exception layer
|
|
448
|
+
* installed with `setErrorHandler()`.
|
|
449
|
+
*/
|
|
450
|
+
registerSecurityHook(hook) {
|
|
451
|
+
this.instance.addHook('onRequest', (request, reply, done) => {
|
|
452
|
+
done(hook(request, reply.raw));
|
|
453
|
+
});
|
|
454
|
+
}
|
|
359
455
|
registerParserMiddleware(prefix, rawBody) {
|
|
360
456
|
if (this._isParserRegistered) {
|
|
361
457
|
return;
|
|
@@ -626,4 +722,36 @@ export class FastifyAdapter extends AbstractHttpAdapter {
|
|
|
626
722
|
}
|
|
627
723
|
return pathStart === -1 ? '/' : url.slice(pathStart);
|
|
628
724
|
}
|
|
725
|
+
trackOpenConnections() {
|
|
726
|
+
const track = (socket) => {
|
|
727
|
+
if (this.isClosing) {
|
|
728
|
+
// Fastify runs its `preClose` hooks before it stops accepting
|
|
729
|
+
// connections, so destroy anything that arrives in the meantime
|
|
730
|
+
socket.destroy();
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
if (this.openConnections.has(socket)) {
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
this.openConnections.add(socket);
|
|
737
|
+
socket.on('close', () => this.openConnections.delete(socket));
|
|
738
|
+
};
|
|
739
|
+
this.httpServer.on('connection', track);
|
|
740
|
+
// Sockets accepted by the secondary servers Fastify binds for every
|
|
741
|
+
// address `listen()` resolves to are only reachable through requests.
|
|
742
|
+
// `inject()` requests carry a mock socket, which must not be tracked.
|
|
743
|
+
this.instance.addHook('onRequest', (request, _reply, done) => {
|
|
744
|
+
const socket = request.raw.socket;
|
|
745
|
+
if (socket instanceof net.Socket) {
|
|
746
|
+
track(socket);
|
|
747
|
+
}
|
|
748
|
+
done();
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
closeOpenConnections() {
|
|
752
|
+
for (const socket of this.openConnections) {
|
|
753
|
+
socket.destroy();
|
|
754
|
+
this.openConnections.delete(socket);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
629
757
|
}
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "@fastify/multipart" plugin options, as accepted by the FastifyAdapter's
|
|
3
|
+
* `multipart` option. Declared locally so the adapter's typings do not
|
|
4
|
+
* require the (optional) plugin to be installed.
|
|
5
|
+
*
|
|
6
|
+
* `attachFieldsToBody` is deliberately left out: it consumes the body before
|
|
7
|
+
* the upload interceptors from "@nestjs/platform-fastify/multipart" run.
|
|
8
|
+
*
|
|
9
|
+
* @see https://github.com/fastify/fastify-multipart/blob/main/types/index.d.ts
|
|
10
|
+
* @publicApi
|
|
11
|
+
*/
|
|
12
|
+
export interface FastifyMultipartOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Limits applied to every multipart request. The upload interceptors merge
|
|
15
|
+
* their own `limits` over these.
|
|
16
|
+
*/
|
|
17
|
+
limits?: {
|
|
18
|
+
/**
|
|
19
|
+
* Max field name size in bytes. Not enforced by the plugin for multipart
|
|
20
|
+
* bodies; the upload interceptors enforce their own `limits.fieldNameSize`.
|
|
21
|
+
*/
|
|
22
|
+
fieldNameSize?: number;
|
|
23
|
+
/** Max field value size in bytes (Default: 1 MiB) */
|
|
24
|
+
fieldSize?: number;
|
|
25
|
+
/** Max number of non-file fields (Default: Infinity) */
|
|
26
|
+
fields?: number;
|
|
27
|
+
/** Max file size in bytes (Default: the Fastify `bodyLimit`, 1 MiB) */
|
|
28
|
+
fileSize?: number;
|
|
29
|
+
/** Max number of file fields (Default: Infinity) */
|
|
30
|
+
files?: number;
|
|
31
|
+
/** Max number of header key => value pairs (Default: 2000) */
|
|
32
|
+
headerPairs?: number;
|
|
33
|
+
/** Max number of parts, fields and files together (Default: 1000) */
|
|
34
|
+
parts?: number;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Throw when a file exceeds `limits.fileSize` while it is read through
|
|
38
|
+
* `req.file()` / `req.files()`. The upload interceptors always reject such
|
|
39
|
+
* a file, whatever this is set to.
|
|
40
|
+
*/
|
|
41
|
+
throwFileSizeLimit?: boolean;
|
|
42
|
+
/** Keep the full path of files instead of just the base name (Default: false) */
|
|
43
|
+
preservePath?: boolean;
|
|
44
|
+
/** Default character set for part header values (Default: 'latin1') */
|
|
45
|
+
defParamCharset?: string;
|
|
46
|
+
/** Default character set when one isn't defined (Default: 'utf8') */
|
|
47
|
+
defCharset?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Detect if a part is a file. By default a part is a file if its content
|
|
50
|
+
* type is `application/octet-stream` or it has a file name.
|
|
51
|
+
*/
|
|
52
|
+
isPartAFile?: (fieldName: string | undefined, contentType: string | undefined, fileName: string | undefined) => boolean;
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const MULTIPART_MODULE_OPTIONS = "MULTIPART_MODULE_OPTIONS";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const MULTIPART_MODULE_OPTIONS = 'MULTIPART_MODULE_OPTIONS';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NestInterceptor, Type } from '@nestjs/common';
|
|
2
|
+
import type { MultipartOptions } from '../interfaces/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Accepts every file, in any field, and exposes them as an array in
|
|
5
|
+
* `req.files` (`@UploadedFiles()`). The Fastify counterpart of
|
|
6
|
+
* platform-express's `AnyFilesInterceptor`.
|
|
7
|
+
*
|
|
8
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
9
|
+
*
|
|
10
|
+
* @publicApi
|
|
11
|
+
*/
|
|
12
|
+
export declare function AnyFilesInterceptor(localOptions?: MultipartOptions): Type<NestInterceptor>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createMultipartInterceptor } from '../multipart/multipart-interceptor.factory.js';
|
|
2
|
+
/**
|
|
3
|
+
* Accepts every file, in any field, and exposes them as an array in
|
|
4
|
+
* `req.files` (`@UploadedFiles()`). The Fastify counterpart of
|
|
5
|
+
* platform-express's `AnyFilesInterceptor`.
|
|
6
|
+
*
|
|
7
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
8
|
+
*
|
|
9
|
+
* @publicApi
|
|
10
|
+
*/
|
|
11
|
+
export function AnyFilesInterceptor(localOptions) {
|
|
12
|
+
return createMultipartInterceptor({ fields: 'ANY', strategy: 'ARRAY' }, localOptions);
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { NestInterceptor, Type } from '@nestjs/common';
|
|
2
|
+
import type { MultipartField, MultipartOptions } from '../interfaces/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Accepts files in each of `uploadFields` and exposes them grouped by field
|
|
5
|
+
* name in `req.files` (`@UploadedFiles()`). The Fastify counterpart of
|
|
6
|
+
* platform-express's `FileFieldsInterceptor`.
|
|
7
|
+
*
|
|
8
|
+
* @param uploadFields accepted fields, each with an optional `maxCount`
|
|
9
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
10
|
+
*
|
|
11
|
+
* @publicApi
|
|
12
|
+
*/
|
|
13
|
+
export declare function FileFieldsInterceptor(uploadFields: MultipartField[], localOptions?: MultipartOptions): Type<NestInterceptor>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createMultipartInterceptor } from '../multipart/multipart-interceptor.factory.js';
|
|
2
|
+
/**
|
|
3
|
+
* Accepts files in each of `uploadFields` and exposes them grouped by field
|
|
4
|
+
* name in `req.files` (`@UploadedFiles()`). The Fastify counterpart of
|
|
5
|
+
* platform-express's `FileFieldsInterceptor`.
|
|
6
|
+
*
|
|
7
|
+
* @param uploadFields accepted fields, each with an optional `maxCount`
|
|
8
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
9
|
+
*
|
|
10
|
+
* @publicApi
|
|
11
|
+
*/
|
|
12
|
+
export function FileFieldsInterceptor(uploadFields, localOptions) {
|
|
13
|
+
return createMultipartInterceptor({ fields: uploadFields, strategy: 'OBJECT' }, localOptions);
|
|
14
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { NestInterceptor, Type } from '@nestjs/common';
|
|
2
|
+
import type { MultipartOptions } from '../interfaces/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Hands the route handler a single file in `fieldName` as a stream
|
|
5
|
+
* (`req.file.stream`, see `MultipartFileStream`), without buffering it or
|
|
6
|
+
* writing it to disk. Fastify only; there is no platform-express
|
|
7
|
+
* counterpart.
|
|
8
|
+
*
|
|
9
|
+
* - Text fields must precede the file in the form: parts that follow it are
|
|
10
|
+
* not parsed.
|
|
11
|
+
* - Whatever the handler leaves unread is discarded before the response is
|
|
12
|
+
* sent, so that the client can finish uploading. When the handler returns
|
|
13
|
+
* a `StreamableFile` (which may be the upload itself), it is discarded
|
|
14
|
+
* once the response has been sent instead. So a handler that responds
|
|
15
|
+
* with a `StreamableFile` of something other than the upload should read
|
|
16
|
+
* the upload first: otherwise, when the connection is not kept alive
|
|
17
|
+
* (`Connection: close`), Node.js closes it right after the response, and
|
|
18
|
+
* a client still uploading fails with EPIPE / ECONNRESET.
|
|
19
|
+
* - A file in any other field fails the request with a 400.
|
|
20
|
+
* - The file size is unknown up front, so `MaxFileSizeValidator` does not
|
|
21
|
+
* apply, and neither does `FileTypeValidator`'s magic-number check.
|
|
22
|
+
* - Once `limits.fileSize` is exceeded, the stream errors with a
|
|
23
|
+
* `PayloadTooLargeException`.
|
|
24
|
+
*
|
|
25
|
+
* @param fieldName name of the form field that carries the file
|
|
26
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
27
|
+
* (`dest` and `storage` do not apply)
|
|
28
|
+
*
|
|
29
|
+
* @publicApi
|
|
30
|
+
*/
|
|
31
|
+
export declare function FileStreamInterceptor(fieldName: string, localOptions?: Omit<MultipartOptions, 'dest' | 'storage'>): Type<NestInterceptor>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createMultipartStreamInterceptor } from '../multipart/multipart-interceptor.factory.js';
|
|
2
|
+
/**
|
|
3
|
+
* Hands the route handler a single file in `fieldName` as a stream
|
|
4
|
+
* (`req.file.stream`, see `MultipartFileStream`), without buffering it or
|
|
5
|
+
* writing it to disk. Fastify only; there is no platform-express
|
|
6
|
+
* counterpart.
|
|
7
|
+
*
|
|
8
|
+
* - Text fields must precede the file in the form: parts that follow it are
|
|
9
|
+
* not parsed.
|
|
10
|
+
* - Whatever the handler leaves unread is discarded before the response is
|
|
11
|
+
* sent, so that the client can finish uploading. When the handler returns
|
|
12
|
+
* a `StreamableFile` (which may be the upload itself), it is discarded
|
|
13
|
+
* once the response has been sent instead. So a handler that responds
|
|
14
|
+
* with a `StreamableFile` of something other than the upload should read
|
|
15
|
+
* the upload first: otherwise, when the connection is not kept alive
|
|
16
|
+
* (`Connection: close`), Node.js closes it right after the response, and
|
|
17
|
+
* a client still uploading fails with EPIPE / ECONNRESET.
|
|
18
|
+
* - A file in any other field fails the request with a 400.
|
|
19
|
+
* - The file size is unknown up front, so `MaxFileSizeValidator` does not
|
|
20
|
+
* apply, and neither does `FileTypeValidator`'s magic-number check.
|
|
21
|
+
* - Once `limits.fileSize` is exceeded, the stream errors with a
|
|
22
|
+
* `PayloadTooLargeException`.
|
|
23
|
+
*
|
|
24
|
+
* @param fieldName name of the form field that carries the file
|
|
25
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
26
|
+
* (`dest` and `storage` do not apply)
|
|
27
|
+
*
|
|
28
|
+
* @publicApi
|
|
29
|
+
*/
|
|
30
|
+
export function FileStreamInterceptor(fieldName, localOptions) {
|
|
31
|
+
return createMultipartStreamInterceptor(fieldName, localOptions);
|
|
32
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { NestInterceptor, Type } from '@nestjs/common';
|
|
2
|
+
import type { MultipartOptions } from '../interfaces/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Accepts a single file in `fieldName` and exposes it as `req.file`
|
|
5
|
+
* (`@UploadedFile()`), with the text fields in `req.body`. The Fastify
|
|
6
|
+
* counterpart of platform-express's `FileInterceptor`.
|
|
7
|
+
*
|
|
8
|
+
* Requires the `@fastify/multipart` package, which the FastifyAdapter
|
|
9
|
+
* registers as a plugin when an upload interceptor is used (see its
|
|
10
|
+
* `multipart` option).
|
|
11
|
+
*
|
|
12
|
+
* @param fieldName name of the form field that carries the file
|
|
13
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
14
|
+
*
|
|
15
|
+
* @publicApi
|
|
16
|
+
*/
|
|
17
|
+
export declare function FileInterceptor(fieldName: string, localOptions?: MultipartOptions): Type<NestInterceptor>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createMultipartInterceptor } from '../multipart/multipart-interceptor.factory.js';
|
|
2
|
+
/**
|
|
3
|
+
* Accepts a single file in `fieldName` and exposes it as `req.file`
|
|
4
|
+
* (`@UploadedFile()`), with the text fields in `req.body`. The Fastify
|
|
5
|
+
* counterpart of platform-express's `FileInterceptor`.
|
|
6
|
+
*
|
|
7
|
+
* Requires the `@fastify/multipart` package, which the FastifyAdapter
|
|
8
|
+
* registers as a plugin when an upload interceptor is used (see its
|
|
9
|
+
* `multipart` option).
|
|
10
|
+
*
|
|
11
|
+
* @param fieldName name of the form field that carries the file
|
|
12
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
13
|
+
*
|
|
14
|
+
* @publicApi
|
|
15
|
+
*/
|
|
16
|
+
export function FileInterceptor(fieldName, localOptions) {
|
|
17
|
+
return createMultipartInterceptor({ fields: [{ name: fieldName, maxCount: 1 }], strategy: 'VALUE' }, localOptions);
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { NestInterceptor, Type } from '@nestjs/common';
|
|
2
|
+
import type { MultipartOptions } from '../interfaces/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Accepts up to `maxCount` files in `fieldName` and exposes them as an array
|
|
5
|
+
* in `req.files` (`@UploadedFiles()`). The Fastify counterpart of
|
|
6
|
+
* platform-express's `FilesInterceptor`.
|
|
7
|
+
*
|
|
8
|
+
* @param fieldName name of the form field that carries the files
|
|
9
|
+
* @param maxCount maximum number of files to accept
|
|
10
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
11
|
+
*
|
|
12
|
+
* @publicApi
|
|
13
|
+
*/
|
|
14
|
+
export declare function FilesInterceptor(fieldName: string, maxCount?: number, localOptions?: MultipartOptions): Type<NestInterceptor>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createMultipartInterceptor } from '../multipart/multipart-interceptor.factory.js';
|
|
2
|
+
/**
|
|
3
|
+
* Accepts up to `maxCount` files in `fieldName` and exposes them as an array
|
|
4
|
+
* in `req.files` (`@UploadedFiles()`). The Fastify counterpart of
|
|
5
|
+
* platform-express's `FilesInterceptor`.
|
|
6
|
+
*
|
|
7
|
+
* @param fieldName name of the form field that carries the files
|
|
8
|
+
* @param maxCount maximum number of files to accept
|
|
9
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
10
|
+
*
|
|
11
|
+
* @publicApi
|
|
12
|
+
*/
|
|
13
|
+
export function FilesInterceptor(fieldName, maxCount, localOptions) {
|
|
14
|
+
return createMultipartInterceptor({ fields: [{ name: fieldName, maxCount }], strategy: 'ARRAY' }, localOptions);
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NestInterceptor, Type } from '@nestjs/common';
|
|
2
|
+
import type { MultipartOptions } from '../interfaces/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Accepts text fields only (exposed in `req.body`); any file fails the
|
|
5
|
+
* request with a 400. The Fastify counterpart of platform-express's
|
|
6
|
+
* `NoFilesInterceptor`.
|
|
7
|
+
*
|
|
8
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
9
|
+
*
|
|
10
|
+
* @publicApi
|
|
11
|
+
*/
|
|
12
|
+
export declare function NoFilesInterceptor(localOptions?: MultipartOptions): Type<NestInterceptor>;
|