diff --git a/packages/twenty-docs/docs/start/self-hosting/self-hosting.mdx b/packages/twenty-docs/docs/start/self-hosting/self-hosting.mdx
index c901345a8e..c96947b02f 100644
--- a/packages/twenty-docs/docs/start/self-hosting/self-hosting.mdx
+++ b/packages/twenty-docs/docs/start/self-hosting/self-hosting.mdx
@@ -43,6 +43,12 @@ import TabItem from '@theme/TabItem';
['PORT', '3000', 'Port'],
]}>
+### Security
+
+
### Tokens
({
driver: YogaDriver,
- imports: [CoreModule],
+ imports: [CoreModule, GraphQLConfigModule],
useClass: GraphQLConfigService,
}),
HealthModule,
diff --git a/packages/twenty-server/src/graphql-config/factories/create-context.factory.ts b/packages/twenty-server/src/graphql-config/factories/create-context.factory.ts
new file mode 100644
index 0000000000..1186898cc7
--- /dev/null
+++ b/packages/twenty-server/src/graphql-config/factories/create-context.factory.ts
@@ -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 {
+ // 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;
+ }
+}
diff --git a/packages/twenty-server/src/graphql-config/factories/index.ts b/packages/twenty-server/src/graphql-config/factories/index.ts
new file mode 100644
index 0000000000..c81ec577e8
--- /dev/null
+++ b/packages/twenty-server/src/graphql-config/factories/index.ts
@@ -0,0 +1,3 @@
+import { CreateContextFactory } from './create-context.factory';
+
+export const graphQLFactories = [CreateContextFactory];
diff --git a/packages/twenty-server/src/graphql-config/graphql-config.module.ts b/packages/twenty-server/src/graphql-config/graphql-config.module.ts
new file mode 100644
index 0000000000..64290089e9
--- /dev/null
+++ b/packages/twenty-server/src/graphql-config/graphql-config.module.ts
@@ -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 {}
diff --git a/packages/twenty-server/src/graphql-config.service.ts b/packages/twenty-server/src/graphql-config/graphql-config.service.ts
similarity index 82%
rename from packages/twenty-server/src/graphql-config.service.ts
rename to packages/twenty-server/src/graphql-config/graphql-config.service.ts
index 46d62c1a82..9fddf661d3 100644
--- a/packages/twenty-server/src/graphql-config.service.ts
+++ b/packages/twenty-server/src/graphql-config/graphql-config.service.ts
@@ -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>
{
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,
}),
],
};
diff --git a/packages/twenty-server/src/graphql-config/interfaces/graphql-context.interface.ts b/packages/twenty-server/src/graphql-config/interfaces/graphql-context.interface.ts
new file mode 100644
index 0000000000..ab5a5d3c83
--- /dev/null
+++ b/packages/twenty-server/src/graphql-config/interfaces/graphql-context.interface.ts
@@ -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;
+}
diff --git a/packages/twenty-server/src/integrations/environment/environment.service.ts b/packages/twenty-server/src/integrations/environment/environment.service.ts
index 3840df1da1..c55f409106 100644
--- a/packages/twenty-server/src/integrations/environment/environment.service.ts
+++ b/packages/twenty-server/src/integrations/environment/environment.service.ts
@@ -287,4 +287,12 @@ export class EnvironmentService {
isSignUpDisabled(): boolean {
return this.configService.get('IS_SIGN_UP_DISABLED') ?? false;
}
+
+ getApiRateLimitingTtl(): number {
+ return this.configService.get('API_RATE_LIMITING_TTL') ?? 100;
+ }
+
+ getApiRateLimitingLimit(): number {
+ return this.configService.get('API_RATE_LIMITING_LIMIT') ?? 200;
+ }
}
diff --git a/packages/twenty-server/src/integrations/exception-handler/hooks/use-exception-handler.hook.ts b/packages/twenty-server/src/integrations/exception-handler/hooks/use-exception-handler.hook.ts
index df39db28ee..f258d1666b 100644
--- a/packages/twenty-server/src/integrations/exception-handler/hooks/use-exception-handler.hook.ts
+++ b/packages/twenty-server/src/integrations/exception-handler/hooks/use-exception-handler.hook.ts
@@ -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 = object,
->(
+export const useExceptionHandler = (
options: ExceptionHandlerPluginOptions,
): Plugin => {
- 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) {
diff --git a/packages/twenty-server/src/integrations/throttler/hooks/use-throttler.ts b/packages/twenty-server/src/integrations/throttler/hooks/use-throttler.ts
new file mode 100644
index 0000000000..6c549d532a
--- /dev/null
+++ b/packages/twenty-server/src/integrations/throttler/hooks/use-throttler.ts
@@ -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 = (
+ 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;
+}
+
+export const useThrottler = (
+ options: ThrottlerPluginOptions,
+): Plugin => {
+ 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()]);
+}
diff --git a/packages/twenty-server/src/metadata/metadata.module-factory.ts b/packages/twenty-server/src/metadata/metadata.module-factory.ts
index 8e76867d11..7dd0dafede 100644
--- a/packages/twenty-server/src/metadata/metadata.module-factory.ts
+++ b/packages/twenty-server/src/metadata/metadata.module-factory.ts
@@ -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 => {
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',
diff --git a/packages/twenty-server/src/metadata/metadata.module.ts b/packages/twenty-server/src/metadata/metadata.module.ts
index ff7fac4c37..19bade105b 100644
--- a/packages/twenty-server/src/metadata/metadata.module.ts
+++ b/packages/twenty-server/src/metadata/metadata.module.ts
@@ -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({
driver: YogaDriver,
useFactory: metadataModuleFactory,
- imports: [AuthModule],
- inject: [EnvironmentService, ExceptionHandlerService, TokenService],
+ imports: [GraphQLConfigModule],
+ inject: [
+ EnvironmentService,
+ ExceptionHandlerService,
+ CreateContextFactory,
+ ],
}),
DataSourceModule,
FieldMetadataModule,
diff --git a/yarn.lock b/yarn.lock
index 12d260d735..c530d727ec 100644
--- a/yarn.lock
+++ b/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"