@mastra/koa 1.2.3 → 1.2.4

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/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Busboy } from '@fastify/busboy';
2
+ import { isProtectedCustomRoute } from '@mastra/server/auth';
2
3
  import { formatZodError } from '@mastra/server/handlers/error';
3
4
  import { MastraServer as MastraServer$1, redactStreamChunk, normalizeQueryParams } from '@mastra/server/server-adapter';
4
- import { isDevPlaygroundRequest, isProtectedPath, canAccessPublicly, checkRules, defaultAuthConfig } from '@mastra/server/auth';
5
5
 
6
6
  // src/index.ts
7
7
 
@@ -211,148 +211,39 @@ ZodError.create = (issues) => {
211
211
  const error = new ZodError(issues);
212
212
  return error;
213
213
  };
214
- var authenticationMiddleware = async (ctx, next) => {
215
- const mastra = ctx.state.mastra;
216
- const authConfig = mastra.getServer()?.auth;
217
- const customRouteAuthConfig = ctx.state.customRouteAuthConfig;
218
- if (!authConfig) {
219
- return next();
220
- }
221
- const path = String(ctx.path || "/");
222
- const method = String(ctx.method || "GET");
223
- const getHeader = (name) => ctx.headers[name.toLowerCase()];
224
- if (isDevPlaygroundRequest(path, method, getHeader, authConfig, customRouteAuthConfig)) {
225
- return next();
226
- }
227
- if (!isProtectedPath(path, method, authConfig, customRouteAuthConfig)) {
228
- return next();
229
- }
230
- if (canAccessPublicly(path, method, authConfig)) {
231
- return next();
232
- }
233
- const authHeader = ctx.headers.authorization;
234
- let token = authHeader ? authHeader.replace("Bearer ", "") : null;
235
- const query = ctx.query;
236
- if (!token && query.apiKey) {
237
- token = query.apiKey || null;
238
- }
239
- if (!token) {
240
- ctx.status = 401;
241
- ctx.body = { error: "Authentication required" };
242
- return;
243
- }
244
- try {
245
- let user;
246
- if (typeof authConfig.authenticateToken === "function") {
247
- user = await authConfig.authenticateToken(token, ctx.request);
248
- } else {
249
- throw new Error("No token verification method configured");
250
- }
251
- if (!user) {
252
- ctx.status = 401;
253
- ctx.body = { error: "Invalid or expired token" };
254
- return;
255
- }
256
- ctx.state.requestContext.set("user", user);
257
- return next();
258
- } catch (err) {
259
- mastra.getLogger()?.error("Authentication error", {
260
- error: err instanceof Error ? { message: err.message, stack: err.stack } : err
214
+
215
+ // src/index.ts
216
+ var _hasPermissionPromise;
217
+ function loadHasPermission() {
218
+ if (!_hasPermissionPromise) {
219
+ _hasPermissionPromise = import('@mastra/core/auth/ee').then((m) => m.hasPermission).catch(() => {
220
+ console.error(
221
+ "[@mastra/koa] Auth features require @mastra/core >= 1.6.0. Please upgrade: npm install @mastra/core@latest"
222
+ );
223
+ return void 0;
261
224
  });
262
- ctx.status = 401;
263
- ctx.body = { error: "Invalid or expired token" };
264
- return;
265
- }
266
- };
267
- var authorizationMiddleware = async (ctx, next) => {
268
- const mastra = ctx.state.mastra;
269
- const authConfig = mastra.getServer()?.auth;
270
- const customRouteAuthConfig = ctx.state.customRouteAuthConfig;
271
- if (!authConfig) {
272
- return next();
273
- }
274
- const path = String(ctx.path || "/");
275
- const method = String(ctx.method || "GET");
276
- const getHeader = (name) => ctx.headers[name.toLowerCase()];
277
- if (isDevPlaygroundRequest(path, method, getHeader, authConfig, customRouteAuthConfig)) {
278
- return next();
279
- }
280
- if (!isProtectedPath(path, method, authConfig, customRouteAuthConfig)) {
281
- return next();
282
- }
283
- if (canAccessPublicly(path, method, authConfig)) {
284
- return next();
285
225
  }
286
- const user = ctx.state.requestContext.get("user");
287
- if ("authorizeUser" in authConfig && typeof authConfig.authorizeUser === "function") {
288
- try {
289
- const isAuthorized = await authConfig.authorizeUser(user, ctx.request);
290
- if (isAuthorized) {
291
- return next();
292
- }
293
- ctx.status = 403;
294
- ctx.body = { error: "Access denied" };
295
- return;
296
- } catch (err) {
297
- mastra.getLogger()?.error("Authorization error in authorizeUser", {
298
- error: err instanceof Error ? { message: err.message, stack: err.stack } : err
299
- });
300
- ctx.status = 500;
301
- ctx.body = { error: "Authorization error" };
302
- return;
303
- }
304
- }
305
- if ("authorize" in authConfig && typeof authConfig.authorize === "function") {
306
- try {
307
- const context = {
308
- get: (key) => {
309
- if (key === "mastra") return ctx.state.mastra;
310
- if (key === "requestContext") return ctx.state.requestContext;
311
- if (key === "tools") return ctx.state.tools;
312
- if (key === "taskStore") return ctx.state.taskStore;
313
- if (key === "customRouteAuthConfig") return ctx.state.customRouteAuthConfig;
314
- return void 0;
315
- },
316
- req: ctx.request
317
- };
318
- const isAuthorized = await authConfig.authorize(path, method, user, context);
319
- if (isAuthorized) {
320
- return next();
226
+ return _hasPermissionPromise;
227
+ }
228
+ function toWebRequest(ctx) {
229
+ const protocol = ctx.protocol || "http";
230
+ const host = ctx.host || "localhost";
231
+ const url = `${protocol}://${host}${ctx.url}`;
232
+ const headers = new Headers();
233
+ for (const [key, value] of Object.entries(ctx.headers)) {
234
+ if (value) {
235
+ if (Array.isArray(value)) {
236
+ value.forEach((v) => headers.append(key, v));
237
+ } else {
238
+ headers.set(key, value);
321
239
  }
322
- ctx.status = 403;
323
- ctx.body = { error: "Access denied" };
324
- return;
325
- } catch (err) {
326
- mastra.getLogger()?.error("Authorization error in authorize", {
327
- error: err instanceof Error ? { message: err.message, stack: err.stack } : err,
328
- path,
329
- method
330
- });
331
- ctx.status = 500;
332
- ctx.body = { error: "Authorization error" };
333
- return;
334
- }
335
- }
336
- if ("rules" in authConfig && authConfig.rules && authConfig.rules.length > 0) {
337
- const isAuthorized = await checkRules(authConfig.rules, path, method, user);
338
- if (isAuthorized) {
339
- return next();
340
- }
341
- ctx.status = 403;
342
- ctx.body = { error: "Access denied" };
343
- return;
344
- }
345
- if (defaultAuthConfig.rules && defaultAuthConfig.rules.length > 0) {
346
- const isAuthorized = await checkRules(defaultAuthConfig.rules, path, method, user);
347
- if (isAuthorized) {
348
- return next();
349
240
  }
350
241
  }
351
- ctx.status = 403;
352
- ctx.body = { error: "Access denied" };
353
- };
354
-
355
- // src/index.ts
242
+ return new globalThis.Request(url, {
243
+ method: ctx.method,
244
+ headers
245
+ });
246
+ }
356
247
  var MastraServer = class extends MastraServer$1 {
357
248
  async init() {
358
249
  this.registerErrorMiddleware();
@@ -610,7 +501,8 @@ var MastraServer = class extends MastraServer$1 {
610
501
  async sendResponse(route, ctx, result, prefix) {
611
502
  const resolvedPrefix = prefix ?? this.prefix ?? "";
612
503
  if (route.responseType === "json") {
613
- ctx.body = result;
504
+ ctx.type = "application/json";
505
+ ctx.body = result === null || result === void 0 ? JSON.stringify(null) : result;
614
506
  } else if (route.responseType === "stream") {
615
507
  await this.stream(route, ctx, result);
616
508
  } else if (route.responseType === "datastream-response") {
@@ -713,7 +605,9 @@ var MastraServer = class extends MastraServer$1 {
713
605
  method: String(ctx.method || "GET"),
714
606
  getHeader: (name) => ctx.headers[name.toLowerCase()],
715
607
  getQuery: (name) => ctx.query[name],
716
- requestContext: ctx.state.requestContext
608
+ requestContext: ctx.state.requestContext,
609
+ request: toWebRequest(ctx),
610
+ buildAuthorizeContext: () => toWebRequest(ctx)
717
611
  });
718
612
  if (authError) {
719
613
  ctx.status = authError.status;
@@ -800,6 +694,22 @@ var MastraServer = class extends MastraServer$1 {
800
694
  abortSignal: ctx.state.abortSignal,
801
695
  routePrefix: prefix
802
696
  };
697
+ const authConfig = this.mastra.getServer()?.auth;
698
+ if (authConfig) {
699
+ const hasPermission = await loadHasPermission();
700
+ if (hasPermission) {
701
+ const userPermissions = ctx.state.requestContext.get("userPermissions");
702
+ const permissionError = this.checkRoutePermission(route, userPermissions, hasPermission);
703
+ if (permissionError) {
704
+ ctx.status = permissionError.status;
705
+ ctx.body = {
706
+ error: permissionError.error,
707
+ message: permissionError.message
708
+ };
709
+ return;
710
+ }
711
+ }
712
+ }
803
713
  try {
804
714
  const result = await route.handler(handlerParams);
805
715
  await this.sendResponse(route, ctx, result, prefix);
@@ -844,6 +754,54 @@ var MastraServer = class extends MastraServer$1 {
844
754
  async registerCustomApiRoutes() {
845
755
  if (!await this.buildCustomRouteHandler()) return;
846
756
  this.app.use(async (ctx, next) => {
757
+ const path = String(ctx.path || "/");
758
+ const method = String(ctx.method || "GET");
759
+ if (isProtectedCustomRoute(path, method, this.customRouteAuthConfig)) {
760
+ const serverRoute = {
761
+ method,
762
+ path,
763
+ responseType: "json",
764
+ handler: async () => {
765
+ }
766
+ };
767
+ const authError = await this.checkRouteAuth(serverRoute, {
768
+ path,
769
+ method,
770
+ getHeader: (name) => ctx.headers[name.toLowerCase()],
771
+ getQuery: (name) => ctx.query[name],
772
+ requestContext: ctx.state.requestContext,
773
+ request: toWebRequest(ctx),
774
+ buildAuthorizeContext: () => toWebRequest(ctx)
775
+ });
776
+ if (authError) {
777
+ ctx.status = authError.status;
778
+ ctx.body = { error: authError.error };
779
+ return;
780
+ }
781
+ const authConfig = this.mastra.getServer()?.auth;
782
+ if (authConfig) {
783
+ let hasPermission;
784
+ try {
785
+ ({ hasPermission } = await import('@mastra/core/auth/ee'));
786
+ } catch {
787
+ console.error(
788
+ "[@mastra/koa] Auth features require @mastra/core >= 1.6.0. Please upgrade: npm install @mastra/core@latest"
789
+ );
790
+ }
791
+ if (hasPermission) {
792
+ const userPermissions = ctx.state.requestContext.get("userPermissions");
793
+ const permissionError = this.checkRoutePermission(serverRoute, userPermissions, hasPermission);
794
+ if (permissionError) {
795
+ ctx.status = permissionError.status;
796
+ ctx.body = {
797
+ error: permissionError.error,
798
+ message: permissionError.message
799
+ };
800
+ return;
801
+ }
802
+ }
803
+ }
804
+ }
847
805
  const response = await this.handleCustomRouteRequest(
848
806
  `${ctx.protocol}://${ctx.host}${ctx.originalUrl || ctx.url}`,
849
807
  ctx.method,
@@ -860,12 +818,44 @@ var MastraServer = class extends MastraServer$1 {
860
818
  this.app.use(this.createContextMiddleware());
861
819
  }
862
820
  registerAuthMiddleware() {
863
- const authConfig = this.mastra.getServer()?.auth;
864
- if (!authConfig) {
821
+ }
822
+ registerHttpLoggingMiddleware() {
823
+ if (!this.httpLoggingConfig?.enabled) {
865
824
  return;
866
825
  }
867
- this.app.use(authenticationMiddleware);
868
- this.app.use(authorizationMiddleware);
826
+ this.app.use(async (ctx, next) => {
827
+ if (!this.shouldLogRequest(ctx.path)) {
828
+ return next();
829
+ }
830
+ const start = Date.now();
831
+ const method = ctx.method;
832
+ const path = ctx.path;
833
+ await next();
834
+ const duration = Date.now() - start;
835
+ const status = ctx.status;
836
+ const level = this.httpLoggingConfig?.level || "info";
837
+ const logData = {
838
+ method,
839
+ path,
840
+ status,
841
+ duration: `${duration}ms`
842
+ };
843
+ if (this.httpLoggingConfig?.includeQueryParams) {
844
+ logData.query = ctx.query;
845
+ }
846
+ if (this.httpLoggingConfig?.includeHeaders) {
847
+ const headers = { ...ctx.headers };
848
+ const redactHeaders = this.httpLoggingConfig.redactHeaders || [];
849
+ redactHeaders.forEach((h) => {
850
+ const key = h.toLowerCase();
851
+ if (headers[key] !== void 0) {
852
+ headers[key] = "[REDACTED]";
853
+ }
854
+ });
855
+ logData.headers = headers;
856
+ }
857
+ this.logger[level](`${method} ${path} ${status} ${duration}ms`, logData);
858
+ });
869
859
  }
870
860
  };
871
861