adminjs-nestjs icon indicating copy to clipboard operation
adminjs-nestjs copied to clipboard

Broken NestJs middleware

Open jgr3go opened this issue 3 years ago • 4 comments

Using NestJs Middleware Consumers is broken with this module. I'm trying to globally add middleware to the admin module with something like:

@Module({
   imports: [
     // whatever
     AdminModule.createAdmin({ 
       adminBroOptions: {
         rootPath: 'admin',
         // etc
       }
     })
  ]
})
export class AppModule implements NestModule {
  confgure(consumer: MiddlewareConsumer) {
     consumer.apply(passport.authenticate('jwt').forRoutes({path: 'admin*', method: RequestMethod.ALL});
  }
}

But the middleware is never executed. I'm reasonably sure this is due to the middleware reordering in express.loader.ts because when I comment it out, the middleware is executed as expected.

jgr3go avatar Jun 13 '21 18:06 jgr3go

anyone? @wojtek-krysiak ?

burgalon avatar Jul 12 '21 14:07 burgalon

My solution for now is to initialize in App.module.js with:

    AdminModule.createAdminAsync({
      customLoader: CustomAdminJsCustomLoader,
      useFactory: () => ({
        adminJsOptions: {
          rootPath: '/admin',
        },
      }),
    }),

and then in CustomAdminJsCustomLoader copy ExpressLoader without the reordering

burgalon avatar Jul 12 '21 14:07 burgalon

My solution for now is to initialize in App.module.js with:

    AdminModule.createAdminAsync({
      customLoader: CustomAdminJsCustomLoader,
      useFactory: () => ({
        adminJsOptions: {
          rootPath: '/admin',
        },
      }),
    }),

and then in CustomAdminJsCustomLoader copy ExpressLoader without the reordering

Hi,

I am looking for a similar solution but as soon as I did that this error happened:

{"message":"\n You probably used old body-parser middleware, which is not compatible\n with @adminjs/express. In order to make it work you will have to\n 1. move body-parser invocation after the AdminJS setup like this:\n \n const adminJs = new AdminJS()\n const router = new buildRouter(adminJs)\n app.use(adminJs.options.rootPath, router)\n \n // body parser goes after the AdminJS router\n app.use(bodyParser())\n \n 2. Upgrade body-parser to the latest version and use it like this:\n app.use(bodyParser.json())\n "}

I was looking for a way to add a middleware for mitigating session fixation...

Any inputs of how I can get add a middleware to the adminjs nestjs setup ?

siddisking avatar Dec 05 '22 12:12 siddisking

Here's a modified version that inserts adminjs after any middleware applied to the rootPath. I have not tested it much but it seems to be working with nestjs applied middleware.

private reorderRoutes(app, rootPath: string) {
    let jsonParser = [];
    let urlencodedParser = [];
    let admin = [];

    // Nestjs uses bodyParser under the hood which is in conflict with adminjs setup.
    // Due to adminjs-expressjs usage of formidable we have to move body parser in layer tree after adminjs init.
    // Notice! This is not documented feature of express, so this may change in the future. We have to keep an eye on it.
    if (app && app._router && app._router.stack) {
      const jsonParserIndex = app._router.stack.findIndex(
        (layer: { name: string }) => layer.name === 'jsonParser',
      );
      if (jsonParserIndex >= 0) {
        jsonParser = app._router.stack.splice(jsonParserIndex, 1);
      }

      const urlencodedParserIndex = app._router.stack.findIndex(
        (layer: { name: string }) => layer.name === 'urlencodedParser',
      );
      if (urlencodedParserIndex >= 0) {
        urlencodedParser = app._router.stack.splice(urlencodedParserIndex, 1);
      }

      const adminIndex = app._router.stack.findIndex(
        (layer: { name: string }) => layer.name === 'admin',
      );
      if (adminIndex >= 0) {
        admin = app._router.stack.splice(adminIndex, 1);
      }

      // if adminjs-nestjs didn't reorder the middleware
      // the body parser would have come after corsMiddleware
      const corsIndex = app._router.stack.findIndex(
        (layer: { name: string }) => layer.name === 'corsMiddleware',
      );

      // in other case if there is no corsIndex we go after expressInit, because right after that
      // there are nest endpoints.
      const expressInitIndex = app._router.stack.findIndex(
        (layer: { name: string }) => layer.name === 'expressInit',
      );

      // in case there are other middleware on the rootPath
      // the adminjs should go after those
      let rootPathMiddlewareIndex = -1;

      const matches = app._router.stack
        .map((s, i) => {
          return { stackIndex: i, ...s };
        })
        .filter((s: { regexp: RegExp; name: string }) => {
          if (['query', 'corsMiddleware', 'expressInit'].includes(s.name)) {
            return false;
          }
          return s.regexp.test(rootPath);
        });

      if (matches.length > 0) {
        rootPathMiddlewareIndex = matches[0].stackIndex + matches.length;
      }

      const initIndex =
        rootPathMiddlewareIndex >= 0
          ? rootPathMiddlewareIndex
          : (corsIndex >= 0 ? corsIndex : expressInitIndex) + 1;

      app._router.stack.splice(
        initIndex,
        0,
        ...admin,
        ...jsonParser,
        ...urlencodedParser,
      );
    }
  }

rckrdstrgrd avatar Jan 19 '24 13:01 rckrdstrgrd