@lenne.tech/nest-server 11.9.0 → 11.10.1
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/dist/config.env.js +2 -0
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/helpers/logging.helper.d.ts +6 -0
- package/dist/core/common/helpers/logging.helper.js +55 -0
- package/dist/core/common/helpers/logging.helper.js.map +1 -0
- package/dist/core/common/interfaces/server-options.interface.d.ts +37 -19
- package/dist/core/modules/auth/guards/roles.guard.js +33 -2
- package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
- package/dist/core/modules/auth/services/core-auth.service.d.ts +5 -5
- package/dist/core/modules/auth/services/core-auth.service.js +4 -4
- package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
- package/dist/core/modules/auth/tokens.decorator.d.ts +1 -1
- package/dist/core/modules/better-auth/better-auth.config.js +32 -10
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.resolver.d.ts +16 -16
- package/dist/core/modules/better-auth/better-auth.resolver.js +34 -34
- package/dist/core/modules/better-auth/better-auth.resolver.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.types.d.ts +2 -1
- package/dist/core/modules/better-auth/better-auth.types.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.d.ts +10 -0
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.js +91 -0
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-auth.model.d.ts +9 -0
- package/dist/core/modules/better-auth/{better-auth-auth.model.js → core-better-auth-auth.model.js} +17 -17
- package/dist/core/modules/better-auth/core-better-auth-auth.model.js.map +1 -0
- package/dist/core/modules/better-auth/{better-auth-migration-status.model.d.ts → core-better-auth-migration-status.model.d.ts} +1 -1
- package/dist/core/modules/better-auth/{better-auth-migration-status.model.js → core-better-auth-migration-status.model.js} +14 -14
- package/dist/core/modules/better-auth/core-better-auth-migration-status.model.js.map +1 -0
- package/dist/core/modules/better-auth/{better-auth-models.d.ts → core-better-auth-models.d.ts} +8 -8
- package/dist/core/modules/better-auth/{better-auth-models.js → core-better-auth-models.js} +61 -61
- package/dist/core/modules/better-auth/core-better-auth-models.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-rate-limit.middleware.d.ts +12 -0
- package/dist/core/modules/better-auth/{better-auth-rate-limit.middleware.js → core-better-auth-rate-limit.middleware.js} +10 -10
- package/dist/core/modules/better-auth/core-better-auth-rate-limit.middleware.js.map +1 -0
- package/dist/core/modules/better-auth/{better-auth-rate-limiter.service.d.ts → core-better-auth-rate-limiter.service.d.ts} +1 -1
- package/dist/core/modules/better-auth/{better-auth-rate-limiter.service.js → core-better-auth-rate-limiter.service.js} +8 -8
- package/dist/core/modules/better-auth/core-better-auth-rate-limiter.service.js.map +1 -0
- package/dist/core/modules/better-auth/{better-auth-user.mapper.d.ts → core-better-auth-user.mapper.d.ts} +1 -1
- package/dist/core/modules/better-auth/{better-auth-user.mapper.js → core-better-auth-user.mapper.js} +10 -9
- package/dist/core/modules/better-auth/core-better-auth-user.mapper.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-web.helper.d.ts +19 -0
- package/dist/core/modules/better-auth/core-better-auth-web.helper.js +152 -0
- package/dist/core/modules/better-auth/core-better-auth-web.helper.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth.controller.d.ts +23 -32
- package/dist/core/modules/better-auth/core-better-auth.controller.js +184 -201
- package/dist/core/modules/better-auth/core-better-auth.controller.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.middleware.d.ts +22 -0
- package/dist/core/modules/better-auth/{better-auth.middleware.js → core-better-auth.middleware.js} +45 -18
- package/dist/core/modules/better-auth/core-better-auth.middleware.js.map +1 -0
- package/dist/core/modules/better-auth/{better-auth.module.d.ts → core-better-auth.module.d.ts} +6 -6
- package/dist/core/modules/better-auth/{better-auth.module.js → core-better-auth.module.js} +65 -60
- package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth.resolver.d.ts +19 -19
- package/dist/core/modules/better-auth/core-better-auth.resolver.js +18 -18
- package/dist/core/modules/better-auth/core-better-auth.resolver.js.map +1 -1
- package/dist/core/modules/better-auth/{better-auth.service.d.ts → core-better-auth.service.d.ts} +3 -2
- package/dist/core/modules/better-auth/{better-auth.service.js → core-better-auth.service.js} +75 -35
- package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -0
- package/dist/core/modules/better-auth/index.d.ts +11 -9
- package/dist/core/modules/better-auth/index.js +11 -9
- package/dist/core/modules/better-auth/index.js.map +1 -1
- package/dist/core/modules/error-code/core-error-code.controller.js.map +1 -1
- package/dist/core/modules/user/interfaces/core-user-service-options.interface.d.ts +2 -2
- package/dist/core.module.js +6 -6
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/server/modules/better-auth/better-auth.controller.d.ts +5 -5
- package/dist/server/modules/better-auth/better-auth.controller.js +4 -4
- package/dist/server/modules/better-auth/better-auth.controller.js.map +1 -1
- package/dist/server/modules/better-auth/better-auth.module.js +3 -3
- package/dist/server/modules/better-auth/better-auth.module.js.map +1 -1
- package/dist/server/modules/better-auth/better-auth.resolver.d.ts +17 -17
- package/dist/server/modules/better-auth/better-auth.resolver.js +18 -18
- package/dist/server/modules/better-auth/better-auth.resolver.js.map +1 -1
- package/dist/server/modules/user/user.service.d.ts +2 -2
- package/dist/server/modules/user/user.service.js +2 -2
- package/dist/server/modules/user/user.service.js.map +1 -1
- package/dist/test/test.helper.d.ts +1 -0
- package/dist/test/test.helper.js +5 -1
- package/dist/test/test.helper.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +5 -3
- package/src/config.env.ts +15 -0
- package/src/core/common/helpers/logging.helper.ts +134 -0
- package/src/core/common/interfaces/server-options.interface.ts +419 -234
- package/src/core/modules/auth/guards/roles.guard.ts +44 -3
- package/src/core/modules/auth/services/core-auth.service.ts +4 -4
- package/src/core/modules/better-auth/ARCHITECTURE.md +102 -0
- package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +277 -8
- package/src/core/modules/better-auth/README.md +97 -53
- package/src/core/modules/better-auth/better-auth.config.ts +66 -18
- package/src/core/modules/better-auth/better-auth.resolver.ts +32 -32
- package/src/core/modules/better-auth/better-auth.types.ts +3 -2
- package/src/core/modules/better-auth/core-better-auth-api.middleware.ts +134 -0
- package/src/core/modules/better-auth/{better-auth-auth.model.ts → core-better-auth-auth.model.ts} +6 -6
- package/src/core/modules/better-auth/{better-auth-migration-status.model.ts → core-better-auth-migration-status.model.ts} +1 -1
- package/src/core/modules/better-auth/{better-auth-models.ts → core-better-auth-models.ts} +9 -9
- package/src/core/modules/better-auth/{better-auth-rate-limit.middleware.ts → core-better-auth-rate-limit.middleware.ts} +5 -5
- package/src/core/modules/better-auth/{better-auth-rate-limiter.service.ts → core-better-auth-rate-limiter.service.ts} +2 -2
- package/src/core/modules/better-auth/{better-auth-user.mapper.ts → core-better-auth-user.mapper.ts} +4 -3
- package/src/core/modules/better-auth/core-better-auth-web.helper.ts +272 -0
- package/src/core/modules/better-auth/core-better-auth.controller.ts +386 -230
- package/src/core/modules/better-auth/{better-auth.middleware.ts → core-better-auth.middleware.ts} +57 -17
- package/src/core/modules/better-auth/{better-auth.module.ts → core-better-auth.module.ts} +77 -66
- package/src/core/modules/better-auth/core-better-auth.resolver.ts +42 -42
- package/src/core/modules/better-auth/{better-auth.service.ts → core-better-auth.service.ts} +86 -40
- package/src/core/modules/better-auth/index.ts +18 -11
- package/src/core/modules/error-code/INTEGRATION-CHECKLIST.md +4 -1
- package/src/core/modules/error-code/core-error-code.controller.ts +3 -2
- package/src/core/modules/user/interfaces/core-user-service-options.interface.ts +3 -3
- package/src/core.module.ts +12 -12
- package/src/index.ts +1 -0
- package/src/server/modules/better-auth/better-auth.controller.ts +4 -4
- package/src/server/modules/better-auth/better-auth.module.ts +1 -1
- package/src/server/modules/better-auth/better-auth.resolver.ts +31 -31
- package/src/server/modules/user/user.service.ts +2 -2
- package/src/test/test.helper.ts +13 -1
- package/dist/core/modules/better-auth/better-auth-auth.model.d.ts +0 -9
- package/dist/core/modules/better-auth/better-auth-auth.model.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth-migration-status.model.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth-models.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.d.ts +0 -12
- package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth-rate-limiter.service.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth-user.mapper.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth.middleware.d.ts +0 -21
- package/dist/core/modules/better-auth/better-auth.middleware.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth.module.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth.service.js.map +0 -1
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { Logger } from '@nestjs/common';
|
|
2
|
+
import * as crypto from 'crypto';
|
|
3
|
+
import { Request, Response } from 'express';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Cookie names used by Better Auth and nest-server
|
|
7
|
+
*/
|
|
8
|
+
export const BETTER_AUTH_COOKIE_NAMES = {
|
|
9
|
+
/** Better Auth's default session token cookie */
|
|
10
|
+
BETTER_AUTH_SESSION: 'better-auth.session_token',
|
|
11
|
+
/** Legacy nest-server token cookie */
|
|
12
|
+
TOKEN: 'token',
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Maximum body size for streaming requests (1MB).
|
|
17
|
+
* Prevents DoS attacks via large request bodies.
|
|
18
|
+
*/
|
|
19
|
+
export const MAX_BODY_SIZE = 1024 * 1024;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Options for converting Express Request to Web Standard Request
|
|
23
|
+
*/
|
|
24
|
+
export interface ToWebRequestOptions {
|
|
25
|
+
/** Base path for cookie names (e.g., 'iam') */
|
|
26
|
+
basePath?: string;
|
|
27
|
+
/** Base URL for constructing the full URL */
|
|
28
|
+
baseUrl: string;
|
|
29
|
+
/** Logger instance for debug output */
|
|
30
|
+
logger?: Logger;
|
|
31
|
+
/** Secret for signing cookies (if provided, cookies will be signed) */
|
|
32
|
+
secret?: string;
|
|
33
|
+
/** Optional session token to inject into headers */
|
|
34
|
+
sessionToken?: null | string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Extracts the session token from Express request cookies or Authorization header.
|
|
39
|
+
*
|
|
40
|
+
* Checks multiple cookie names for compatibility with different configurations:
|
|
41
|
+
* 1. `{basePath}.session_token` - Based on configured basePath (e.g., iam.session_token)
|
|
42
|
+
* 2. `better-auth.session_token` - Better Auth default
|
|
43
|
+
* 3. `token` - Legacy nest-server cookie
|
|
44
|
+
* 4. Authorization: Bearer header
|
|
45
|
+
*
|
|
46
|
+
* @param req - Express request
|
|
47
|
+
* @param basePath - Base path for cookie names (e.g., '/iam' or 'iam')
|
|
48
|
+
* @returns Session token or null if not found
|
|
49
|
+
*/
|
|
50
|
+
export function extractSessionToken(req: Request, basePath: string = 'iam'): null | string {
|
|
51
|
+
// Check Authorization header first
|
|
52
|
+
const authHeader = req.headers.authorization;
|
|
53
|
+
if (authHeader?.startsWith('Bearer ')) {
|
|
54
|
+
return authHeader.substring(7);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Normalize basePath (remove leading slash, replace slashes with dots)
|
|
58
|
+
const normalizedBasePath = basePath.replace(/^\//, '').replace(/\//g, '.');
|
|
59
|
+
|
|
60
|
+
// Cookie names to check (in order of priority)
|
|
61
|
+
const cookieNames = [
|
|
62
|
+
`${normalizedBasePath}.session_token`, // Based on configured basePath
|
|
63
|
+
BETTER_AUTH_COOKIE_NAMES.BETTER_AUTH_SESSION, // Better Auth default
|
|
64
|
+
BETTER_AUTH_COOKIE_NAMES.TOKEN, // Legacy nest-server cookie
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
// Try to get cookies from req.cookies (parsed by cookie-parser) or from header
|
|
68
|
+
const cookies = (req as any).cookies || parseCookieHeader(req.headers.cookie);
|
|
69
|
+
|
|
70
|
+
for (const name of cookieNames) {
|
|
71
|
+
const token = cookies?.[name];
|
|
72
|
+
if (token && typeof token === 'string') {
|
|
73
|
+
return token;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Parses a Cookie header string into an object.
|
|
82
|
+
*
|
|
83
|
+
* @param cookieHeader - The Cookie header string
|
|
84
|
+
* @returns Object mapping cookie names to values
|
|
85
|
+
*/
|
|
86
|
+
export function parseCookieHeader(cookieHeader: string | undefined): Record<string, string> {
|
|
87
|
+
if (!cookieHeader) {
|
|
88
|
+
return {};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const cookies: Record<string, string> = {};
|
|
92
|
+
const pairs = cookieHeader.split(';');
|
|
93
|
+
|
|
94
|
+
for (const pair of pairs) {
|
|
95
|
+
const [name, ...valueParts] = pair.trim().split('=');
|
|
96
|
+
if (name && valueParts.length > 0) {
|
|
97
|
+
cookies[name.trim()] = valueParts.join('=').trim();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return cookies;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Sends a Web Standard Response as an Express response.
|
|
106
|
+
*
|
|
107
|
+
* This converts the Fetch API Response object back to Express format,
|
|
108
|
+
* preserving headers, status code, and body.
|
|
109
|
+
*
|
|
110
|
+
* @param res - Express response
|
|
111
|
+
* @param webResponse - Web Standard Response
|
|
112
|
+
*/
|
|
113
|
+
export async function sendWebResponse(res: Response, webResponse: globalThis.Response): Promise<void> {
|
|
114
|
+
// Set status code
|
|
115
|
+
res.status(webResponse.status);
|
|
116
|
+
|
|
117
|
+
// Copy headers
|
|
118
|
+
webResponse.headers.forEach((value, key) => {
|
|
119
|
+
// Skip certain headers that Express handles differently
|
|
120
|
+
const lowerKey = key.toLowerCase();
|
|
121
|
+
if (lowerKey === 'content-encoding' || lowerKey === 'transfer-encoding') {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
res.setHeader(key, value);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Send body
|
|
128
|
+
if (webResponse.body) {
|
|
129
|
+
const reader = webResponse.body.getReader();
|
|
130
|
+
try {
|
|
131
|
+
while (true) {
|
|
132
|
+
const { done, value } = await reader.read();
|
|
133
|
+
if (done) break;
|
|
134
|
+
res.write(value);
|
|
135
|
+
}
|
|
136
|
+
} finally {
|
|
137
|
+
reader.releaseLock();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
res.end();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Signs a cookie value using HMAC-SHA256.
|
|
146
|
+
*
|
|
147
|
+
* Better Auth expects signed cookies in the format: `${value}.${signature}`
|
|
148
|
+
* where the signature is a base64-encoded HMAC-SHA256 hash of the value.
|
|
149
|
+
*
|
|
150
|
+
* @param value - The raw cookie value to sign
|
|
151
|
+
* @param secret - The secret to use for signing
|
|
152
|
+
* @returns The signed cookie value (URL-encoded)
|
|
153
|
+
* @throws Error if secret is not provided
|
|
154
|
+
*/
|
|
155
|
+
export function signCookieValue(value: string, secret: string): string {
|
|
156
|
+
if (!secret) {
|
|
157
|
+
throw new Error('Cannot sign cookie: Better Auth secret is not configured');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const signature = crypto
|
|
161
|
+
.createHmac('sha256', secret)
|
|
162
|
+
.update(value)
|
|
163
|
+
.digest('base64');
|
|
164
|
+
const signedValue = `${value}.${signature}`;
|
|
165
|
+
return encodeURIComponent(signedValue);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Converts an Express Request to a Web Standard Request.
|
|
170
|
+
*
|
|
171
|
+
* Better Auth uses the Fetch API's Request/Response objects internally.
|
|
172
|
+
* This method converts the Express request (including already-parsed body)
|
|
173
|
+
* to a Web Standard Request that Better Auth can process.
|
|
174
|
+
*
|
|
175
|
+
* @param req - Express request
|
|
176
|
+
* @param options - Conversion options
|
|
177
|
+
* @returns Web Standard Request
|
|
178
|
+
*/
|
|
179
|
+
export async function toWebRequest(req: Request, options: ToWebRequestOptions): Promise<globalThis.Request> {
|
|
180
|
+
const { basePath, baseUrl, logger, secret, sessionToken } = options;
|
|
181
|
+
const url = new URL(req.originalUrl || req.url, baseUrl);
|
|
182
|
+
|
|
183
|
+
// Build headers
|
|
184
|
+
const headers = new Headers();
|
|
185
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
186
|
+
if (typeof value === 'string') {
|
|
187
|
+
headers.set(key, value);
|
|
188
|
+
} else if (Array.isArray(value)) {
|
|
189
|
+
headers.set(key, value.join(', '));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Inject session token into Authorization header if provided
|
|
194
|
+
// This helps Better Auth find the session via bearer token lookup
|
|
195
|
+
if (sessionToken) {
|
|
196
|
+
headers.set('authorization', `Bearer ${sessionToken}`);
|
|
197
|
+
|
|
198
|
+
// Also ensure the session token is in the cookies with PROPER SIGNING
|
|
199
|
+
// IMPORTANT: We must REPLACE unsigned cookies with signed ones, not just add if missing
|
|
200
|
+
const normalizedBasePath = basePath?.replace(/^\//, '').replace(/\//g, '.') || 'iam';
|
|
201
|
+
const existingCookieString = headers.get('cookie') || '';
|
|
202
|
+
|
|
203
|
+
// Sign the session token for Better Auth (if secret is provided)
|
|
204
|
+
let signedToken: string;
|
|
205
|
+
if (secret) {
|
|
206
|
+
signedToken = signCookieValue(sessionToken, secret);
|
|
207
|
+
} else {
|
|
208
|
+
logger?.warn('No Better Auth secret configured - cookies will not be signed');
|
|
209
|
+
signedToken = sessionToken;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Cookie names that need signed tokens
|
|
213
|
+
const primaryCookieName = `${normalizedBasePath}.session_token`;
|
|
214
|
+
const sessionCookieNames = [
|
|
215
|
+
primaryCookieName,
|
|
216
|
+
BETTER_AUTH_COOKIE_NAMES.BETTER_AUTH_SESSION,
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
// Parse existing cookies
|
|
220
|
+
const existingCookies = parseCookieHeader(existingCookieString);
|
|
221
|
+
|
|
222
|
+
// Replace session token cookies with signed versions
|
|
223
|
+
for (const cookieName of sessionCookieNames) {
|
|
224
|
+
existingCookies[cookieName] = signedToken;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Keep the unsigned token cookie for nest-server compatibility
|
|
228
|
+
if (!existingCookies[BETTER_AUTH_COOKIE_NAMES.TOKEN]) {
|
|
229
|
+
existingCookies[BETTER_AUTH_COOKIE_NAMES.TOKEN] = sessionToken;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Rebuild the cookie string
|
|
233
|
+
const newCookieString = Object.entries(existingCookies)
|
|
234
|
+
.map(([name, value]) => `${name}=${value}`)
|
|
235
|
+
.join('; ');
|
|
236
|
+
|
|
237
|
+
headers.set('cookie', newCookieString);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Build request options
|
|
241
|
+
const init: RequestInit = {
|
|
242
|
+
headers,
|
|
243
|
+
method: req.method,
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// Handle body for non-GET/HEAD requests
|
|
247
|
+
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
248
|
+
// Check if body was already parsed by NestJS/Express
|
|
249
|
+
if (req.body && typeof req.body === 'object' && Object.keys(req.body).length > 0) {
|
|
250
|
+
// Body was parsed - reconstruct it as JSON
|
|
251
|
+
init.body = JSON.stringify(req.body);
|
|
252
|
+
headers.set('content-type', 'application/json');
|
|
253
|
+
} else if (req.readable) {
|
|
254
|
+
// Body wasn't parsed - stream it directly (ideal case)
|
|
255
|
+
// This happens when the middleware runs before body-parser
|
|
256
|
+
const chunks: Buffer[] = [];
|
|
257
|
+
let totalSize = 0;
|
|
258
|
+
for await (const chunk of req) {
|
|
259
|
+
totalSize += chunk.length;
|
|
260
|
+
if (totalSize > MAX_BODY_SIZE) {
|
|
261
|
+
throw new Error(`Request body too large (max ${MAX_BODY_SIZE} bytes)`);
|
|
262
|
+
}
|
|
263
|
+
chunks.push(chunk);
|
|
264
|
+
}
|
|
265
|
+
if (chunks.length > 0) {
|
|
266
|
+
init.body = Buffer.concat(chunks);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return new globalThis.Request(url.toString(), init);
|
|
272
|
+
}
|