import { queryTTL } from './lib/config'; import type { GQLRequest } from './types'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { All, Controller, Delete, Get, HttpException, HttpStatus, Inject, Query, Req, Res, } from '@nestjs/common'; import type { Cache } from 'cache-manager'; import { FastifyReply, FastifyRequest } from 'fastify'; import { env } from 'src/config/env'; type RedisStore = Omit & { set: (key: string, value: unknown, { ttl }: { ttl: number }) => Promise; }; @Controller('proxy') export class ProxyController { constructor( @Inject(CACHE_MANAGER) private readonly cacheManager: RedisStore, ) {} @All('/graphql') public async graphql(@Req() req: FastifyRequest, @Res() reply: FastifyReply) { const { operationName, query, variables } = req.body as GQLRequest; const key = `${operationName} ${JSON.stringify(variables)}`; const cached = await this.cacheManager.get(key); if (cached) return reply.send(cached); const response = await fetch(env.URL_CRM_GRAPHQL_DIRECT, { body: JSON.stringify({ operationName, query, variables }), headers: { Authorization: req.headers.authorization, 'Content-Type': 'application/json', Cookie: req.headers.cookie, }, method: req.method, }); if (!response.ok) throw new HttpException( response.statusText, response.status || HttpStatus.INTERNAL_SERVER_ERROR, ); const data = await response.json(); const ttl = queryTTL[operationName]; if (data && ttl !== false) await this.cacheManager.set(key, data, { ttl: ttl || env.CACHE_TTL }); return reply.send(data); } @Get('/queries') public async getQueriesList( @Req() req: FastifyRequest, @Res() reply: FastifyReply, ) { const list = await this.cacheManager.store.keys('*'); const res = (Object.keys(queryTTL) as Array).reduce( (acc, queryName) => { const queries = list.filter((x) => x.split(' ').at(0) === queryName); const ttl = queryTTL[queryName]; acc[queryName] = { queries, ttl }; return acc; }, {} as Record, ); return reply.send(res); } @Delete('delete-query') public async deleteQuery( @Query('queryName') queryName: string, @Res() reply: FastifyReply, ) { try { await this.cacheManager.del(queryName); return reply.send('ok'); } catch (error) { throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR); } } @Delete('flush-all') public async flushAll(@Res() reply: FastifyReply) { try { await this.cacheManager.reset(); return reply.send('ok'); } catch (error) { throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR); } } }