mirror of
https://github.com/twentyhq/twenty.git
synced 2024-10-27 03:33:21 +03:00
Add rate limiting in the server using built in Nest.js capability (#3566)
* Add rate limiting in the server using built in Nest.js capability * Generatekey based on ip address when an http request is sent * Update env var types to number for ttl and limit * Remove unused env variables * Use getRequest utility function * fix: remove dist from path * fix: adding .env variables * fix: remove unused functions * feat: throttler plugin * Fix according to review --------- Co-authored-by: Jérémy Magrin <jeremy.magrin@gmail.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
parent
3831ddc002
commit
850eab8f8f
@ -43,6 +43,12 @@ import TabItem from '@theme/TabItem';
|
||||
['PORT', '3000', 'Port'],
|
||||
]}></OptionTable>
|
||||
|
||||
### Security
|
||||
|
||||
<OptionTable options={[
|
||||
['API_RATE_LIMITING_TTL', '100', 'API rate limiting time window'],
|
||||
['API_RATE_LIMITING_LIMIT', '200', 'API rate limiting max requests'],
|
||||
]}></OptionTable>
|
||||
### Tokens
|
||||
|
||||
<OptionTable options={[
|
||||
|
@ -52,3 +52,5 @@ SIGN_IN_PREFILLED=true
|
||||
# EMAIL_SMTP_USER=
|
||||
# EMAIL_SMTP_PASSWORD=
|
||||
# PASSWORD_RESET_TOKEN_EXPIRES_IN=5m
|
||||
# API_RATE_LIMITING_TTL=
|
||||
# API_RATE_LIMITING_LIMIT=
|
||||
|
@ -10,7 +10,6 @@ ACCESS_TOKEN_SECRET=secret_jwt
|
||||
LOGIN_TOKEN_SECRET=secret_login_tokens
|
||||
REFRESH_TOKEN_SECRET=secret_refresh_token
|
||||
|
||||
|
||||
# ———————— Optional ————————
|
||||
# DEBUG_MODE=false
|
||||
# SIGN_IN_PREFILLED=false
|
||||
|
@ -36,6 +36,7 @@
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.363.0",
|
||||
"@aws-sdk/credential-providers": "^3.363.0",
|
||||
"@envelop/on-resolve": "^4.1.0",
|
||||
"@graphql-yoga/nestjs": "patch:@graphql-yoga/nestjs@2.1.0#./patches/@graphql-yoga+nestjs+2.1.0.patch",
|
||||
"@nestjs/apollo": "^11.0.5",
|
||||
"@nestjs/axios": "^3.0.1",
|
||||
@ -70,6 +71,8 @@
|
||||
"googleapis": "105",
|
||||
"graphql": "^16.8.1",
|
||||
"graphql-fields": "^2.0.3",
|
||||
"graphql-middleware": "^6.1.35",
|
||||
"graphql-rate-limit": "^3.3.0",
|
||||
"graphql-subscriptions": "2.0.0",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"graphql-type-json": "^0.3.2",
|
||||
|
@ -4,12 +4,13 @@ import { ConfigModule } from '@nestjs/config';
|
||||
|
||||
import { YogaDriver, YogaDriverConfig } from '@graphql-yoga/nestjs';
|
||||
|
||||
import { GraphQLConfigService } from 'src/graphql-config.service';
|
||||
import { GraphQLConfigService } from 'src/graphql-config/graphql-config.service';
|
||||
|
||||
import { CoreModule } from './core/core.module';
|
||||
import { IntegrationsModule } from './integrations/integrations.module';
|
||||
import { HealthModule } from './health/health.module';
|
||||
import { WorkspaceModule } from './workspace/workspace.module';
|
||||
import { GraphQLConfigModule } from './graphql-config/graphql-config.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -18,7 +19,7 @@ import { WorkspaceModule } from './workspace/workspace.module';
|
||||
}),
|
||||
GraphQLModule.forRootAsync<YogaDriverConfig>({
|
||||
driver: YogaDriver,
|
||||
imports: [CoreModule],
|
||||
imports: [CoreModule, GraphQLConfigModule],
|
||||
useClass: GraphQLConfigService,
|
||||
}),
|
||||
HealthModule,
|
||||
|
@ -0,0 +1,26 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { YogaDriverServerContext } from '@graphql-yoga/nestjs';
|
||||
|
||||
import { GraphQLContext } from 'src/graphql-config/interfaces/graphql-context.interface';
|
||||
|
||||
import { TokenService } from 'src/core/auth/services/token.service';
|
||||
|
||||
@Injectable()
|
||||
export class CreateContextFactory {
|
||||
constructor(private readonly tokenService: TokenService) {}
|
||||
|
||||
async create(
|
||||
context: YogaDriverServerContext<'express'>,
|
||||
): Promise<GraphQLContext> {
|
||||
// Check if token is present in the request
|
||||
if (this.tokenService.isTokenPresent(context.req)) {
|
||||
const data = await this.tokenService.validateToken(context.req);
|
||||
|
||||
// Inject user and workspace into the context
|
||||
return { ...context, ...data };
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
import { CreateContextFactory } from './create-context.factory';
|
||||
|
||||
export const graphQLFactories = [CreateContextFactory];
|
@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { CoreModule } from 'src/core/core.module';
|
||||
import { graphQLFactories } from 'src/graphql-config/factories';
|
||||
|
||||
@Module({
|
||||
imports: [CoreModule],
|
||||
providers: [...graphQLFactories],
|
||||
exports: [...graphQLFactories],
|
||||
})
|
||||
export class GraphQLConfigModule {}
|
@ -19,15 +19,23 @@ import { ExceptionHandlerService } from 'src/integrations/exception-handler/exce
|
||||
import { handleExceptionAndConvertToGraphQLError } from 'src/filters/utils/global-exception-handler.util';
|
||||
import { renderApolloPlayground } from 'src/workspace/utils/render-apollo-playground.util';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import { useExceptionHandler } from 'src/integrations/exception-handler/hooks/use-exception-handler.hook';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
import { useThrottler } from 'src/integrations/throttler/hooks/use-throttler';
|
||||
|
||||
import { useExceptionHandler } from './integrations/exception-handler/hooks/use-exception-handler.hook';
|
||||
import { User } from './core/user/user.entity';
|
||||
import { CreateContextFactory } from './factories/create-context.factory';
|
||||
|
||||
export interface GraphQLContext extends YogaDriverServerContext<'express'> {
|
||||
user?: User;
|
||||
workspace?: Workspace;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class GraphQLConfigService
|
||||
implements GqlOptionsFactory<YogaDriverConfig<'express'>>
|
||||
{
|
||||
constructor(
|
||||
private readonly createContextFactory: CreateContextFactory,
|
||||
private readonly tokenService: TokenService,
|
||||
private readonly exceptionHandlerService: ExceptionHandlerService,
|
||||
private readonly environmentService: EnvironmentService,
|
||||
@ -37,7 +45,7 @@ export class GraphQLConfigService
|
||||
createGqlOptions(): YogaDriverConfig {
|
||||
const isDebugMode = this.environmentService.isDebugMode();
|
||||
const config: YogaDriverConfig = {
|
||||
context: ({ req }) => ({ req }),
|
||||
context: (context) => this.createContextFactory.create(context),
|
||||
autoSchemaFile: true,
|
||||
include: [CoreModule],
|
||||
conditionalSchema: async (context) => {
|
||||
@ -93,9 +101,15 @@ export class GraphQLConfigService
|
||||
},
|
||||
resolvers: { JSON: GraphQLJSON },
|
||||
plugins: [
|
||||
useThrottler({
|
||||
ttl: this.environmentService.getApiRateLimitingTtl(),
|
||||
limit: this.environmentService.getApiRateLimitingLimit(),
|
||||
identifyFn: (context) => {
|
||||
return context.user?.id ?? context.req.ip ?? 'anonymous';
|
||||
},
|
||||
}),
|
||||
useExceptionHandler({
|
||||
exceptionHandlerService: this.exceptionHandlerService,
|
||||
tokenService: this.tokenService,
|
||||
}),
|
||||
],
|
||||
};
|
@ -0,0 +1,9 @@
|
||||
import { YogaDriverServerContext } from '@graphql-yoga/nestjs';
|
||||
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
import { Workspace } from 'src/core/workspace/workspace.entity';
|
||||
|
||||
export interface GraphQLContext extends YogaDriverServerContext<'express'> {
|
||||
user?: User;
|
||||
workspace?: Workspace;
|
||||
}
|
@ -287,4 +287,12 @@ export class EnvironmentService {
|
||||
isSignUpDisabled(): boolean {
|
||||
return this.configService.get<boolean>('IS_SIGN_UP_DISABLED') ?? false;
|
||||
}
|
||||
|
||||
getApiRateLimitingTtl(): number {
|
||||
return this.configService.get<number>('API_RATE_LIMITING_TTL') ?? 100;
|
||||
}
|
||||
|
||||
getApiRateLimitingLimit(): number {
|
||||
return this.configService.get<number>('API_RATE_LIMITING_LIMIT') ?? 200;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { GraphQLError, Kind, OperationDefinitionNode, print } from 'graphql';
|
||||
import * as express from 'express';
|
||||
import {
|
||||
getDocumentString,
|
||||
handleStreamOrSingleExecutionResult,
|
||||
@ -7,10 +6,9 @@ import {
|
||||
Plugin,
|
||||
} from '@envelop/core';
|
||||
|
||||
import { ExceptionHandlerUser } from 'src/integrations/exception-handler/interfaces/exception-handler-user.interface';
|
||||
import { GraphQLContext } from 'src/graphql-config/interfaces/graphql-context.interface';
|
||||
|
||||
import { ExceptionHandlerService } from 'src/integrations/exception-handler/exception-handler.service';
|
||||
import { TokenService } from 'src/core/auth/services/token.service';
|
||||
import {
|
||||
convertExceptionToGraphQLError,
|
||||
filterException,
|
||||
@ -18,13 +16,9 @@ import {
|
||||
|
||||
export type ExceptionHandlerPluginOptions = {
|
||||
/**
|
||||
* The driver to use to handle exceptions.
|
||||
* The exception handler service to use.
|
||||
*/
|
||||
exceptionHandlerService: ExceptionHandlerService;
|
||||
/**
|
||||
* The token service to use to get the token from the request.
|
||||
*/
|
||||
tokenService: TokenService;
|
||||
/**
|
||||
* The key of the event id in the error's extension. `null` to disable.
|
||||
* @default exceptionEventId
|
||||
@ -32,13 +26,9 @@ export type ExceptionHandlerPluginOptions = {
|
||||
eventIdKey?: string | null;
|
||||
};
|
||||
|
||||
export const useExceptionHandler = <
|
||||
PluginContext extends Record<string, any> = object,
|
||||
>(
|
||||
export const useExceptionHandler = <PluginContext extends GraphQLContext>(
|
||||
options: ExceptionHandlerPluginOptions,
|
||||
): Plugin<PluginContext> => {
|
||||
const exceptionHandlerService = options.exceptionHandlerService;
|
||||
const tokenService = options.tokenService;
|
||||
const eventIdKey = options.eventIdKey === null ? null : 'exceptionEventId';
|
||||
|
||||
function addEventId(
|
||||
@ -54,28 +44,17 @@ export const useExceptionHandler = <
|
||||
|
||||
return {
|
||||
async onExecute({ args }) {
|
||||
const exceptionHandlerService = options.exceptionHandlerService;
|
||||
const rootOperation = args.document.definitions.find(
|
||||
(o) => o.kind === Kind.OPERATION_DEFINITION,
|
||||
) as OperationDefinitionNode;
|
||||
const operationType = rootOperation.operation;
|
||||
const user = args.contextValue.user;
|
||||
const document = getDocumentString(args.document, print);
|
||||
const request = args.contextValue.req as express.Request;
|
||||
const opName =
|
||||
args.operationName ||
|
||||
rootOperation.name?.value ||
|
||||
'Anonymous Operation';
|
||||
let user: ExceptionHandlerUser | undefined;
|
||||
|
||||
if (tokenService.isTokenPresent(request)) {
|
||||
try {
|
||||
const data = await tokenService.validateToken(request);
|
||||
|
||||
user = {
|
||||
id: data.user?.id,
|
||||
email: data.user?.email,
|
||||
};
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return {
|
||||
onExecuteDone(payload) {
|
||||
|
@ -0,0 +1,86 @@
|
||||
import { GraphQLResolveInfo } from 'graphql';
|
||||
import { getGraphQLRateLimiter } from 'graphql-rate-limit';
|
||||
import { Plugin } from '@envelop/core';
|
||||
import { useOnResolve } from '@envelop/on-resolve';
|
||||
|
||||
import { GraphQLContext } from 'src/graphql-config/graphql-config.service';
|
||||
|
||||
export class UnauthenticatedError extends Error {}
|
||||
|
||||
export type IdentifyFn<ContextType = ThrottlerContext> = (
|
||||
context: ContextType,
|
||||
) => string;
|
||||
|
||||
export type ThrottlerPluginOptions = {
|
||||
identifyFn: IdentifyFn;
|
||||
ttl?: number;
|
||||
limit?: number;
|
||||
transformError?: (message: string) => Error;
|
||||
onThrottlerError?: (event: {
|
||||
error: string;
|
||||
identifier: string;
|
||||
context: unknown;
|
||||
info: GraphQLResolveInfo;
|
||||
}) => void;
|
||||
};
|
||||
|
||||
interface ThrottlerContext extends GraphQLContext {
|
||||
rateLimiterFn: ReturnType<typeof getGraphQLRateLimiter>;
|
||||
}
|
||||
|
||||
export const useThrottler = (
|
||||
options: ThrottlerPluginOptions,
|
||||
): Plugin<ThrottlerContext> => {
|
||||
const rateLimiterFn = getGraphQLRateLimiter({
|
||||
identifyContext: options.identifyFn,
|
||||
});
|
||||
|
||||
return {
|
||||
onPluginInit({ addPlugin }) {
|
||||
addPlugin(
|
||||
useOnResolve(async ({ args, root, context, info }) => {
|
||||
if (options.limit && options.ttl) {
|
||||
const id = options.identifyFn(context);
|
||||
|
||||
const errorMessage = await context.rateLimiterFn(
|
||||
{ parent: root, args, context, info },
|
||||
{
|
||||
max: options?.limit,
|
||||
window: `${options?.ttl}s`,
|
||||
message: interpolate('Too much request.', {
|
||||
id,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
if (errorMessage) {
|
||||
if (options.onThrottlerError) {
|
||||
options.onThrottlerError({
|
||||
error: errorMessage,
|
||||
identifier: id,
|
||||
context,
|
||||
info,
|
||||
});
|
||||
}
|
||||
|
||||
if (options.transformError) {
|
||||
throw options.transformError(errorMessage);
|
||||
}
|
||||
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
},
|
||||
async onContextBuilding({ extendContext }) {
|
||||
extendContext({
|
||||
rateLimiterFn,
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
function interpolate(message: string, args: { [key: string]: string }) {
|
||||
return message.replace(/\{{([^)]*)\}}/g, (_, key) => args[key.trim()]);
|
||||
}
|
@ -1,20 +1,23 @@
|
||||
import { YogaDriverConfig } from '@graphql-yoga/nestjs';
|
||||
import GraphQLJSON from 'graphql-type-json';
|
||||
|
||||
import { TokenService } from 'src/core/auth/services/token.service';
|
||||
import { CreateContextFactory } from 'src/graphql-config/factories/create-context.factory';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import { ExceptionHandlerService } from 'src/integrations/exception-handler/exception-handler.service';
|
||||
import { useExceptionHandler } from 'src/integrations/exception-handler/hooks/use-exception-handler.hook';
|
||||
import { useThrottler } from 'src/integrations/throttler/hooks/use-throttler';
|
||||
import { MetadataModule } from 'src/metadata/metadata.module';
|
||||
import { renderApolloPlayground } from 'src/workspace/utils/render-apollo-playground.util';
|
||||
|
||||
export const metadataModuleFactory = async (
|
||||
environmentService: EnvironmentService,
|
||||
exceptionHandlerService: ExceptionHandlerService,
|
||||
tokenService: TokenService,
|
||||
createContextFactory: CreateContextFactory,
|
||||
): Promise<YogaDriverConfig> => {
|
||||
const config: YogaDriverConfig = {
|
||||
context: ({ req }) => ({ req }),
|
||||
context(context) {
|
||||
return createContextFactory.create(context);
|
||||
},
|
||||
autoSchemaFile: true,
|
||||
include: [MetadataModule],
|
||||
renderGraphiQL() {
|
||||
@ -22,9 +25,15 @@ export const metadataModuleFactory = async (
|
||||
},
|
||||
resolvers: { JSON: GraphQLJSON },
|
||||
plugins: [
|
||||
useThrottler({
|
||||
ttl: environmentService.getApiRateLimitingTtl(),
|
||||
limit: environmentService.getApiRateLimitingLimit(),
|
||||
identifyFn: (context) => {
|
||||
return context.user?.id ?? context.req.ip ?? 'anonymous';
|
||||
},
|
||||
}),
|
||||
useExceptionHandler({
|
||||
exceptionHandlerService,
|
||||
tokenService,
|
||||
}),
|
||||
],
|
||||
path: '/metadata',
|
||||
|
@ -8,20 +8,25 @@ import { WorkspaceMigrationModule } from 'src/metadata/workspace-migration/works
|
||||
import { metadataModuleFactory } from 'src/metadata/metadata.module-factory';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import { ExceptionHandlerService } from 'src/integrations/exception-handler/exception-handler.service';
|
||||
import { TokenService } from 'src/core/auth/services/token.service';
|
||||
import { AuthModule } from 'src/core/auth/auth.module';
|
||||
import { GraphQLConfigModule } from 'src/graphql-config/graphql-config.module';
|
||||
import { CreateContextFactory } from 'src/graphql-config/factories/create-context.factory';
|
||||
|
||||
import { DataSourceModule } from './data-source/data-source.module';
|
||||
import { FieldMetadataModule } from './field-metadata/field-metadata.module';
|
||||
import { ObjectMetadataModule } from './object-metadata/object-metadata.module';
|
||||
import { RelationMetadataModule } from './relation-metadata/relation-metadata.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
GraphQLModule.forRootAsync<YogaDriverConfig>({
|
||||
driver: YogaDriver,
|
||||
useFactory: metadataModuleFactory,
|
||||
imports: [AuthModule],
|
||||
inject: [EnvironmentService, ExceptionHandlerService, TokenService],
|
||||
imports: [GraphQLConfigModule],
|
||||
inject: [
|
||||
EnvironmentService,
|
||||
ExceptionHandlerService,
|
||||
CreateContextFactory,
|
||||
],
|
||||
}),
|
||||
DataSourceModule,
|
||||
FieldMetadataModule,
|
||||
|
227
yarn.lock
227
yarn.lock
@ -639,6 +639,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ardatan/aggregate-error@npm:0.0.6":
|
||||
version: 0.0.6
|
||||
resolution: "@ardatan/aggregate-error@npm:0.0.6"
|
||||
dependencies:
|
||||
tslib: "npm:~2.0.1"
|
||||
checksum: e374247b506baf753b21fdb32bd8eda12c3b3bf2bd7cc8954e2761ae3eb10e5033ab9cde6a0f279fbdb09e263358b29d40c05e79eb50a1eab08fbf8916a0253c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ardatan/relay-compiler@npm:12.0.0":
|
||||
version: 12.0.0
|
||||
resolution: "@ardatan/relay-compiler@npm:12.0.0"
|
||||
@ -3243,6 +3252,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:^7.15.4":
|
||||
version: 7.23.9
|
||||
resolution: "@babel/runtime@npm:7.23.9"
|
||||
dependencies:
|
||||
regenerator-runtime: "npm:^0.14.0"
|
||||
checksum: e71205fdd7082b2656512cc98e647d9ea7e222e4fe5c36e9e5adc026446fcc3ba7b3cdff8b0b694a0b78bb85db83e7b1e3d4c56ef90726682b74f13249cf952d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.2, @babel/runtime@npm:^7.7.2":
|
||||
version: 7.23.6
|
||||
resolution: "@babel/runtime@npm:7.23.6"
|
||||
@ -4611,6 +4629,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@envelop/on-resolve@npm:^4.1.0":
|
||||
version: 4.1.0
|
||||
resolution: "@envelop/on-resolve@npm:4.1.0"
|
||||
peerDependencies:
|
||||
"@envelop/core": ^5.0.0
|
||||
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0
|
||||
checksum: 4eb454df4b446303711edd0cd31e707fec06337c2e8327c7987d279fef8785e47e47ffe72c7444b4650a83efbf3234c6b85f550a8044ca181dcd10c05dcb3650
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@envelop/types@npm:4.0.1":
|
||||
version: 4.0.1
|
||||
resolution: "@envelop/types@npm:4.0.1"
|
||||
@ -5613,6 +5641,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@graphql-tools/batch-execute@npm:8.5.1":
|
||||
version: 8.5.1
|
||||
resolution: "@graphql-tools/batch-execute@npm:8.5.1"
|
||||
dependencies:
|
||||
"@graphql-tools/utils": "npm:8.9.0"
|
||||
dataloader: "npm:2.1.0"
|
||||
tslib: "npm:^2.4.0"
|
||||
value-or-promise: "npm:1.0.11"
|
||||
peerDependencies:
|
||||
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
|
||||
checksum: f41d05247c6d6218e874dfbb7ffc45bcdd957e00954dd6c8a6848fde76de727250b38a71904f973688c70cd1199a4538a4272f66deee6066acdb7442798d59c1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@graphql-tools/batch-execute@npm:^8.5.22":
|
||||
version: 8.5.22
|
||||
resolution: "@graphql-tools/batch-execute@npm:8.5.22"
|
||||
@ -5642,6 +5684,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@graphql-tools/delegate@npm:^8.8.1":
|
||||
version: 8.8.1
|
||||
resolution: "@graphql-tools/delegate@npm:8.8.1"
|
||||
dependencies:
|
||||
"@graphql-tools/batch-execute": "npm:8.5.1"
|
||||
"@graphql-tools/schema": "npm:8.5.1"
|
||||
"@graphql-tools/utils": "npm:8.9.0"
|
||||
dataloader: "npm:2.1.0"
|
||||
tslib: "npm:~2.4.0"
|
||||
value-or-promise: "npm:1.0.11"
|
||||
peerDependencies:
|
||||
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
|
||||
checksum: 2cd43a18f4f0ca4bccc52ad405b894933996b1269b676632651ca54e3e9f602615f453a235556dea1c9806ac8223079a1eae8f9d5dc624de7f2a8f822c3ceab4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@graphql-tools/delegate@npm:^9.0.31":
|
||||
version: 9.0.35
|
||||
resolution: "@graphql-tools/delegate@npm:9.0.35"
|
||||
@ -6008,6 +6066,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@graphql-tools/schema@npm:8.5.1, @graphql-tools/schema@npm:^8.0.0, @graphql-tools/schema@npm:^8.5.1":
|
||||
version: 8.5.1
|
||||
resolution: "@graphql-tools/schema@npm:8.5.1"
|
||||
dependencies:
|
||||
"@graphql-tools/merge": "npm:8.3.1"
|
||||
"@graphql-tools/utils": "npm:8.9.0"
|
||||
tslib: "npm:^2.4.0"
|
||||
value-or-promise: "npm:1.0.11"
|
||||
peerDependencies:
|
||||
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
|
||||
checksum: 06000908fc5d3143f7f70eaee82874b87df4dfdd24316e88231e71e6f62f50df2e5a4b6a063b36e98f05caac09afa17861bbc5bf1c886b3f2155b96ea15c973b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@graphql-tools/schema@npm:^10.0.0":
|
||||
version: 10.0.2
|
||||
resolution: "@graphql-tools/schema@npm:10.0.2"
|
||||
@ -6022,20 +6094,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@graphql-tools/schema@npm:^8.0.0":
|
||||
version: 8.5.1
|
||||
resolution: "@graphql-tools/schema@npm:8.5.1"
|
||||
dependencies:
|
||||
"@graphql-tools/merge": "npm:8.3.1"
|
||||
"@graphql-tools/utils": "npm:8.9.0"
|
||||
tslib: "npm:^2.4.0"
|
||||
value-or-promise: "npm:1.0.11"
|
||||
peerDependencies:
|
||||
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
|
||||
checksum: 06000908fc5d3143f7f70eaee82874b87df4dfdd24316e88231e71e6f62f50df2e5a4b6a063b36e98f05caac09afa17861bbc5bf1c886b3f2155b96ea15c973b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@graphql-tools/schema@npm:^9.0.0, @graphql-tools/schema@npm:^9.0.18, @graphql-tools/schema@npm:^9.0.19":
|
||||
version: 9.0.19
|
||||
resolution: "@graphql-tools/schema@npm:9.0.19"
|
||||
@ -6110,6 +6168,19 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@graphql-tools/utils@npm:^7.6.0":
|
||||
version: 7.10.0
|
||||
resolution: "@graphql-tools/utils@npm:7.10.0"
|
||||
dependencies:
|
||||
"@ardatan/aggregate-error": "npm:0.0.6"
|
||||
camel-case: "npm:4.1.2"
|
||||
tslib: "npm:~2.2.0"
|
||||
peerDependencies:
|
||||
graphql: ^14.0.0 || ^15.0.0
|
||||
checksum: e8b29bf3ff63c9ca123daa3785422189177ec0273331bb739a422d3055b5b3d0e956d357988e46b4b06e74d727c1ff228fe467d4e956a72ca8b6e292d0ce0f02
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@graphql-tools/utils@npm:^8.8.0":
|
||||
version: 8.13.1
|
||||
resolution: "@graphql-tools/utils@npm:8.13.1"
|
||||
@ -15259,7 +15330,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/lodash@npm:*, @types/lodash@npm:^4.14.167":
|
||||
"@types/lodash@npm:*, @types/lodash@npm:^4.14.167, @types/lodash@npm:^4.14.175":
|
||||
version: 4.14.202
|
||||
resolution: "@types/lodash@npm:4.14.202"
|
||||
checksum: 6064d43c8f454170841bd67c8266cc9069d9e570a72ca63f06bceb484cb4a3ee60c9c1f305c1b9e3a87826049fd41124b8ef265c4dd08b00f6766609c7fe9973
|
||||
@ -15924,6 +15995,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/yup@npm:0.29.13":
|
||||
version: 0.29.13
|
||||
resolution: "@types/yup@npm:0.29.13"
|
||||
checksum: d42c787bf3a837c2eeb53948a6c7f48046ddadddfdaab28db99b3b7ace3b988094a12e31c4f4cd5715eef94fb1e447b5ea19e321f1745b6279cf38735b455895
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/eslint-plugin@npm:^6.10.0":
|
||||
version: 6.17.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:6.17.0"
|
||||
@ -19836,7 +19914,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"camel-case@npm:^4.1.2":
|
||||
"camel-case@npm:4.1.2, camel-case@npm:^4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "camel-case@npm:4.1.2"
|
||||
dependencies:
|
||||
@ -22172,6 +22250,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dataloader@npm:2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "dataloader@npm:2.1.0"
|
||||
checksum: 91749b97c6cf218874aecc57116defbe28eb5dd102a2a6e292e084939f725d123dd49c186796069492a77eb105ff2aabae9c8b144cf82f92c1f673eb1abff7da
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dataloader@npm:^2.2.2":
|
||||
version: 2.2.2
|
||||
resolution: "dataloader@npm:2.2.2"
|
||||
@ -26308,6 +26393,32 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"graphql-middleware@npm:^6.1.35":
|
||||
version: 6.1.35
|
||||
resolution: "graphql-middleware@npm:6.1.35"
|
||||
dependencies:
|
||||
"@graphql-tools/delegate": "npm:^8.8.1"
|
||||
"@graphql-tools/schema": "npm:^8.5.1"
|
||||
peerDependencies:
|
||||
graphql: ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
|
||||
checksum: faa948e9490493e39a096b9a44941aa0ad66a760ca9413c02b7d177c38251cf6db8560aa182b6efa01e6d519f5e76c4802243e30b87f510fe6e31a89740885d0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"graphql-rate-limit@npm:^3.3.0":
|
||||
version: 3.3.0
|
||||
resolution: "graphql-rate-limit@npm:3.3.0"
|
||||
dependencies:
|
||||
"@graphql-tools/utils": "npm:^7.6.0"
|
||||
graphql-shield: "npm:^7.5.0"
|
||||
lodash.get: "npm:^4.4.2"
|
||||
ms: "npm:^2.1.3"
|
||||
peerDependencies:
|
||||
graphql: "*"
|
||||
checksum: 1c4eb8a83204ecec955ac5ad8994ddac723474920b788c77671da813419b383ee0e49123d8634820deada28687fb6f96aa47438b765c3340617048764a6b85f3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"graphql-request@npm:^6.0.0":
|
||||
version: 6.1.0
|
||||
resolution: "graphql-request@npm:6.1.0"
|
||||
@ -26320,6 +26431,21 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"graphql-shield@npm:^7.5.0":
|
||||
version: 7.6.5
|
||||
resolution: "graphql-shield@npm:7.6.5"
|
||||
dependencies:
|
||||
"@types/yup": "npm:0.29.13"
|
||||
object-hash: "npm:^3.0.0"
|
||||
tslib: "npm:^2.4.0"
|
||||
yup: "npm:^0.32.0"
|
||||
peerDependencies:
|
||||
graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
|
||||
graphql-middleware: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^6.0.0
|
||||
checksum: aed5783f2b632a31d93c5b3dc2cd8d62d99a471db397aad8d8db58487b4fbce3ae431129369de711500795ac9b5f5aaa2bdef48c4650ac81a9ff8bd8840a3605
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"graphql-subscriptions@npm:2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "graphql-subscriptions@npm:2.0.0"
|
||||
@ -30875,6 +31001,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash-es@npm:^4.17.21":
|
||||
version: 4.17.21
|
||||
resolution: "lodash-es@npm:4.17.21"
|
||||
checksum: fb407355f7e6cd523a9383e76e6b455321f0f153a6c9625e21a8827d10c54c2a2341bd2ae8d034358b60e07325e1330c14c224ff582d04612a46a4f0479ff2f2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash._reinterpolate@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "lodash._reinterpolate@npm:3.0.0"
|
||||
@ -33961,7 +34094,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1":
|
||||
"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1, ms@npm:^2.1.3":
|
||||
version: 2.1.3
|
||||
resolution: "ms@npm:2.1.3"
|
||||
checksum: d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48
|
||||
@ -34158,6 +34291,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nanoclone@npm:^0.2.1":
|
||||
version: 0.2.1
|
||||
resolution: "nanoclone@npm:0.2.1"
|
||||
checksum: 760b569ea841c9678fdf8d763c6d7bb093f0889150087f82d86c536a318b302939c82ce35cdaec999d0f687789d0d79d0f3f75a272d7a98dfac7a067c0b47053
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nanoid@npm:^3.1.32, nanoid@npm:^3.3.6, nanoid@npm:^3.3.7":
|
||||
version: 3.3.7
|
||||
resolution: "nanoid@npm:3.3.7"
|
||||
@ -35172,6 +35312,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"object-hash@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "object-hash@npm:3.0.0"
|
||||
checksum: a06844537107b960c1c8b96cd2ac8592a265186bfa0f6ccafe0d34eabdb526f6fa81da1f37c43df7ed13b12a4ae3457a16071603bcd39d8beddb5f08c37b0f47
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"object-inspect@npm:^1.13.1, object-inspect@npm:^1.9.0":
|
||||
version: 1.13.1
|
||||
resolution: "object-inspect@npm:1.13.1"
|
||||
@ -37472,6 +37619,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"property-expr@npm:^2.0.4":
|
||||
version: 2.0.6
|
||||
resolution: "property-expr@npm:2.0.6"
|
||||
checksum: 69b7da15038a1146d6447c69c445306f66a33c425271235bb20507f1846dbf9577a8f9dfafe8acbfcb66f924b270157f155248308f026a68758f35fc72265b3c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"property-information@npm:^6.0.0":
|
||||
version: 6.4.0
|
||||
resolution: "property-information@npm:6.4.0"
|
||||
@ -42495,6 +42649,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"toposort@npm:^2.0.2":
|
||||
version: 2.0.2
|
||||
resolution: "toposort@npm:2.0.2"
|
||||
checksum: ab9ca91fce4b972ccae9e2f539d755bf799a0c7eb60da07fd985fce0f14c159ed1e92305ff55697693b5bc13e300f5417db90e2593b127d421c9f6c440950222
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"totalist@npm:^3.0.0":
|
||||
version: 3.0.1
|
||||
resolution: "totalist@npm:3.0.1"
|
||||
@ -42879,6 +43040,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tslib@npm:~2.0.1":
|
||||
version: 2.0.3
|
||||
resolution: "tslib@npm:2.0.3"
|
||||
checksum: 57d9f8e71a768c37a70fcabbb76d686b31773329200db7135faff905818038c742191a0c3791e452ae738d057522c6151d34beddc8e4d0d897f28df84e55a0c0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tslib@npm:~2.2.0":
|
||||
version: 2.2.0
|
||||
resolution: "tslib@npm:2.2.0"
|
||||
checksum: 62c705c4d73bcafa3e191df21ed8f024497b61f0e97c3f3e864ae51bcc98d31b830f73ab94b12f7c0dbd2e8f26af759cb521dd61ae88793f0f2abc32b43599a3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tslib@npm:~2.4.0":
|
||||
version: 2.4.1
|
||||
resolution: "tslib@npm:2.4.1"
|
||||
@ -43009,6 +43184,7 @@ __metadata:
|
||||
dependencies:
|
||||
"@aws-sdk/client-s3": "npm:^3.363.0"
|
||||
"@aws-sdk/credential-providers": "npm:^3.363.0"
|
||||
"@envelop/on-resolve": "npm:^4.1.0"
|
||||
"@graphql-yoga/nestjs": "patch:@graphql-yoga/nestjs@2.1.0#./patches/@graphql-yoga+nestjs+2.1.0.patch"
|
||||
"@nestjs/apollo": "npm:^11.0.5"
|
||||
"@nestjs/axios": "npm:^3.0.1"
|
||||
@ -43049,6 +43225,8 @@ __metadata:
|
||||
googleapis: "npm:105"
|
||||
graphql: "npm:^16.8.1"
|
||||
graphql-fields: "npm:^2.0.3"
|
||||
graphql-middleware: "npm:^6.1.35"
|
||||
graphql-rate-limit: "npm:^3.3.0"
|
||||
graphql-subscriptions: "npm:2.0.0"
|
||||
graphql-tag: "npm:^2.12.6"
|
||||
graphql-type-json: "npm:^0.3.2"
|
||||
@ -46274,6 +46452,21 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yup@npm:^0.32.0":
|
||||
version: 0.32.11
|
||||
resolution: "yup@npm:0.32.11"
|
||||
dependencies:
|
||||
"@babel/runtime": "npm:^7.15.4"
|
||||
"@types/lodash": "npm:^4.14.175"
|
||||
lodash: "npm:^4.17.21"
|
||||
lodash-es: "npm:^4.17.21"
|
||||
nanoclone: "npm:^0.2.1"
|
||||
property-expr: "npm:^2.0.4"
|
||||
toposort: "npm:^2.0.2"
|
||||
checksum: f0802798dc64b49f313886b983a9bea5f283e2094ee2aa1197587b84f50ac5b5d03af99857c313139e63dc02558fac3aaa343503bdbffa96f70006b39d1f59c9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"z-schema@npm:~5.0.2":
|
||||
version: 5.0.5
|
||||
resolution: "z-schema@npm:5.0.5"
|
||||
|
Loading…
Reference in New Issue
Block a user