diff --git a/src/app.module.ts b/src/app.module.ts index 09a820ed17..a05d479b94 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -44,6 +44,7 @@ import { RelayControllerModule } from '@/routes/relay/relay.controller.module'; import { SubscriptionControllerModule } from '@/routes/subscriptions/subscription.module'; import { LockingModule } from '@/routes/locking/locking.module'; import { ZodErrorFilter } from '@/routes/common/filters/zod-error.filter'; +import { CacheControlInterceptor } from '@/routes/common/interceptors/cache-control.interceptor'; @Module({}) export class AppModule implements NestModule { @@ -116,6 +117,10 @@ export class AppModule implements NestModule { provide: APP_INTERCEPTOR, useClass: RouteLoggerInterceptor, }, + { + provide: APP_INTERCEPTOR, + useClass: CacheControlInterceptor, + }, { provide: APP_FILTER, useClass: GlobalErrorFilter, diff --git a/src/routes/common/interceptors/cache-control.interceptor.spec.ts b/src/routes/common/interceptors/cache-control.interceptor.spec.ts new file mode 100644 index 0000000000..09cdfa3ce3 --- /dev/null +++ b/src/routes/common/interceptors/cache-control.interceptor.spec.ts @@ -0,0 +1,37 @@ +import { + Controller, + Get, + INestApplication, + UseInterceptors, +} from '@nestjs/common'; +import { CacheControlInterceptor } from '@/routes/common/interceptors/cache-control.interceptor'; +import { Test } from '@nestjs/testing'; +import * as request from 'supertest'; + +@Controller() +@UseInterceptors(CacheControlInterceptor) +class TestController { + @Get() + test(): void { + return; + } +} + +describe('CacheControlInterceptor tests', () => { + let app: INestApplication; + + beforeEach(async () => { + const module = await Test.createTestingModule({ + controllers: [TestController], + }).compile(); + + app = module.createNestApplication(); + await app.init(); + }); + + it('should set the Cache-Control header to no-cache', () => { + return request(app.getHttpServer()) + .get('/') + .expect('Cache-Control', 'no-cache'); + }); +}); diff --git a/src/routes/common/interceptors/cache-control.interceptor.ts b/src/routes/common/interceptors/cache-control.interceptor.ts new file mode 100644 index 0000000000..641c4d7c7a --- /dev/null +++ b/src/routes/common/interceptors/cache-control.interceptor.ts @@ -0,0 +1,25 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, +} from '@nestjs/common'; +import { Observable, tap } from 'rxjs'; + +/** + * This interceptor can be used to set the `Cache-Control` header to `no-cache`. + */ +@Injectable() +export class CacheControlInterceptor implements NestInterceptor { + intercept( + context: ExecutionContext, + next: CallHandler, + ): Observable | Promise> { + return next.handle().pipe( + tap(() => { + const response = context.switchToHttp().getResponse(); + response.header('Cache-Control', 'no-cache'); + }), + ); + } +} diff --git a/src/routes/relay/relay.legacy.controller.ts b/src/routes/relay/relay.legacy.controller.ts index 5fdcab50e9..463cd45127 100644 --- a/src/routes/relay/relay.legacy.controller.ts +++ b/src/routes/relay/relay.legacy.controller.ts @@ -1,15 +1,14 @@ import { RelayLegacyDto } from '@/routes/relay/entities/relay.legacy.dto.entity'; import { RelayLegacyDtoValidationPipe } from '@/routes/relay/pipes/relay.legacy.validation.pipe'; import { + Body, Controller, - Post, Get, HttpStatus, - Res, Param, - Body, + Post, + Redirect, } from '@nestjs/common'; -import { Response } from 'express'; @Controller({ version: '1', @@ -17,26 +16,20 @@ import { Response } from 'express'; }) export class RelayLegacyController { @Post() + @Redirect(undefined, HttpStatus.PERMANENT_REDIRECT) relay( @Body(RelayLegacyDtoValidationPipe) relayLegacyDto: RelayLegacyDto, - @Res() res: Response, - ): void { - res.redirect( - HttpStatus.PERMANENT_REDIRECT, - `/v1/chains/${relayLegacyDto.chainId}/relay`, - ); + ): { url: string } { + return { url: `/v1/chains/${relayLegacyDto.chainId}/relay` }; } @Get('/:chainId/:safeAddress') + @Redirect(undefined, HttpStatus.MOVED_PERMANENTLY) getRelaysRemaining( @Param('chainId') chainId: string, @Param('safeAddress') safeAddress: string, - @Res() res: Response, - ): void { - res.redirect( - HttpStatus.MOVED_PERMANENTLY, - `/v1/chains/${chainId}/relay/${safeAddress}`, - ); + ): { url: string } { + return { url: `/v1/chains/${chainId}/relay/${safeAddress}` }; } }