Compare commits
1 Commits
dev
...
feature/gr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00378ece8d |
62
apps/web/config/graphql/ttl.ts
Normal file
62
apps/web/config/graphql/ttl.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { seconds } from '@/utils/time';
|
||||||
|
|
||||||
|
export const queryTTL: Record<string, number | false> = {
|
||||||
|
GetAddProductType: seconds().fromHours(12),
|
||||||
|
GetAddproductTypes: seconds().fromHours(12),
|
||||||
|
GetAgent: seconds().fromHours(12),
|
||||||
|
GetBrand: seconds().fromHours(3),
|
||||||
|
GetBrands: seconds().fromHours(3),
|
||||||
|
GetCoefficients: seconds().fromHours(12),
|
||||||
|
GetConfiguration: seconds().fromHours(3),
|
||||||
|
GetConfigurations: seconds().fromMinutes(15),
|
||||||
|
GetCurrencyChanges: seconds().fromHours(1),
|
||||||
|
GetDealer: seconds().fromHours(1),
|
||||||
|
GetDealerPerson: seconds().fromHours(1),
|
||||||
|
GetDealerPersons: seconds().fromHours(1),
|
||||||
|
GetDealers: seconds().fromMinutes(15),
|
||||||
|
GetFuelCards: seconds().fromHours(12),
|
||||||
|
GetGPSBrands: seconds().fromHours(24),
|
||||||
|
GetGPSModels: seconds().fromHours(24),
|
||||||
|
GetImportProgram: seconds().fromHours(12),
|
||||||
|
GetInsNSIBTypes: seconds().fromHours(12),
|
||||||
|
GetInsuranceCompanies: seconds().fromHours(12),
|
||||||
|
GetInsuranceCompany: seconds().fromHours(12),
|
||||||
|
GetLead: false,
|
||||||
|
GetLeadUrl: seconds().fromHours(12),
|
||||||
|
GetLeads: false,
|
||||||
|
GetLeaseObjectType: seconds().fromHours(24),
|
||||||
|
GetLeaseObjectTypes: seconds().fromHours(24),
|
||||||
|
GetLeasingWithoutKaskoTypes: seconds().fromHours(12),
|
||||||
|
GetModel: seconds().fromHours(3),
|
||||||
|
GetModels: seconds().fromMinutes(15),
|
||||||
|
GetOpportunities: false,
|
||||||
|
GetOpportunity: false,
|
||||||
|
GetOpportunityUrl: seconds().fromHours(12),
|
||||||
|
GetProduct: seconds().fromHours(12),
|
||||||
|
GetProducts: seconds().fromHours(12),
|
||||||
|
GetQuote: false,
|
||||||
|
GetQuoteData: false,
|
||||||
|
GetQuoteUrl: seconds().fromHours(12),
|
||||||
|
GetQuotes: false,
|
||||||
|
GetRate: seconds().fromHours(12),
|
||||||
|
GetRates: seconds().fromHours(12),
|
||||||
|
GetRegion: seconds().fromHours(24),
|
||||||
|
GetRegions: seconds().fromHours(24),
|
||||||
|
GetRegistrationTypes: seconds().fromHours(12),
|
||||||
|
GetRewardCondition: seconds().fromHours(1),
|
||||||
|
GetRewardConditions: seconds().fromHours(1),
|
||||||
|
GetRoles: seconds().fromHours(12),
|
||||||
|
GetSotCoefficientType: seconds().fromHours(12),
|
||||||
|
GetSubsidies: seconds().fromHours(12),
|
||||||
|
GetSubsidy: seconds().fromHours(12),
|
||||||
|
GetSystemUser: seconds().fromHours(12),
|
||||||
|
GetTarif: seconds().fromHours(12),
|
||||||
|
GetTarifs: seconds().fromHours(12),
|
||||||
|
GetTechnicalCards: seconds().fromHours(12),
|
||||||
|
GetTelematicTypes: seconds().fromHours(12),
|
||||||
|
GetTown: seconds().fromHours(24),
|
||||||
|
GetTowns: seconds().fromHours(24),
|
||||||
|
GetTrackerTypes: seconds().fromHours(12),
|
||||||
|
GetTransactionCurrencies: seconds().fromHours(12),
|
||||||
|
GetTransactionCurrency: seconds().fromHours(12),
|
||||||
|
};
|
||||||
@ -3,6 +3,11 @@ const { z } = require('zod');
|
|||||||
const envSchema = z.object({
|
const envSchema = z.object({
|
||||||
BASE_PATH: z.string().optional().default(''),
|
BASE_PATH: z.string().optional().default(''),
|
||||||
PORT: z.string().optional(),
|
PORT: z.string().optional(),
|
||||||
|
REDIS_HOST: z.string(),
|
||||||
|
REDIS_PORT: z
|
||||||
|
.string()
|
||||||
|
.transform((val) => Number.parseInt(val, 10))
|
||||||
|
.default('6379'),
|
||||||
SENTRY_AUTH_TOKEN: z.string(),
|
SENTRY_AUTH_TOKEN: z.string(),
|
||||||
SENTRY_DSN: z.string(),
|
SENTRY_DSN: z.string(),
|
||||||
SENTRY_ENVIRONMENT: z.string(),
|
SENTRY_ENVIRONMENT: z.string(),
|
||||||
@ -16,7 +21,6 @@ const envSchema = z.object({
|
|||||||
URL_CRM_CREATEKP_DIRECT: z.string(),
|
URL_CRM_CREATEKP_DIRECT: z.string(),
|
||||||
URL_CRM_DOWNLOADKP_BASE: z.string(),
|
URL_CRM_DOWNLOADKP_BASE: z.string(),
|
||||||
URL_CRM_GRAPHQL_DIRECT: z.string(),
|
URL_CRM_GRAPHQL_DIRECT: z.string(),
|
||||||
URL_CRM_GRAPHQL_PROXY: z.string().default('http://api:3001/proxy/graphql'),
|
|
||||||
URL_ELT_KASKO_DIRECT: z.string(),
|
URL_ELT_KASKO_DIRECT: z.string(),
|
||||||
URL_ELT_OSAGO_DIRECT: z.string(),
|
URL_ELT_OSAGO_DIRECT: z.string(),
|
||||||
URL_GET_USER_DIRECT: z.string(),
|
URL_GET_USER_DIRECT: z.string(),
|
||||||
|
|||||||
@ -21,7 +21,7 @@ function getUrls() {
|
|||||||
PORT,
|
PORT,
|
||||||
URL_ELT_KASKO_DIRECT,
|
URL_ELT_KASKO_DIRECT,
|
||||||
URL_ELT_OSAGO_DIRECT,
|
URL_ELT_OSAGO_DIRECT,
|
||||||
URL_CRM_GRAPHQL_PROXY,
|
URL_CRM_GRAPHQL_DIRECT,
|
||||||
URL_CACHE_GET_QUERIES_DIRECT,
|
URL_CACHE_GET_QUERIES_DIRECT,
|
||||||
URL_CACHE_DELETE_QUERY_DIRECT,
|
URL_CACHE_DELETE_QUERY_DIRECT,
|
||||||
URL_CACHE_RESET_QUERIES_DIRECT,
|
URL_CACHE_RESET_QUERIES_DIRECT,
|
||||||
@ -41,7 +41,7 @@ function getUrls() {
|
|||||||
URL_CORE_FINGAP: URL_CORE_FINGAP_DIRECT,
|
URL_CORE_FINGAP: URL_CORE_FINGAP_DIRECT,
|
||||||
URL_CRM_CREATEKP: URL_CRM_CREATEKP_DIRECT,
|
URL_CRM_CREATEKP: URL_CRM_CREATEKP_DIRECT,
|
||||||
URL_CRM_DOWNLOADKP: withBasePath(urls.URL_CRM_DOWNLOADKP_PROXY),
|
URL_CRM_DOWNLOADKP: withBasePath(urls.URL_CRM_DOWNLOADKP_PROXY),
|
||||||
URL_CRM_GRAPHQL: URL_CRM_GRAPHQL_PROXY,
|
URL_CRM_GRAPHQL: URL_CRM_GRAPHQL_DIRECT,
|
||||||
URL_ELT_KASKO: URL_ELT_KASKO_DIRECT,
|
URL_ELT_KASKO: URL_ELT_KASKO_DIRECT,
|
||||||
URL_ELT_OSAGO: URL_ELT_OSAGO_DIRECT,
|
URL_ELT_OSAGO: URL_ELT_OSAGO_DIRECT,
|
||||||
URL_GET_USER: URL_GET_USER_DIRECT,
|
URL_GET_USER: URL_GET_USER_DIRECT,
|
||||||
|
|||||||
@ -8,7 +8,7 @@ module.exports = {
|
|||||||
URL_CORE_FINGAP_PROXY: '/api/core/fingap',
|
URL_CORE_FINGAP_PROXY: '/api/core/fingap',
|
||||||
URL_CRM_CREATEKP_PROXY: '/api/crm/create-kp',
|
URL_CRM_CREATEKP_PROXY: '/api/crm/create-kp',
|
||||||
URL_CRM_DOWNLOADKP_PROXY: '/api/crm/download-kp',
|
URL_CRM_DOWNLOADKP_PROXY: '/api/crm/download-kp',
|
||||||
URL_CRM_GRAPHQL_PROXY: '/api/graphql/crm',
|
URL_CRM_GRAPHQL_PROXY: '/api/crm/graphql',
|
||||||
URL_ELT_KASKO_PROXY: '/api/elt/kasko',
|
URL_ELT_KASKO_PROXY: '/api/elt/kasko',
|
||||||
URL_ELT_OSAGO_PROXY: '/api/elt/osago',
|
URL_ELT_OSAGO_PROXY: '/api/elt/osago',
|
||||||
URL_GET_USER_PROXY: '/api/auth/user',
|
URL_GET_USER_PROXY: '/api/auth/user',
|
||||||
|
|||||||
@ -49,10 +49,6 @@ module.exports = withSentryConfig(
|
|||||||
|
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
return [
|
return [
|
||||||
{
|
|
||||||
destination: env.URL_CRM_GRAPHQL_PROXY + '/:path*',
|
|
||||||
source: urls.URL_CRM_GRAPHQL_PROXY + '/:path*',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
destination: env.URL_CRM_DOWNLOADKP_BASE + '/:path*',
|
destination: env.URL_CRM_DOWNLOADKP_BASE + '/:path*',
|
||||||
source: urls.URL_CRM_DOWNLOADKP_PROXY + '/:path*',
|
source: urls.URL_CRM_DOWNLOADKP_PROXY + '/:path*',
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
"@trpc/server": "^10.45.1",
|
"@trpc/server": "^10.45.1",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
|
"ioredis": "^5.3.2",
|
||||||
"mobx": "^6.12.0",
|
"mobx": "^6.12.0",
|
||||||
"mobx-react-lite": "^4.0.5",
|
"mobx-react-lite": "^4.0.5",
|
||||||
"modern-normalize": "^2.0.0",
|
"modern-normalize": "^2.0.0",
|
||||||
|
|||||||
50
apps/web/pages/api/crm/graphql.ts
Normal file
50
apps/web/pages/api/crm/graphql.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { queryTTL } from '@/config/graphql/ttl';
|
||||||
|
import getUrls from '@/config/urls';
|
||||||
|
import { createRedisInstance } from '@/redis/client';
|
||||||
|
import { HttpError } from '@/utils/error';
|
||||||
|
import type { ApolloQueryResult, GraphQLRequest } from '@apollo/client';
|
||||||
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
|
||||||
|
const { URL_CRM_GRAPHQL } = getUrls();
|
||||||
|
|
||||||
|
const redis = createRedisInstance();
|
||||||
|
|
||||||
|
function getHeaders(req: NextApiRequest) {
|
||||||
|
const headers = new Headers();
|
||||||
|
Object.keys(req.headers).forEach((key) => {
|
||||||
|
const value = req.headers[key];
|
||||||
|
if (value !== undefined && typeof value === 'string') headers.set(key, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
const headers = getHeaders(req);
|
||||||
|
|
||||||
|
const { operationName, variables } = req.body as GraphQLRequest;
|
||||||
|
const key = `${operationName} ${JSON.stringify(variables)}`;
|
||||||
|
const cached = await redis.get(key);
|
||||||
|
|
||||||
|
if (cached) return res.send({ ...JSON.parse(cached), cached: true });
|
||||||
|
|
||||||
|
const response = await fetch(URL_CRM_GRAPHQL, {
|
||||||
|
body: JSON.stringify(req.body),
|
||||||
|
headers,
|
||||||
|
method: req.method,
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = (await response.json()) as ApolloQueryResult<unknown>;
|
||||||
|
|
||||||
|
if (!response.ok || data?.error || data?.errors?.length) {
|
||||||
|
throw new HttpError(response.statusText, response.status || 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operationName) {
|
||||||
|
const ttl = queryTTL[operationName];
|
||||||
|
|
||||||
|
if (data && ttl !== false) await redis.set(key, JSON.stringify(data), 'EX', ttl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.send(data);
|
||||||
|
}
|
||||||
36
apps/web/redis/client.ts
Normal file
36
apps/web/redis/client.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
import envSchema from '@/config/schema/env';
|
||||||
|
import type { RedisOptions } from 'ioredis';
|
||||||
|
import { Redis } from 'ioredis';
|
||||||
|
|
||||||
|
const { REDIS_HOST, REDIS_PORT } = envSchema.parse(process.env);
|
||||||
|
|
||||||
|
export function createRedisInstance() {
|
||||||
|
try {
|
||||||
|
const options: RedisOptions = {
|
||||||
|
enableAutoPipelining: true,
|
||||||
|
host: REDIS_HOST,
|
||||||
|
lazyConnect: true,
|
||||||
|
maxRetriesPerRequest: 0,
|
||||||
|
port: REDIS_PORT,
|
||||||
|
retryStrategy: (times: number) => {
|
||||||
|
if (times > 3) {
|
||||||
|
throw new Error(`[Redis] Could not connect after ${times} attempts`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.min(times * 200, 1000);
|
||||||
|
},
|
||||||
|
showFriendlyErrorStack: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const redis = new Redis(options);
|
||||||
|
|
||||||
|
redis.on('error', (error: unknown) => {
|
||||||
|
console.warn('[Redis] Error connecting', error);
|
||||||
|
});
|
||||||
|
|
||||||
|
return redis;
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`[Redis] Could not create a Redis instance. ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
apps/web/utils/time.ts
Normal file
13
apps/web/utils/time.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export function seconds() {
|
||||||
|
return {
|
||||||
|
fromDays(days: number) {
|
||||||
|
return days * 24 * 60 * 60;
|
||||||
|
},
|
||||||
|
fromHours(hours: number) {
|
||||||
|
return hours * 60 * 60;
|
||||||
|
},
|
||||||
|
fromMinutes(minutes: number) {
|
||||||
|
return minutes * 60;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@ -156,6 +156,9 @@ importers:
|
|||||||
dayjs:
|
dayjs:
|
||||||
specifier: ^1.11.10
|
specifier: ^1.11.10
|
||||||
version: 1.11.10
|
version: 1.11.10
|
||||||
|
ioredis:
|
||||||
|
specifier: ^5.3.2
|
||||||
|
version: 5.3.2
|
||||||
mobx:
|
mobx:
|
||||||
specifier: ^6.12.0
|
specifier: ^6.12.0
|
||||||
version: 6.12.0
|
version: 6.12.0
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user