@mastra/express 1.4.11-alpha.0 → 1.4.11-alpha.2
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 +16 -0
- package/dist/index.cjs +569 -660
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +567 -657
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -1,666 +1,576 @@
|
|
|
1
|
-
import { Busboy } from
|
|
2
|
-
import { coreAuthMiddleware, findMatchingCustomRoute, isProtectedCustomRoute } from
|
|
3
|
-
import { MastraServer as MastraServer$1,
|
|
4
|
-
import { RequestContext } from
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return new globalThis.Request(url, {
|
|
21
|
-
method: req.method,
|
|
22
|
-
headers
|
|
23
|
-
});
|
|
1
|
+
import { Busboy } from "@fastify/busboy";
|
|
2
|
+
import { coreAuthMiddleware, findMatchingCustomRoute, isProtectedCustomRoute } from "@mastra/server/auth";
|
|
3
|
+
import { MastraServer as MastraServer$1, checkRouteFGA, isZodError, normalizeQueryParams, redactStreamChunk, serializeStreamChunk } from "@mastra/server/server-adapter";
|
|
4
|
+
import { RequestContext } from "@mastra/core/request-context";
|
|
5
|
+
//#region src/auth-middleware.ts
|
|
6
|
+
function toWebRequest$1(req) {
|
|
7
|
+
const url = `${req.protocol || "http"}://${req.get("host") || "localhost"}${req.originalUrl || req.url}`;
|
|
8
|
+
const headers = new Headers();
|
|
9
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
10
|
+
if (!value) continue;
|
|
11
|
+
if (Array.isArray(value)) value.forEach((v) => headers.append(key, v));
|
|
12
|
+
else headers.set(key, value);
|
|
13
|
+
}
|
|
14
|
+
return new globalThis.Request(url, {
|
|
15
|
+
method: req.method,
|
|
16
|
+
headers
|
|
17
|
+
});
|
|
24
18
|
}
|
|
25
|
-
function createAuthMiddleware({
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
next();
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
res.status(result.status).json(result.body);
|
|
68
|
-
};
|
|
19
|
+
function createAuthMiddleware({ mastra, requiresAuth = true }) {
|
|
20
|
+
return async (req, res, next) => {
|
|
21
|
+
if (!requiresAuth) {
|
|
22
|
+
next();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const authConfig = mastra.getServer()?.auth;
|
|
26
|
+
if (!authConfig) {
|
|
27
|
+
next();
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const requestContext = res.locals.requestContext ?? new RequestContext();
|
|
31
|
+
res.locals.requestContext = requestContext;
|
|
32
|
+
res.locals.mastra = res.locals.mastra ?? mastra;
|
|
33
|
+
const path = String(req.path || "/");
|
|
34
|
+
const method = String(req.method || "GET");
|
|
35
|
+
const customRouteAuthConfig = new Map(res.locals.customRouteAuthConfig ?? []);
|
|
36
|
+
customRouteAuthConfig.set(`${method}:${path}`, true);
|
|
37
|
+
const authHeader = req.headers.authorization;
|
|
38
|
+
let token = authHeader ? authHeader.replace("Bearer ", "") : null;
|
|
39
|
+
if (!token && req.query.apiKey) token = req.query.apiKey || null;
|
|
40
|
+
const result = await coreAuthMiddleware({
|
|
41
|
+
path,
|
|
42
|
+
method,
|
|
43
|
+
getHeader: (name) => req.headers[name.toLowerCase()],
|
|
44
|
+
mastra,
|
|
45
|
+
authConfig,
|
|
46
|
+
customRouteAuthConfig,
|
|
47
|
+
requestContext,
|
|
48
|
+
rawRequest: toWebRequest$1(req),
|
|
49
|
+
token,
|
|
50
|
+
buildAuthorizeContext: () => toWebRequest$1(req)
|
|
51
|
+
});
|
|
52
|
+
if (result.action === "next") {
|
|
53
|
+
next();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
res.status(result.status).json(result.body);
|
|
57
|
+
};
|
|
69
58
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/index.ts
|
|
61
|
+
let _hasPermissionPromise;
|
|
73
62
|
function loadHasPermission() {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
);
|
|
79
|
-
return void 0;
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
return _hasPermissionPromise;
|
|
63
|
+
if (!_hasPermissionPromise) _hasPermissionPromise = import("@mastra/core/auth/ee").then((m) => m.hasPermission).catch(() => {
|
|
64
|
+
console.error("[@mastra/express] Auth features require @mastra/core >= 1.6.0. Please upgrade: npm install @mastra/core@latest");
|
|
65
|
+
});
|
|
66
|
+
return _hasPermissionPromise;
|
|
83
67
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return new globalThis.Request(url, {
|
|
99
|
-
method: req.method,
|
|
100
|
-
headers
|
|
101
|
-
});
|
|
68
|
+
/**
|
|
69
|
+
* Convert Express request to Web API Request for cookie-based auth providers.
|
|
70
|
+
*/
|
|
71
|
+
function toWebRequest(req) {
|
|
72
|
+
const url = `${req.protocol || "http"}://${req.get("host") || "localhost"}${req.originalUrl || req.url}`;
|
|
73
|
+
const headers = new Headers();
|
|
74
|
+
for (const [key, value] of Object.entries(req.headers)) if (value) if (Array.isArray(value)) value.forEach((v) => headers.append(key, v));
|
|
75
|
+
else headers.set(key, value);
|
|
76
|
+
return new globalThis.Request(url, {
|
|
77
|
+
method: req.method,
|
|
78
|
+
headers
|
|
79
|
+
});
|
|
102
80
|
}
|
|
103
81
|
var MastraServer = class extends MastraServer$1 {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
const fgaError = await checkRouteFGA(this.mastra, serverRoute, res.locals.requestContext, {
|
|
597
|
-
...matchedRoute?.params ?? {},
|
|
598
|
-
...req.query,
|
|
599
|
-
...typeof req.body === "object" && req.body !== null ? req.body : {}
|
|
600
|
-
});
|
|
601
|
-
if (fgaError) {
|
|
602
|
-
return res.status(fgaError.status).json({ error: fgaError.error, message: fgaError.message });
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
const response = await this.handleCustomRouteRequest(
|
|
606
|
-
`${req.protocol}://${req.get("host") || "localhost"}${req.originalUrl}`,
|
|
607
|
-
req.method,
|
|
608
|
-
req.headers,
|
|
609
|
-
req.body,
|
|
610
|
-
res.locals.requestContext,
|
|
611
|
-
res.locals.abortSignal
|
|
612
|
-
);
|
|
613
|
-
if (!response) return next();
|
|
614
|
-
await this.writeCustomRouteResponse(response, res, res.locals.abortSignal);
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
registerContextMiddleware() {
|
|
618
|
-
this.app.use(this.createContextMiddleware());
|
|
619
|
-
}
|
|
620
|
-
registerAuthMiddleware() {
|
|
621
|
-
}
|
|
622
|
-
registerHttpLoggingMiddleware() {
|
|
623
|
-
if (!this.httpLoggingConfig?.enabled) {
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
this.app.use((req, res, next) => {
|
|
627
|
-
if (!this.shouldLogRequest(req.path)) {
|
|
628
|
-
return next();
|
|
629
|
-
}
|
|
630
|
-
const start = Date.now();
|
|
631
|
-
const method = req.method;
|
|
632
|
-
const path = req.path;
|
|
633
|
-
res.on("finish", () => {
|
|
634
|
-
const duration = Date.now() - start;
|
|
635
|
-
const status = res.statusCode;
|
|
636
|
-
const level = this.httpLoggingConfig?.level || "info";
|
|
637
|
-
const logData = {
|
|
638
|
-
method,
|
|
639
|
-
path,
|
|
640
|
-
status,
|
|
641
|
-
duration: `${duration}ms`
|
|
642
|
-
};
|
|
643
|
-
if (this.httpLoggingConfig?.includeQueryParams) {
|
|
644
|
-
logData.query = req.query;
|
|
645
|
-
}
|
|
646
|
-
if (this.httpLoggingConfig?.includeHeaders) {
|
|
647
|
-
const headers = { ...req.headers };
|
|
648
|
-
const redactHeaders = this.httpLoggingConfig.redactHeaders || [];
|
|
649
|
-
redactHeaders.forEach((h) => {
|
|
650
|
-
const key = h.toLowerCase();
|
|
651
|
-
if (headers[key] !== void 0) {
|
|
652
|
-
headers[key] = "[REDACTED]";
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
logData.headers = headers;
|
|
656
|
-
}
|
|
657
|
-
this.logger[level](`${method} ${path} ${status} ${duration}ms`, logData);
|
|
658
|
-
});
|
|
659
|
-
next();
|
|
660
|
-
});
|
|
661
|
-
}
|
|
82
|
+
createContextMiddleware() {
|
|
83
|
+
return async (req, res, next) => {
|
|
84
|
+
let bodyRequestContext;
|
|
85
|
+
let paramsRequestContext;
|
|
86
|
+
if (req.method === "POST" || req.method === "PUT") {
|
|
87
|
+
if (req.headers["content-type"]?.includes("application/json") && req.body) {
|
|
88
|
+
if (req.body.requestContext) bodyRequestContext = req.body.requestContext;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (req.method === "GET") try {
|
|
92
|
+
const encodedRequestContext = req.query.requestContext;
|
|
93
|
+
if (typeof encodedRequestContext === "string") try {
|
|
94
|
+
paramsRequestContext = JSON.parse(encodedRequestContext);
|
|
95
|
+
} catch {
|
|
96
|
+
try {
|
|
97
|
+
const json = Buffer.from(encodedRequestContext, "base64").toString("utf-8");
|
|
98
|
+
paramsRequestContext = JSON.parse(json);
|
|
99
|
+
} catch {}
|
|
100
|
+
}
|
|
101
|
+
} catch {}
|
|
102
|
+
const requestContext = this.mergeRequestContext({
|
|
103
|
+
paramsRequestContext,
|
|
104
|
+
bodyRequestContext
|
|
105
|
+
});
|
|
106
|
+
this.applyRequestMetadataToContext({
|
|
107
|
+
requestContext,
|
|
108
|
+
getHeader: (name) => req.get(name)
|
|
109
|
+
});
|
|
110
|
+
res.locals.requestContext = requestContext;
|
|
111
|
+
res.locals.mastra = this.mastra;
|
|
112
|
+
res.locals.registeredTools = this.tools || {};
|
|
113
|
+
if (this.taskStore) res.locals.taskStore = this.taskStore;
|
|
114
|
+
res.locals.customRouteAuthConfig = this.customRouteAuthConfig;
|
|
115
|
+
const controller = new AbortController();
|
|
116
|
+
res.on("close", () => {
|
|
117
|
+
if (!res.writableFinished) controller.abort();
|
|
118
|
+
});
|
|
119
|
+
res.locals.abortSignal = controller.signal;
|
|
120
|
+
next();
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
async stream(route, res, result) {
|
|
124
|
+
const streamFormat = route.streamFormat || "stream";
|
|
125
|
+
if (streamFormat === "sse") {
|
|
126
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
127
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
128
|
+
res.setHeader("Connection", "keep-alive");
|
|
129
|
+
res.setHeader("X-Accel-Buffering", "no");
|
|
130
|
+
} else res.setHeader("Content-Type", "text/plain");
|
|
131
|
+
res.setHeader("Transfer-Encoding", "chunked");
|
|
132
|
+
res.flushHeaders();
|
|
133
|
+
if (streamFormat === "sse" && route.sseFlushOnConnect) res.write(": connected\n\n");
|
|
134
|
+
const reader = (result instanceof ReadableStream ? result : result.fullStream).getReader();
|
|
135
|
+
try {
|
|
136
|
+
while (true) {
|
|
137
|
+
const { done, value } = await reader.read();
|
|
138
|
+
if (done) break;
|
|
139
|
+
if (value) {
|
|
140
|
+
if (streamFormat === "sse" && typeof value === "string" && value.startsWith(":")) {
|
|
141
|
+
res.write(value);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const outputValue = this.streamOptions?.redact ?? true ? redactStreamChunk(value) : value;
|
|
145
|
+
const serialized = serializeStreamChunk(outputValue);
|
|
146
|
+
if (!serialized.ok) {
|
|
147
|
+
this.mastra.getLogger()?.error("Failed to serialize stream chunk, skipping", {
|
|
148
|
+
path: route.path,
|
|
149
|
+
chunkType: outputValue?.type,
|
|
150
|
+
error: serialized.error.message
|
|
151
|
+
});
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (streamFormat === "sse") res.write(`data: ${serialized.json}\n\n`);
|
|
155
|
+
else res.write(serialized.json + "");
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
} catch (error) {
|
|
159
|
+
this.mastra.getLogger()?.error("Error in stream processing", { error: error instanceof Error ? {
|
|
160
|
+
message: error.message,
|
|
161
|
+
stack: error.stack
|
|
162
|
+
} : error });
|
|
163
|
+
} finally {
|
|
164
|
+
res.end();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async getParams(route, request) {
|
|
168
|
+
const urlParams = request.params;
|
|
169
|
+
const queryParams = normalizeQueryParams(request.query);
|
|
170
|
+
let body;
|
|
171
|
+
let bodyParseError;
|
|
172
|
+
if (route.method === "POST" || route.method === "PUT" || route.method === "PATCH" || route.method === "DELETE") if ((request.headers["content-type"] || "").includes("multipart/form-data")) try {
|
|
173
|
+
const maxFileSize = route.maxBodySize ?? this.bodyLimitOptions?.maxSize;
|
|
174
|
+
body = await this.parseMultipartFormData(request, maxFileSize);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
this.mastra.getLogger()?.error("Failed to parse multipart form data", { error: error instanceof Error ? {
|
|
177
|
+
message: error.message,
|
|
178
|
+
stack: error.stack
|
|
179
|
+
} : error });
|
|
180
|
+
if (error instanceof Error && error.message.toLowerCase().includes("size")) throw error;
|
|
181
|
+
bodyParseError = { message: error instanceof Error ? error.message : "Failed to parse multipart form data" };
|
|
182
|
+
}
|
|
183
|
+
else body = request.body;
|
|
184
|
+
return {
|
|
185
|
+
urlParams,
|
|
186
|
+
queryParams,
|
|
187
|
+
body,
|
|
188
|
+
bodyParseError
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Parse multipart/form-data using @fastify/busboy.
|
|
193
|
+
* Converts file uploads to Buffers and parses JSON field values.
|
|
194
|
+
*
|
|
195
|
+
* @param request - The Express request object
|
|
196
|
+
* @param maxFileSize - Optional maximum file size in bytes
|
|
197
|
+
*/
|
|
198
|
+
parseMultipartFormData(request, maxFileSize) {
|
|
199
|
+
return new Promise((resolve, reject) => {
|
|
200
|
+
const result = {};
|
|
201
|
+
const busboy = new Busboy({
|
|
202
|
+
headers: { "content-type": request.headers["content-type"] },
|
|
203
|
+
limits: maxFileSize ? { fileSize: maxFileSize } : void 0
|
|
204
|
+
});
|
|
205
|
+
busboy.on("file", (fieldname, file) => {
|
|
206
|
+
const chunks = [];
|
|
207
|
+
let limitExceeded = false;
|
|
208
|
+
file.on("data", (chunk) => {
|
|
209
|
+
chunks.push(chunk);
|
|
210
|
+
});
|
|
211
|
+
file.on("limit", () => {
|
|
212
|
+
limitExceeded = true;
|
|
213
|
+
reject(/* @__PURE__ */ new Error(`File size limit exceeded${maxFileSize ? ` (max: ${maxFileSize} bytes)` : ""}`));
|
|
214
|
+
});
|
|
215
|
+
file.on("end", () => {
|
|
216
|
+
if (!limitExceeded) result[fieldname] = Buffer.concat(chunks);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
busboy.on("field", (fieldname, value) => {
|
|
220
|
+
try {
|
|
221
|
+
result[fieldname] = JSON.parse(value);
|
|
222
|
+
} catch {
|
|
223
|
+
result[fieldname] = value;
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
busboy.on("finish", () => {
|
|
227
|
+
resolve(result);
|
|
228
|
+
});
|
|
229
|
+
busboy.on("error", (error) => {
|
|
230
|
+
reject(error);
|
|
231
|
+
});
|
|
232
|
+
request.pipe(busboy);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
async sendResponse(route, response, result, request, prefix) {
|
|
236
|
+
const resolvedPrefix = prefix ?? this.prefix ?? "";
|
|
237
|
+
if (result && typeof result === "object" && "__refreshHeaders" in result) {
|
|
238
|
+
const refreshHeaders = result.__refreshHeaders;
|
|
239
|
+
for (const [key, value] of Object.entries(refreshHeaders)) response.setHeader(key, value);
|
|
240
|
+
delete result.__refreshHeaders;
|
|
241
|
+
}
|
|
242
|
+
if (route.responseType === "json") response.json(result);
|
|
243
|
+
else if (route.responseType === "stream") await this.stream(route, response, result);
|
|
244
|
+
else if (route.responseType === "datastream-response") {
|
|
245
|
+
const fetchResponse = result;
|
|
246
|
+
fetchResponse.headers.forEach((value, key) => response.setHeader(key, value));
|
|
247
|
+
response.status(fetchResponse.status);
|
|
248
|
+
if (fetchResponse.body) {
|
|
249
|
+
const reader = fetchResponse.body.getReader();
|
|
250
|
+
const onResError = (err) => {
|
|
251
|
+
this.mastra.getLogger()?.error("Error writing datastream response", { error: err instanceof Error ? {
|
|
252
|
+
message: err.message,
|
|
253
|
+
stack: err.stack
|
|
254
|
+
} : err });
|
|
255
|
+
reader.cancel("response write error");
|
|
256
|
+
};
|
|
257
|
+
response.once("error", onResError);
|
|
258
|
+
try {
|
|
259
|
+
while (true) {
|
|
260
|
+
const { done, value } = await reader.read();
|
|
261
|
+
if (done) break;
|
|
262
|
+
response.write(value);
|
|
263
|
+
}
|
|
264
|
+
} catch (error) {
|
|
265
|
+
this.mastra.getLogger()?.error("Error in datastream processing", { error: error instanceof Error ? {
|
|
266
|
+
message: error.message,
|
|
267
|
+
stack: error.stack
|
|
268
|
+
} : error });
|
|
269
|
+
} finally {
|
|
270
|
+
response.off("error", onResError);
|
|
271
|
+
response.end();
|
|
272
|
+
}
|
|
273
|
+
} else response.end();
|
|
274
|
+
} else if (route.responseType === "mcp-http") {
|
|
275
|
+
if (!request) {
|
|
276
|
+
response.status(500).json({ error: "Request object required for MCP transport" });
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const { server, httpPath, mcpOptions: routeMcpOptions } = result;
|
|
280
|
+
try {
|
|
281
|
+
const options = {
|
|
282
|
+
...this.mcpOptions,
|
|
283
|
+
...routeMcpOptions
|
|
284
|
+
};
|
|
285
|
+
await server.startHTTP({
|
|
286
|
+
url: new URL(request.url, `http://${request.headers.host}`),
|
|
287
|
+
httpPath: `${resolvedPrefix}${httpPath}`,
|
|
288
|
+
req: request,
|
|
289
|
+
res: response,
|
|
290
|
+
options: Object.keys(options).length > 0 ? options : void 0
|
|
291
|
+
});
|
|
292
|
+
} catch {
|
|
293
|
+
if (!response.headersSent) response.status(500).json({
|
|
294
|
+
jsonrpc: "2.0",
|
|
295
|
+
error: {
|
|
296
|
+
code: -32603,
|
|
297
|
+
message: "Internal server error"
|
|
298
|
+
},
|
|
299
|
+
id: null
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
} else if (route.responseType === "mcp-sse") {
|
|
303
|
+
if (!request) {
|
|
304
|
+
response.status(500).json({ error: "Request object required for MCP transport" });
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const { server, ssePath, messagePath } = result;
|
|
308
|
+
try {
|
|
309
|
+
await server.startSSE({
|
|
310
|
+
url: new URL(request.url, `http://${request.headers.host}`),
|
|
311
|
+
ssePath: `${resolvedPrefix}${ssePath}`,
|
|
312
|
+
messagePath: `${resolvedPrefix}${messagePath}`,
|
|
313
|
+
req: request,
|
|
314
|
+
res: response
|
|
315
|
+
});
|
|
316
|
+
} catch {
|
|
317
|
+
if (!response.headersSent) response.status(500).json({ error: "Error handling MCP SSE request" });
|
|
318
|
+
}
|
|
319
|
+
} else response.sendStatus(500);
|
|
320
|
+
}
|
|
321
|
+
async registerRoute(app, route, { prefix: prefixParam } = {}) {
|
|
322
|
+
const prefix = prefixParam ?? this.prefix ?? "";
|
|
323
|
+
const shouldApplyBodyLimit = this.bodyLimitOptions && [
|
|
324
|
+
"POST",
|
|
325
|
+
"PUT",
|
|
326
|
+
"PATCH"
|
|
327
|
+
].includes(route.method.toUpperCase());
|
|
328
|
+
const maxSize = route.maxBodySize ?? this.bodyLimitOptions?.maxSize;
|
|
329
|
+
const middlewares = [];
|
|
330
|
+
if (shouldApplyBodyLimit && maxSize && this.bodyLimitOptions) {
|
|
331
|
+
const bodyLimitMiddleware = (req, res, next) => {
|
|
332
|
+
const contentLength = req.headers["content-length"];
|
|
333
|
+
if (contentLength && parseInt(contentLength, 10) > maxSize) try {
|
|
334
|
+
const errorResponse = this.bodyLimitOptions.onError({ error: "Request body too large" });
|
|
335
|
+
return res.status(413).json(errorResponse);
|
|
336
|
+
} catch {
|
|
337
|
+
return res.status(413).json({ error: "Request body too large" });
|
|
338
|
+
}
|
|
339
|
+
next();
|
|
340
|
+
};
|
|
341
|
+
middlewares.push(bodyLimitMiddleware);
|
|
342
|
+
}
|
|
343
|
+
app[route.method.toLowerCase()](`${prefix}${route.path}`, ...middlewares, async (req, res) => {
|
|
344
|
+
const authError = await this.checkRouteAuth(route, {
|
|
345
|
+
path: String(req.path || "/"),
|
|
346
|
+
method: String(req.method || "GET"),
|
|
347
|
+
getHeader: (name) => req.headers[name.toLowerCase()],
|
|
348
|
+
getQuery: (name) => req.query[name],
|
|
349
|
+
requestContext: res.locals.requestContext,
|
|
350
|
+
request: toWebRequest(req),
|
|
351
|
+
buildAuthorizeContext: () => toWebRequest(req)
|
|
352
|
+
});
|
|
353
|
+
if (authError) {
|
|
354
|
+
const authResult = authError;
|
|
355
|
+
if (authResult.headers) for (const [key, value] of Object.entries(authResult.headers)) res.setHeader(key, value);
|
|
356
|
+
if (authResult.error) return res.status(authResult.status).json({ error: authResult.error });
|
|
357
|
+
}
|
|
358
|
+
const params = await this.getParams(route, req);
|
|
359
|
+
if (params.bodyParseError) return res.status(400).json({
|
|
360
|
+
error: "Invalid request body",
|
|
361
|
+
issues: [{
|
|
362
|
+
field: "body",
|
|
363
|
+
message: params.bodyParseError.message
|
|
364
|
+
}]
|
|
365
|
+
});
|
|
366
|
+
if (params.queryParams) try {
|
|
367
|
+
params.queryParams = await this.parseQueryParams(route, params.queryParams);
|
|
368
|
+
} catch (error) {
|
|
369
|
+
this.mastra.getLogger()?.error("Error parsing query params", { error: error instanceof Error ? {
|
|
370
|
+
message: error.message,
|
|
371
|
+
stack: error.stack
|
|
372
|
+
} : error });
|
|
373
|
+
if (isZodError(error)) {
|
|
374
|
+
const { status, body } = this.resolveValidationError(route, error, "query");
|
|
375
|
+
return res.status(status).json(body);
|
|
376
|
+
}
|
|
377
|
+
return res.status(400).json({
|
|
378
|
+
error: "Invalid query parameters",
|
|
379
|
+
issues: [{
|
|
380
|
+
field: "unknown",
|
|
381
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
382
|
+
}]
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
if (params.body) try {
|
|
386
|
+
params.body = await this.parseBody(route, params.body);
|
|
387
|
+
} catch (error) {
|
|
388
|
+
this.mastra.getLogger()?.error("Error parsing body", { error: error instanceof Error ? {
|
|
389
|
+
message: error.message,
|
|
390
|
+
stack: error.stack
|
|
391
|
+
} : error });
|
|
392
|
+
if (isZodError(error)) {
|
|
393
|
+
const { status, body } = this.resolveValidationError(route, error, "body");
|
|
394
|
+
return res.status(status).json(body);
|
|
395
|
+
}
|
|
396
|
+
return res.status(400).json({
|
|
397
|
+
error: "Invalid request body",
|
|
398
|
+
issues: [{
|
|
399
|
+
field: "unknown",
|
|
400
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
401
|
+
}]
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
if (params.urlParams) try {
|
|
405
|
+
params.urlParams = await this.parsePathParams(route, params.urlParams);
|
|
406
|
+
} catch (error) {
|
|
407
|
+
this.mastra.getLogger()?.error("Error parsing path params", { error: error instanceof Error ? {
|
|
408
|
+
message: error.message,
|
|
409
|
+
stack: error.stack
|
|
410
|
+
} : error });
|
|
411
|
+
if (isZodError(error)) {
|
|
412
|
+
const { status, body } = this.resolveValidationError(route, error, "path");
|
|
413
|
+
return res.status(status).json(body);
|
|
414
|
+
}
|
|
415
|
+
return res.status(400).json({
|
|
416
|
+
error: "Invalid path parameters",
|
|
417
|
+
issues: [{
|
|
418
|
+
field: "unknown",
|
|
419
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
420
|
+
}]
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
const handlerParams = {
|
|
424
|
+
...params.urlParams,
|
|
425
|
+
...params.queryParams,
|
|
426
|
+
...typeof params.body === "object" ? params.body : {},
|
|
427
|
+
requestContext: res.locals.requestContext,
|
|
428
|
+
mastra: this.mastra,
|
|
429
|
+
registeredTools: res.locals.registeredTools,
|
|
430
|
+
taskStore: res.locals.taskStore,
|
|
431
|
+
abortSignal: res.locals.abortSignal,
|
|
432
|
+
routePrefix: prefix,
|
|
433
|
+
request: toWebRequest(req)
|
|
434
|
+
};
|
|
435
|
+
if (this.mastra.getServer()?.auth) {
|
|
436
|
+
const hasPermission = await loadHasPermission();
|
|
437
|
+
if (hasPermission) {
|
|
438
|
+
const userPermissions = res.locals.requestContext.get("mastra__userPermissions");
|
|
439
|
+
const permissionError = this.checkRoutePermission(route, userPermissions, hasPermission);
|
|
440
|
+
if (permissionError) return res.status(permissionError.status).json({
|
|
441
|
+
error: permissionError.error,
|
|
442
|
+
message: permissionError.message
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const fgaError = await checkRouteFGA(this.mastra, route, res.locals.requestContext, {
|
|
447
|
+
...params.urlParams,
|
|
448
|
+
...params.queryParams,
|
|
449
|
+
...typeof params.body === "object" ? params.body : {}
|
|
450
|
+
});
|
|
451
|
+
if (fgaError) return res.status(fgaError.status).json({
|
|
452
|
+
error: fgaError.error,
|
|
453
|
+
message: fgaError.message
|
|
454
|
+
});
|
|
455
|
+
try {
|
|
456
|
+
const result = await route.handler(handlerParams);
|
|
457
|
+
await this.sendResponse(route, res, result, req, prefix);
|
|
458
|
+
} catch (error) {
|
|
459
|
+
const httpStatus = error && typeof error === "object" && "status" in error ? error.status : void 0;
|
|
460
|
+
if (!(typeof httpStatus === "number" && httpStatus >= 400 && httpStatus < 500)) this.mastra.getLogger()?.error("Error calling handler", {
|
|
461
|
+
error: error instanceof Error ? {
|
|
462
|
+
message: error.message,
|
|
463
|
+
stack: error.stack
|
|
464
|
+
} : error,
|
|
465
|
+
path: route.path,
|
|
466
|
+
method: route.method
|
|
467
|
+
});
|
|
468
|
+
let status = 500;
|
|
469
|
+
if (error && typeof error === "object") {
|
|
470
|
+
if ("status" in error) status = error.status;
|
|
471
|
+
else if ("details" in error && error.details && typeof error.details === "object" && "status" in error.details) status = error.details.status;
|
|
472
|
+
}
|
|
473
|
+
res.status(status).json({ error: error instanceof Error ? error.message : "Unknown error" });
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
async registerCustomApiRoutes() {
|
|
478
|
+
if (!await this.buildCustomRouteHandler()) return;
|
|
479
|
+
this.app.use(async (req, res, next) => {
|
|
480
|
+
const path = String(req.path || "/");
|
|
481
|
+
const method = String(req.method || "GET");
|
|
482
|
+
const matchedRoute = findMatchingCustomRoute(path, method, this.customApiRoutes ?? this.mastra.getServer()?.apiRoutes);
|
|
483
|
+
const shouldRunCustomRouteAuth = isProtectedCustomRoute(path, method, this.customRouteAuthConfig);
|
|
484
|
+
const shouldRunCustomRouteFGA = !!matchedRoute?.route.fga;
|
|
485
|
+
if (shouldRunCustomRouteAuth || shouldRunCustomRouteFGA) {
|
|
486
|
+
const serverRoute = {
|
|
487
|
+
method: matchedRoute?.route.method ?? method,
|
|
488
|
+
path: matchedRoute?.route.path ?? path,
|
|
489
|
+
responseType: "json",
|
|
490
|
+
handler: async () => {},
|
|
491
|
+
requiresAuth: matchedRoute?.route.requiresAuth,
|
|
492
|
+
requiresPermission: matchedRoute?.route.requiresPermission,
|
|
493
|
+
fga: matchedRoute?.route.fga
|
|
494
|
+
};
|
|
495
|
+
if (shouldRunCustomRouteAuth) {
|
|
496
|
+
const authError = await this.checkRouteAuth(serverRoute, {
|
|
497
|
+
path,
|
|
498
|
+
method,
|
|
499
|
+
getHeader: (name) => req.headers[name.toLowerCase()],
|
|
500
|
+
getQuery: (name) => req.query[name],
|
|
501
|
+
requestContext: res.locals.requestContext,
|
|
502
|
+
request: toWebRequest(req),
|
|
503
|
+
buildAuthorizeContext: () => toWebRequest(req)
|
|
504
|
+
});
|
|
505
|
+
if (authError) {
|
|
506
|
+
const authResult = authError;
|
|
507
|
+
if (authResult.headers) for (const [key, value] of Object.entries(authResult.headers)) res.setHeader(key, value);
|
|
508
|
+
if (authResult.error) return res.status(authResult.status).json({ error: authResult.error });
|
|
509
|
+
}
|
|
510
|
+
if (this.mastra.getServer()?.auth) {
|
|
511
|
+
const hasPermission = await loadHasPermission();
|
|
512
|
+
if (hasPermission) {
|
|
513
|
+
const userPermissions = res.locals.requestContext.get("mastra__userPermissions");
|
|
514
|
+
const permissionError = this.checkRoutePermission(serverRoute, userPermissions, hasPermission);
|
|
515
|
+
if (permissionError) return res.status(permissionError.status).json({
|
|
516
|
+
error: permissionError.error,
|
|
517
|
+
message: permissionError.message
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
const fgaError = await checkRouteFGA(this.mastra, serverRoute, res.locals.requestContext, {
|
|
523
|
+
...matchedRoute?.params ?? {},
|
|
524
|
+
...req.query,
|
|
525
|
+
...typeof req.body === "object" && req.body !== null ? req.body : {}
|
|
526
|
+
});
|
|
527
|
+
if (fgaError) return res.status(fgaError.status).json({
|
|
528
|
+
error: fgaError.error,
|
|
529
|
+
message: fgaError.message
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
const response = await this.handleCustomRouteRequest(`${req.protocol}://${req.get("host") || "localhost"}${req.originalUrl}`, req.method, req.headers, req.body, res.locals.requestContext, res.locals.abortSignal);
|
|
533
|
+
if (!response) return next();
|
|
534
|
+
await this.writeCustomRouteResponse(response, res, res.locals.abortSignal);
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
registerContextMiddleware() {
|
|
538
|
+
this.app.use(this.createContextMiddleware());
|
|
539
|
+
}
|
|
540
|
+
registerAuthMiddleware() {}
|
|
541
|
+
registerHttpLoggingMiddleware() {
|
|
542
|
+
if (!this.httpLoggingConfig?.enabled) return;
|
|
543
|
+
this.app.use((req, res, next) => {
|
|
544
|
+
if (!this.shouldLogRequest(req.path)) return next();
|
|
545
|
+
const start = Date.now();
|
|
546
|
+
const method = req.method;
|
|
547
|
+
const path = req.path;
|
|
548
|
+
res.on("finish", () => {
|
|
549
|
+
const duration = Date.now() - start;
|
|
550
|
+
const status = res.statusCode;
|
|
551
|
+
const level = this.httpLoggingConfig?.level || "info";
|
|
552
|
+
const logData = {
|
|
553
|
+
method,
|
|
554
|
+
path,
|
|
555
|
+
status,
|
|
556
|
+
duration: `${duration}ms`
|
|
557
|
+
};
|
|
558
|
+
if (this.httpLoggingConfig?.includeQueryParams) logData.query = req.query;
|
|
559
|
+
if (this.httpLoggingConfig?.includeHeaders) {
|
|
560
|
+
const headers = { ...req.headers };
|
|
561
|
+
(this.httpLoggingConfig.redactHeaders || []).forEach((h) => {
|
|
562
|
+
const key = h.toLowerCase();
|
|
563
|
+
if (headers[key] !== void 0) headers[key] = "[REDACTED]";
|
|
564
|
+
});
|
|
565
|
+
logData.headers = headers;
|
|
566
|
+
}
|
|
567
|
+
this.logger[level](`${method} ${path} ${status} ${duration}ms`, logData);
|
|
568
|
+
});
|
|
569
|
+
next();
|
|
570
|
+
});
|
|
571
|
+
}
|
|
662
572
|
};
|
|
663
|
-
|
|
573
|
+
//#endregion
|
|
664
574
|
export { MastraServer, createAuthMiddleware };
|
|
665
|
-
|
|
575
|
+
|
|
666
576
|
//# sourceMappingURL=index.js.map
|