diff --git a/apps/backend/package.json b/apps/backend/package.json index 6050691..34adc21 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -50,6 +50,7 @@ "@bull-board/nestjs": "^5.9.1", "@golevelup/nestjs-stripe": "^0.6.3", "@nestjs/bullmq": "^10.0.1", + "@nestjs/cache-manager": "^2.1.1", "@nestjs/common": "^10.2.7", "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.2.7", @@ -60,6 +61,8 @@ "@superfluid-finance/widget": "^0.4.7", "@types/lodash": "^4.14.200", "bullmq": "^4.12.6", + "cache-manager": "^5.2.4", + "cache-manager-redis-store": "^3.0.1", "date-fns": "^2.30.0", "lodash": "^4.17.21", "openapi-fetch": "^0.8.1", diff --git a/apps/backend/pnpm-lock.yaml b/apps/backend/pnpm-lock.yaml index b85cbe7..2c7142b 100644 --- a/apps/backend/pnpm-lock.yaml +++ b/apps/backend/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '6.1' settings: autoInstallPeers: true @@ -20,6 +20,9 @@ dependencies: '@nestjs/bullmq': specifier: ^10.0.1 version: 10.0.1(@nestjs/common@10.2.7)(@nestjs/core@10.2.7)(bullmq@4.12.6) + '@nestjs/cache-manager': + specifier: ^2.1.1 + version: 2.1.1(@nestjs/common@10.2.7)(@nestjs/core@10.2.7)(cache-manager@5.2.4)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/common': specifier: ^10.2.7 version: 10.2.7(reflect-metadata@0.1.13)(rxjs@7.8.1) @@ -50,6 +53,12 @@ dependencies: bullmq: specifier: ^4.12.6 version: 4.12.6 + cache-manager: + specifier: ^5.2.4 + version: 5.2.4 + cache-manager-redis-store: + specifier: ^3.0.1 + version: 3.0.1 date-fns: specifier: ^2.30.0 version: 2.30.0 @@ -1655,6 +1664,22 @@ packages: tslib: 2.6.0 dev: false + /@nestjs/cache-manager@2.1.1(@nestjs/common@10.2.7)(@nestjs/core@10.2.7)(cache-manager@5.2.4)(reflect-metadata@0.1.13)(rxjs@7.8.1): + resolution: {integrity: sha512-oYfRys4Ng0zp2HTUPNjH7gizf4vvG3PQZZ+3yGemb3xrF+p3JxDSK0cDq9NTjHzD5UmhjiyAftB9GkuL+t3r9g==} + peerDependencies: + '@nestjs/common': ^9.0.0 || ^10.0.0 + '@nestjs/core': ^9.0.0 || ^10.0.0 + cache-manager: <=5 + reflect-metadata: ^0.1.12 + rxjs: ^7.0.0 + dependencies: + '@nestjs/common': 10.2.7(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.7(@nestjs/common@10.2.7)(@nestjs/platform-express@10.2.7)(reflect-metadata@0.1.13)(rxjs@7.8.1) + cache-manager: 5.2.4 + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + dev: false + /@nestjs/cli@10.2.0(@swc/cli@0.1.62)(@swc/core@1.3.95): resolution: {integrity: sha512-OMbn6A/YNu7QSk1nM8VucrtUwocaa0XEa9uoqRpw5Acvh/KIetvMHCn8L6yIBxiK7yvYiKa8q9U+RpLsKpKfgw==} engines: {node: '>= 16.14'} @@ -1979,6 +2004,55 @@ packages: resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} dev: false + /@redis/bloom@1.2.0(@redis/client@1.5.11): + resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.5.11 + dev: false + + /@redis/client@1.5.11: + resolution: {integrity: sha512-cV7yHcOAtNQ5x/yQl7Yw1xf53kO0FNDTdDU6bFIMbW6ljB7U7ns0YRM+QIkpoqTAt6zK5k9Fq0QWlUbLcq9AvA==} + engines: {node: '>=14'} + dependencies: + cluster-key-slot: 1.1.2 + generic-pool: 3.9.0 + yallist: 4.0.0 + dev: false + + /@redis/graph@1.1.0(@redis/client@1.5.11): + resolution: {integrity: sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.5.11 + dev: false + + /@redis/json@1.0.6(@redis/client@1.5.11): + resolution: {integrity: sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.5.11 + dev: false + + /@redis/search@1.1.5(@redis/client@1.5.11): + resolution: {integrity: sha512-hPP8w7GfGsbtYEJdn4n7nXa6xt6hVZnnDktKW4ArMaFQ/m/aR7eFvsLQmG/mn1Upq99btPJk+F27IQ2dYpCoUg==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.5.11 + dev: false + + /@redis/time-series@1.0.5(@redis/client@1.5.11): + resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.5.11 + dev: false + /@safe-global/safe-apps-provider@0.17.1(typescript@5.2.2)(zod@3.22.4): resolution: {integrity: sha512-lYfRqrbbK1aKU1/UGkYWc/X7PgySYcumXKc5FB2uuwAs2Ghj8uETuW5BrwPqyjBknRxutFbTv+gth/JzjxAhdQ==} dependencies: @@ -4042,6 +4116,20 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + /cache-manager-redis-store@3.0.1: + resolution: {integrity: sha512-o560kw+dFqusC9lQJhcm6L2F2fMKobJ5af+FoR2PdnMVdpQ3f3Bz6qzvObTGyvoazQJxjQNWgMQeChP4vRTuXQ==} + engines: {node: '>= 16.18.0'} + dependencies: + redis: 4.6.10 + dev: false + + /cache-manager@5.2.4: + resolution: {integrity: sha512-gkuCjug16NdGvKm/sydxGVx17uffrSWcEe2xraBtwRCgdYcFxwJAla4OYpASAZT2yhSoxgDiWL9XH6IAChcZJA==} + dependencies: + lodash.clonedeep: 4.5.0 + lru-cache: 10.0.1 + dev: false + /cacheable-lookup@5.0.4: resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} engines: {node: '>=10.6.0'} @@ -5453,6 +5541,11 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true + /generic-pool@3.9.0: + resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} + engines: {node: '>= 4'} + dev: false + /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -6828,6 +6921,10 @@ packages: p-locate: 5.0.0 dev: true + /lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + dev: false + /lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} dev: false @@ -6877,7 +6974,6 @@ packages: /lru-cache@10.0.1: resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==} engines: {node: 14 || >=16.14} - dev: true /lru-cache@4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} @@ -7910,6 +8006,17 @@ packages: redis-errors: 1.2.0 dev: false + /redis@4.6.10: + resolution: {integrity: sha512-mmbyhuKgDiJ5TWUhiKhBssz+mjsuSI/lSZNPI9QvZOYzWvYGejtb+W3RlDDf8LD6Bdl5/mZeG8O1feUGhXTxEg==} + dependencies: + '@redis/bloom': 1.2.0(@redis/client@1.5.11) + '@redis/client': 1.5.11 + '@redis/graph': 1.1.0(@redis/client@1.5.11) + '@redis/json': 1.0.6(@redis/client@1.5.11) + '@redis/search': 1.1.5(@redis/client@1.5.11) + '@redis/time-series': 1.0.5(@redis/client@1.5.11) + dev: false + /reflect-metadata@0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts index 77d354f..50d7889 100644 --- a/apps/backend/src/app.module.ts +++ b/apps/backend/src/app.module.ts @@ -2,6 +2,8 @@ import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { BullModule } from '@nestjs/bullmq'; +import { redisStore } from 'cache-manager-redis-store'; +import { CacheInterceptor, CacheModule, CacheStore } from '@nestjs/cache-manager'; import { QueueDashboardModule } from './queue-dashboard/queue-dashboard.module'; import { CheckoutSessionModule } from './checkout-session/checkout-session.module'; import { StripeListenerModule } from './stripe-listener/stripe-listener.module'; @@ -10,6 +12,7 @@ import { SuperTokenAccountingModule } from './super-token-accounting/super-token import { SuperfluidStripeConverterModule } from './superfluid-stripe-converter/superfluid-stripe-converter.module'; import { HealthModule } from './health/health.module'; import { registerStripeModule } from './stripe-module-config'; +import { APP_INTERCEPTOR } from '@nestjs/core'; const registerConfigModule = () => ConfigModule.forRoot({ @@ -19,12 +22,12 @@ const registerConfigModule = () => const registerBullModule = () => BullModule.forRootAsync({ inject: [ConfigService], - useFactory: async (configService: ConfigService) => ({ + useFactory: async (config: ConfigService) => ({ connection: { - host: configService.getOrThrow('REDIS_HOST'), - port: configService.getOrThrow('REDIS_PORT'), - password: configService.get('REDIS_PASSWORD'), - username: configService.get('REDIS_USER'), + host: config.getOrThrow('REDIS_HOST'), + port: config.getOrThrow('REDIS_PORT'), + password: config.get('REDIS_PASSWORD'), + username: config.get('REDIS_USER'), }, defaultJobOptions: { attempts: 3, @@ -36,9 +39,32 @@ const registerBullModule = () => }), }); +const registerCacheModule = () => + CacheModule.registerAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: async (config: ConfigService) => { + const store = await redisStore({ + socket: { + host: config.getOrThrow('REDIS_HOST'), + port: Number(config.getOrThrow('REDIS_PORT')), + }, + username: config.get('REDIS_USER'), + password: config.get('REDIS_PASSWORD') + }); + + return { + isGlobal: true, + store: store as unknown as CacheStore, // Nest.js hasn't caught up with right types + ttl: 3000, // In cache-manager v5, TTL is configured in milliseconds: https://docs.nestjs.com/techniques/caching + }; + }, + }); + @Module({ imports: [ registerConfigModule(), + registerCacheModule(), registerStripeModule(), registerBullModule(), QueueDashboardModule, @@ -50,6 +76,11 @@ const registerBullModule = () => HealthModule, ], controllers: [], - providers: [], + providers: [ + { + provide: APP_INTERCEPTOR, + useClass: CacheInterceptor, + }, + ], }) export class AppModule {}