diff --git a/packages/twenty-server/scripts/utils.ts b/packages/twenty-server/scripts/utils.ts index f944c4231..3b2bea525 100644 --- a/packages/twenty-server/scripts/utils.ts +++ b/packages/twenty-server/scripts/utils.ts @@ -2,16 +2,16 @@ import { ConfigService } from '@nestjs/config'; import console from 'console'; -import { config } from 'dotenv'; import { DataSource } from 'typeorm'; -config(); -const configService = new ConfigService(); +import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; + +const environmentService = new EnvironmentService(new ConfigService()); export const connectionSource = new DataSource({ type: 'postgres', logging: false, - url: configService.get('PG_DATABASE_URL'), + url: environmentService.get('PG_DATABASE_URL'), }); export const camelToSnakeCase = (str) => diff --git a/packages/twenty-server/src/command/command.ts b/packages/twenty-server/src/command/command.ts index bf1774bbe..b73377d6b 100644 --- a/packages/twenty-server/src/command/command.ts +++ b/packages/twenty-server/src/command/command.ts @@ -1,8 +1,11 @@ +import { ConfigService } from '@nestjs/config'; + import { CommandFactory } from 'nest-commander'; import { filterException } from 'src/engine/filters/utils/global-exception-handler.util'; import { ExceptionHandlerService } from 'src/engine/integrations/exception-handler/exception-handler.service'; import { LoggerService } from 'src/engine/integrations/logger/logger.service'; +import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; import { CommandModule } from './command.module'; @@ -17,8 +20,10 @@ async function bootstrap() { exceptionHandlerService.captureExceptions([err]); }; + const environmentService = new EnvironmentService(new ConfigService()); + const app = await CommandFactory.createWithoutRunning(CommandModule, { - bufferLogs: process.env.LOGGER_IS_BUFFER_ENABLED === 'true', + bufferLogs: environmentService.get('LOGGER_IS_BUFFER_ENABLED'), errorHandler, serviceErrorHandler: errorHandler, }); diff --git a/packages/twenty-server/src/database/typeorm/core/core.datasource.ts b/packages/twenty-server/src/database/typeorm/core/core.datasource.ts index c3187df96..0d2bbbcdd 100644 --- a/packages/twenty-server/src/database/typeorm/core/core.datasource.ts +++ b/packages/twenty-server/src/database/typeorm/core/core.datasource.ts @@ -7,7 +7,7 @@ config(); const configService = new ConfigService(); export const typeORMCoreModuleOptions: TypeOrmModuleOptions = { - url: configService.get('PG_DATABASE_URL'), + url: configService.get('PG_DATABASE_URL'), type: 'postgres', logging: ['error'], schema: 'core', diff --git a/packages/twenty-server/src/database/typeorm/metadata/metadata.datasource.ts b/packages/twenty-server/src/database/typeorm/metadata/metadata.datasource.ts index 94a65ab87..408f047be 100644 --- a/packages/twenty-server/src/database/typeorm/metadata/metadata.datasource.ts +++ b/packages/twenty-server/src/database/typeorm/metadata/metadata.datasource.ts @@ -7,7 +7,7 @@ config(); const configService = new ConfigService(); export const typeORMMetadataModuleOptions: TypeOrmModuleOptions = { - url: configService.get('PG_DATABASE_URL'), + url: configService.get('PG_DATABASE_URL'), type: 'postgres', logging: ['error'], schema: 'metadata', diff --git a/packages/twenty-server/src/engine/api/rest/api-rest.service.ts b/packages/twenty-server/src/engine/api/rest/api-rest.service.ts index f5306b55e..7d89c20f5 100644 --- a/packages/twenty-server/src/engine/api/rest/api-rest.service.ts +++ b/packages/twenty-server/src/engine/api/rest/api-rest.service.ts @@ -8,6 +8,7 @@ import { ApiRestQueryBuilderFactory } from 'src/engine/api/rest/api-rest-query-b import { TokenService } from 'src/engine/modules/auth/services/token.service'; import { ApiRestResponse } from 'src/engine/api/rest/types/api-rest-response.type'; import { ApiRestQuery } from 'src/engine/api/rest/types/api-rest-query.type'; +import { getServerUrl } from 'src/utils/get-server-url'; @Injectable() export class ApiRestService { @@ -22,7 +23,10 @@ export class ApiRestService { request: Request, data: ApiRestQuery, ): Promise { - const baseUrl = this.environmentService.getBaseUrl(request); + const baseUrl = getServerUrl( + request, + this.environmentService.get('SERVER_URL'), + ); try { return await this.httpService.axiosRef.post(`${baseUrl}/graphql`, data, { diff --git a/packages/twenty-server/src/engine/api/rest/metadata-rest.service.ts b/packages/twenty-server/src/engine/api/rest/metadata-rest.service.ts index 4b460236d..8a4b61be5 100644 --- a/packages/twenty-server/src/engine/api/rest/metadata-rest.service.ts +++ b/packages/twenty-server/src/engine/api/rest/metadata-rest.service.ts @@ -7,6 +7,7 @@ import { ApiRestQuery } from 'src/engine/api/rest/types/api-rest-query.type'; import { TokenService } from 'src/engine/modules/auth/services/token.service'; import { parseMetadataPath } from 'src/engine/api/rest/api-rest-query-builder/utils/parse-path.utils'; import { capitalize } from 'src/utils/capitalize'; +import { getServerUrl } from 'src/utils/get-server-url'; @Injectable() export class ApiRestMetadataService { @@ -18,7 +19,10 @@ export class ApiRestMetadataService { ) {} async callMetadata(request, data: ApiRestQuery) { - const baseUrl = this.environmentService.getBaseUrl(request); + const baseUrl = getServerUrl( + request, + this.environmentService.get('SERVER_URL'), + ); try { return await this.httpService.axiosRef.post(`${baseUrl}/metadata`, data, { diff --git a/packages/twenty-server/src/engine/integrations/environment/environment-variables.ts b/packages/twenty-server/src/engine/integrations/environment/environment-variables.ts index 028e8a319..9280550fc 100644 --- a/packages/twenty-server/src/engine/integrations/environment/environment-variables.ts +++ b/packages/twenty-server/src/engine/integrations/environment/environment-variables.ts @@ -21,6 +21,7 @@ import { ExceptionHandlerDriver } from 'src/engine/integrations/exception-handle import { StorageDriverType } from 'src/engine/integrations/file-storage/interfaces'; import { LoggerDriverType } from 'src/engine/integrations/logger/interfaces'; import { IsStrictlyLowerThan } from 'src/engine/integrations/environment/decorators/is-strictly-lower-than.decorator'; +import { MessageQueueDriverType } from 'src/engine/integrations/message-queue/interfaces'; import { IsDuration } from './decorators/is-duration.decorator'; import { AwsRegion } from './interfaces/aws-region.interface'; @@ -35,17 +36,17 @@ export class EnvironmentVariables { @CastToBoolean() @IsOptional() @IsBoolean() - DEBUG_MODE: boolean; + DEBUG_MODE: boolean = false; @CastToBoolean() @IsOptional() @IsBoolean() - SIGN_IN_PREFILLED: boolean; + SIGN_IN_PREFILLED: boolean = false; @CastToBoolean() @IsOptional() @IsBoolean() - IS_BILLING_ENABLED: boolean; + IS_BILLING_ENABLED: boolean = false; @IsString() @ValidateIf((env) => env.IS_BILLING_ENABLED === true) @@ -59,7 +60,7 @@ export class EnvironmentVariables { @CastToPositiveNumber() @IsOptional() @ValidateIf((env) => env.IS_BILLING_ENABLED === true) - BILLING_FREE_TRIAL_DURATION_IN_DAYS: number; + BILLING_FREE_TRIAL_DURATION_IN_DAYS: number = 7; @IsString() @ValidateIf((env) => env.IS_BILLING_ENABLED === true) @@ -72,17 +73,17 @@ export class EnvironmentVariables { @CastToBoolean() @IsOptional() @IsBoolean() - TELEMETRY_ENABLED: boolean; + TELEMETRY_ENABLED: boolean = true; @CastToBoolean() @IsOptional() @IsBoolean() - TELEMETRY_ANONYMIZATION_ENABLED: boolean; + TELEMETRY_ANONYMIZATION_ENABLED: boolean = true; @CastToPositiveNumber() @IsNumber() @IsOptional() - PORT: number; + PORT: number = 3000; // Database @IsDefined() @@ -108,25 +109,25 @@ export class EnvironmentVariables { @IsDuration() @IsOptional() - ACCESS_TOKEN_EXPIRES_IN: string; + ACCESS_TOKEN_EXPIRES_IN: string = '30m'; @IsString() REFRESH_TOKEN_SECRET: string; @IsDuration() @IsOptional() - REFRESH_TOKEN_EXPIRES_IN: string; + REFRESH_TOKEN_EXPIRES_IN: string = '30m'; @IsDuration() @IsOptional() - REFRESH_TOKEN_COOL_DOWN: string; + REFRESH_TOKEN_COOL_DOWN: string = '1m'; @IsString() - LOGIN_TOKEN_SECRET: string; + LOGIN_TOKEN_SECRET: string = '30m'; @IsDuration() @IsOptional() - LOGIN_TOKEN_EXPIRES_IN: string; + LOGIN_TOKEN_EXPIRES_IN: string = '15m'; // Auth @IsUrl({ require_tld: false }) @@ -136,7 +137,7 @@ export class EnvironmentVariables { @CastToBoolean() @IsOptional() @IsBoolean() - AUTH_GOOGLE_ENABLED: boolean; + AUTH_GOOGLE_ENABLED: boolean = false; @IsString() @ValidateIf((env) => env.AUTH_GOOGLE_ENABLED === true) @@ -153,7 +154,7 @@ export class EnvironmentVariables { // Storage @IsEnum(StorageDriverType) @IsOptional() - STORAGE_TYPE: StorageDriverType; + STORAGE_TYPE: StorageDriverType = StorageDriverType.Local; @ValidateIf((env) => env.STORAGE_TYPE === StorageDriverType.S3) @IsAWSRegion() @@ -170,12 +171,12 @@ export class EnvironmentVariables { @IsString() @ValidateIf((env) => env.STORAGE_TYPE === StorageDriverType.Local) - STORAGE_LOCAL_PATH: string; + STORAGE_LOCAL_PATH: string = '.local-storage'; // Support @IsEnum(SupportDriver) @IsOptional() - SUPPORT_DRIVER: SupportDriver; + SUPPORT_DRIVER: SupportDriver = SupportDriver.None; @ValidateIf((env) => env.SUPPORT_DRIVER === SupportDriver.Front) @IsString() @@ -187,19 +188,20 @@ export class EnvironmentVariables { @IsEnum(LoggerDriverType) @IsOptional() - LOGGER_DRIVER: LoggerDriverType; + LOGGER_DRIVER: LoggerDriverType = LoggerDriverType.Console; @IsEnum(ExceptionHandlerDriver) @IsOptional() - EXCEPTION_HANDLER_DRIVER: ExceptionHandlerDriver; + EXCEPTION_HANDLER_DRIVER: ExceptionHandlerDriver = + ExceptionHandlerDriver.Console; @CastToLogLevelArray() @IsOptional() - LOG_LEVELS: LogLevel[]; + LOG_LEVELS: LogLevel[] = ['log', 'error', 'warn']; @CastToStringArray() @IsOptional() - DEMO_WORKSPACE_IDS: string[]; + DEMO_WORKSPACE_IDS: string[] = []; @ValidateIf( (env) => env.EXCEPTION_HANDLER_DRIVER === ExceptionHandlerDriver.Sentry, @@ -209,7 +211,7 @@ export class EnvironmentVariables { @IsDuration() @IsOptional() - PASSWORD_RESET_TOKEN_EXPIRES_IN: string; + PASSWORD_RESET_TOKEN_EXPIRES_IN: string = '5m'; @CastToPositiveNumber() @IsNumber() @@ -219,48 +221,48 @@ export class EnvironmentVariables { '"WORKSPACE_INACTIVE_DAYS_BEFORE_NOTIFICATION" should be strictly lower that "WORKSPACE_INACTIVE_DAYS_BEFORE_DELETION"', }) @ValidateIf((env) => env.WORKSPACE_INACTIVE_DAYS_BEFORE_DELETION > 0) - WORKSPACE_INACTIVE_DAYS_BEFORE_NOTIFICATION: number; + WORKSPACE_INACTIVE_DAYS_BEFORE_NOTIFICATION: number = 30; @CastToPositiveNumber() @IsNumber() @ValidateIf((env) => env.WORKSPACE_INACTIVE_DAYS_BEFORE_NOTIFICATION > 0) - WORKSPACE_INACTIVE_DAYS_BEFORE_DELETION: number; + WORKSPACE_INACTIVE_DAYS_BEFORE_DELETION: number = 60; @CastToBoolean() @IsOptional() @IsBoolean() - IS_SIGN_UP_DISABLED: boolean; + IS_SIGN_UP_DISABLED: boolean = false; @CastToPositiveNumber() @IsOptional() @IsNumber() - MUTATION_MAXIMUM_RECORD_AFFECTED: number; + MUTATION_MAXIMUM_RECORD_AFFECTED: number = 100; - REDIS_HOST: string; + REDIS_HOST: string = '127.0.0.1'; - REDIS_PORT: number; + REDIS_PORT: number = 6379; - API_TOKEN_EXPIRES_IN: string; + API_TOKEN_EXPIRES_IN: string = '100y'; - SHORT_TERM_TOKEN_EXPIRES_IN: string; + SHORT_TERM_TOKEN_EXPIRES_IN: string = '5m'; - MESSAGING_PROVIDER_GMAIL_ENABLED: boolean; + MESSAGING_PROVIDER_GMAIL_ENABLED: boolean = false; MESSAGING_PROVIDER_GMAIL_CALLBACK_URL: string; - MESSAGE_QUEUE_TYPE: string; + MESSAGE_QUEUE_TYPE: string = MessageQueueDriverType.Sync; - EMAIL_FROM_ADDRESS: string; + EMAIL_FROM_ADDRESS: string = 'noreply@yourdomain.com'; - EMAIL_SYSTEM_ADDRESS: string; + EMAIL_SYSTEM_ADDRESS: string = 'system@yourdomain.com'; - EMAIL_FROM_NAME: string; + EMAIL_FROM_NAME: string = 'Felix from Twenty'; - EMAIL_DRIVER: EmailDriver; + EMAIL_DRIVER: EmailDriver = EmailDriver.Logger; EMAIL_SMTP_HOST: string; - EMAIL_SMTP_PORT: number; + EMAIL_SMTP_PORT: number = 587; EMAIL_SMTP_USER: string; @@ -268,15 +270,19 @@ export class EnvironmentVariables { OPENROUTER_API_KEY: string; - API_RATE_LIMITING_TTL: number; + API_RATE_LIMITING_TTL: number = 100; - API_RATE_LIMITING_LIMIT: number; + API_RATE_LIMITING_LIMIT: number = 500; - CACHE_STORAGE_TYPE: string; + CACHE_STORAGE_TYPE: string = 'memory'; + + CACHE_STORAGE_TTL: number = 3600 * 24 * 7; + + CALENDAR_PROVIDER_GOOGLE_ENABLED: boolean = false; - CACHE_STORAGE_TTL: number; - CALENDAR_PROVIDER_GOOGLE_ENABLED: boolean; AUTH_GOOGLE_APIS_CALLBACK_URL: string; + + LOGGER_IS_BUFFER_ENABLED: boolean = true; } export const validate = (config: Record) => { diff --git a/packages/twenty-server/src/engine/integrations/environment/environment.default.ts b/packages/twenty-server/src/engine/integrations/environment/environment.default.ts deleted file mode 100644 index 9c724584d..000000000 --- a/packages/twenty-server/src/engine/integrations/environment/environment.default.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { EmailDriver } from 'src/engine/integrations/email/interfaces/email.interface'; -import { SupportDriver } from 'src/engine/integrations/environment/interfaces/support.interface'; - -import { ExceptionHandlerDriver } from 'src/engine/integrations/exception-handler/interfaces'; -import { StorageDriverType } from 'src/engine/integrations/file-storage/interfaces'; -import { LoggerDriverType } from 'src/engine/integrations/logger/interfaces'; -import { MessageQueueDriverType } from 'src/engine/integrations/message-queue/interfaces'; -import { EnvironmentVariables } from 'src/engine/integrations/environment/environment-variables'; - -const EnvironmentDefault = new EnvironmentVariables(); - -EnvironmentDefault.DEBUG_MODE = false; -EnvironmentDefault.SIGN_IN_PREFILLED = false; -EnvironmentDefault.IS_BILLING_ENABLED = false; -EnvironmentDefault.BILLING_PLAN_REQUIRED_LINK = ''; -EnvironmentDefault.BILLING_STRIPE_BASE_PLAN_PRODUCT_ID = ''; -EnvironmentDefault.BILLING_FREE_TRIAL_DURATION_IN_DAYS = 7; -EnvironmentDefault.BILLING_STRIPE_API_KEY = ''; -EnvironmentDefault.BILLING_STRIPE_WEBHOOK_SECRET = ''; -EnvironmentDefault.TELEMETRY_ENABLED = true; -EnvironmentDefault.TELEMETRY_ANONYMIZATION_ENABLED = true; -EnvironmentDefault.PORT = 3000; -EnvironmentDefault.REDIS_HOST = '127.0.0.1'; -EnvironmentDefault.REDIS_PORT = 6379; -EnvironmentDefault.PG_DATABASE_URL = ''; -EnvironmentDefault.FRONT_BASE_URL = ''; -EnvironmentDefault.SERVER_URL = ''; -EnvironmentDefault.ACCESS_TOKEN_SECRET = 'random_string'; -EnvironmentDefault.ACCESS_TOKEN_EXPIRES_IN = '30m'; -EnvironmentDefault.REFRESH_TOKEN_SECRET = 'random_string'; -EnvironmentDefault.REFRESH_TOKEN_EXPIRES_IN = '30m'; -EnvironmentDefault.REFRESH_TOKEN_COOL_DOWN = '1m'; -EnvironmentDefault.LOGIN_TOKEN_SECRET = 'random_string'; -EnvironmentDefault.LOGIN_TOKEN_EXPIRES_IN = '30m'; -EnvironmentDefault.API_TOKEN_EXPIRES_IN = '100y'; -EnvironmentDefault.SHORT_TERM_TOKEN_EXPIRES_IN = '5m'; -EnvironmentDefault.FRONT_AUTH_CALLBACK_URL = ''; -EnvironmentDefault.MESSAGING_PROVIDER_GMAIL_ENABLED = false; -EnvironmentDefault.MESSAGING_PROVIDER_GMAIL_CALLBACK_URL = ''; -EnvironmentDefault.AUTH_GOOGLE_ENABLED = false; -EnvironmentDefault.AUTH_GOOGLE_CLIENT_ID = ''; -EnvironmentDefault.AUTH_GOOGLE_CLIENT_SECRET = ''; -EnvironmentDefault.AUTH_GOOGLE_CALLBACK_URL = ''; -EnvironmentDefault.STORAGE_TYPE = StorageDriverType.Local; -EnvironmentDefault.STORAGE_S3_REGION = 'aws-east-1'; -EnvironmentDefault.STORAGE_S3_NAME = ''; -EnvironmentDefault.STORAGE_S3_ENDPOINT = ''; -EnvironmentDefault.STORAGE_LOCAL_PATH = '.local-storage'; -EnvironmentDefault.MESSAGE_QUEUE_TYPE = MessageQueueDriverType.Sync; -EnvironmentDefault.EMAIL_FROM_ADDRESS = 'noreply@yourdomain.com'; -EnvironmentDefault.EMAIL_SYSTEM_ADDRESS = 'system@yourdomain.com'; -EnvironmentDefault.EMAIL_FROM_NAME = 'John from Twenty'; -EnvironmentDefault.EMAIL_DRIVER = EmailDriver.Logger; -EnvironmentDefault.EMAIL_SMTP_HOST = ''; -EnvironmentDefault.EMAIL_SMTP_PORT = 587; -EnvironmentDefault.EMAIL_SMTP_USER = ''; -EnvironmentDefault.EMAIL_SMTP_PASSWORD = ''; -EnvironmentDefault.SUPPORT_DRIVER = SupportDriver.None; -EnvironmentDefault.SUPPORT_FRONT_CHAT_ID = ''; -EnvironmentDefault.SUPPORT_FRONT_HMAC_KEY = ''; -EnvironmentDefault.LOGGER_DRIVER = LoggerDriverType.Console; -EnvironmentDefault.EXCEPTION_HANDLER_DRIVER = ExceptionHandlerDriver.Console; -EnvironmentDefault.LOG_LEVELS = ['log', 'error', 'warn']; -EnvironmentDefault.SENTRY_DSN = ''; -EnvironmentDefault.DEMO_WORKSPACE_IDS = []; -EnvironmentDefault.OPENROUTER_API_KEY = ''; -EnvironmentDefault.PASSWORD_RESET_TOKEN_EXPIRES_IN = '5m'; -EnvironmentDefault.WORKSPACE_INACTIVE_DAYS_BEFORE_NOTIFICATION = 30; -EnvironmentDefault.WORKSPACE_INACTIVE_DAYS_BEFORE_DELETION = 60; -EnvironmentDefault.IS_SIGN_UP_DISABLED = false; -EnvironmentDefault.API_RATE_LIMITING_TTL = 100; -EnvironmentDefault.API_RATE_LIMITING_LIMIT = 500; -EnvironmentDefault.MUTATION_MAXIMUM_RECORD_AFFECTED = 100; -EnvironmentDefault.CACHE_STORAGE_TYPE = 'memory'; -EnvironmentDefault.CACHE_STORAGE_TTL = 3600 * 24 * 7; - -export { EnvironmentDefault }; diff --git a/packages/twenty-server/src/engine/integrations/environment/environment.service.ts b/packages/twenty-server/src/engine/integrations/environment/environment.service.ts index 275671a05..844024955 100644 --- a/packages/twenty-server/src/engine/integrations/environment/environment.service.ts +++ b/packages/twenty-server/src/engine/integrations/environment/environment.service.ts @@ -2,47 +2,16 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { Request } from 'express'; - import { EnvironmentVariables } from 'src/engine/integrations/environment/environment-variables'; -import { EnvironmentDefault } from 'src/engine/integrations/environment/environment.default'; @Injectable() export class EnvironmentService { - constructor(private configService: ConfigService) {} + constructor(private readonly configService: ConfigService) {} get(key: T): EnvironmentVariables[T] { - return ( - this.configService.get(key) ?? - EnvironmentDefault[key] + return this.configService.get( + key, + new EnvironmentVariables()[key], ); } - - getServerUrl(): string { - const url = this.configService.get('SERVER_URL')!; - - if (url?.endsWith('/')) { - return url.substring(0, url.length - 1); - } - - return url; - } - - getBaseUrl(request: Request): string { - return ( - this.getServerUrl() || `${request.protocol}://${request.get('host')}` - ); - } - - getFrontAuthCallbackUrl(): string { - return ( - this.configService.get('FRONT_AUTH_CALLBACK_URL') ?? - this.get('FRONT_BASE_URL') + '/verify' - ); - } - - // TODO: check because it isn't called - getLoggerIsBufferEnabled(): boolean | undefined { - return this.configService.get('LOGGER_IS_BUFFER_ENABLED') ?? true; - } } diff --git a/packages/twenty-server/src/engine/modules/auth/services/token.service.ts b/packages/twenty-server/src/engine/modules/auth/services/token.service.ts index 30a573be4..72aa8fc83 100644 --- a/packages/twenty-server/src/engine/modules/auth/services/token.service.ts +++ b/packages/twenty-server/src/engine/modules/auth/services/token.service.ts @@ -361,7 +361,9 @@ export class TokenService { } computeRedirectURI(loginToken: string): string { - return `${this.environmentService.getFrontAuthCallbackUrl()}?loginToken=${loginToken}`; + return `${this.environmentService.get( + 'FRONT_BASE_URL', + )}/verify?loginToken=${loginToken}`; } async verifyJwt(token: string, secret?: string) { diff --git a/packages/twenty-server/src/engine/modules/client-config/client-config.entity.ts b/packages/twenty-server/src/engine/modules/client-config/client-config.entity.ts index 57a719acc..184c70047 100644 --- a/packages/twenty-server/src/engine/modules/client-config/client-config.entity.ts +++ b/packages/twenty-server/src/engine/modules/client-config/client-config.entity.ts @@ -26,11 +26,11 @@ class Billing { @Field(() => Boolean) isBillingEnabled: boolean; - @Field(() => String) - billingUrl: string; + @Field(() => String, { nullable: true }) + billingUrl?: string; @Field(() => Number, { nullable: true }) - billingFreeTrialDurationInDays: number | undefined; + billingFreeTrialDurationInDays?: number; } @ObjectType() @@ -39,13 +39,13 @@ class Support { supportDriver: string; @Field(() => String, { nullable: true }) - supportFrontChatId: string | undefined; + supportFrontChatId?: string; } @ObjectType() class Sentry { @Field(() => String, { nullable: true }) - dsn: string | undefined; + dsn?: string; } @ObjectType() diff --git a/packages/twenty-server/src/engine/modules/open-api/open-api.service.ts b/packages/twenty-server/src/engine/modules/open-api/open-api.service.ts index 40f5378e8..bc0b9d388 100644 --- a/packages/twenty-server/src/engine/modules/open-api/open-api.service.ts +++ b/packages/twenty-server/src/engine/modules/open-api/open-api.service.ts @@ -26,6 +26,7 @@ import { } from 'src/engine/modules/open-api/utils/responses.utils'; import { getRequestBody } from 'src/engine/modules/open-api/utils/request-body.utils'; import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; +import { getServerUrl } from 'src/utils/get-server-url'; @Injectable() export class OpenApiService { @@ -36,7 +37,10 @@ export class OpenApiService { ) {} async generateCoreSchema(request: Request): Promise { - const baseUrl = this.environmentService.getBaseUrl(request); + const baseUrl = getServerUrl( + request, + this.environmentService.get('SERVER_URL'), + ); const schema = baseSchema('core', baseUrl); @@ -93,7 +97,10 @@ export class OpenApiService { async generateMetaDataSchema( request: Request, ): Promise { - const baseUrl = this.environmentService.getBaseUrl(request); + const baseUrl = getServerUrl( + request, + this.environmentService.get('SERVER_URL'), + ); const schema = baseSchema('metadata', baseUrl); diff --git a/packages/twenty-server/src/main.ts b/packages/twenty-server/src/main.ts index 80b0543e4..5e7913012 100644 --- a/packages/twenty-server/src/main.ts +++ b/packages/twenty-server/src/main.ts @@ -1,6 +1,7 @@ import { NestFactory } from '@nestjs/core'; import { ValidationPipe } from '@nestjs/common'; import { NestExpressApplication } from '@nestjs/platform-express'; +import { ConfigService } from '@nestjs/config'; import * as Sentry from '@sentry/node'; import { graphqlUploadExpress } from 'graphql-upload'; @@ -15,9 +16,11 @@ import { LoggerService } from './engine/integrations/logger/logger.service'; import { EnvironmentService } from './engine/integrations/environment/environment.service'; const bootstrap = async () => { + const environmentService = new EnvironmentService(new ConfigService()); + const app = await NestFactory.create(AppModule, { cors: true, - bufferLogs: process.env.LOGGER_IS_BUFFER_ENABLED === 'true', + bufferLogs: environmentService.get('LOGGER_IS_BUFFER_ENABLED'), rawBody: true, }); const logger = app.get(LoggerService); diff --git a/packages/twenty-server/src/queue-worker/queue-worker.ts b/packages/twenty-server/src/queue-worker/queue-worker.ts index 6218a488e..eae85670f 100644 --- a/packages/twenty-server/src/queue-worker/queue-worker.ts +++ b/packages/twenty-server/src/queue-worker/queue-worker.ts @@ -1,4 +1,5 @@ import { NestFactory } from '@nestjs/core'; +import { ConfigService } from '@nestjs/config'; import { MessageQueueJob, @@ -13,14 +14,17 @@ import { MessageQueue } from 'src/engine/integrations/message-queue/message-queu import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service'; import { getJobClassName } from 'src/engine/integrations/message-queue/utils/get-job-class-name.util'; import { QueueWorkerModule } from 'src/queue-worker/queue-worker.module'; +import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; async function bootstrap() { let exceptionHandlerService: ExceptionHandlerService | undefined; let loggerService: LoggerService | undefined; try { + const environmentService = new EnvironmentService(new ConfigService()); + const app = await NestFactory.createApplicationContext(QueueWorkerModule, { - bufferLogs: process.env.LOGGER_IS_BUFFER_ENABLED === 'true', + bufferLogs: environmentService.get('LOGGER_IS_BUFFER_ENABLED'), }); loggerService = app.get(LoggerService); diff --git a/packages/twenty-server/src/utils/get-server-url.ts b/packages/twenty-server/src/utils/get-server-url.ts new file mode 100644 index 000000000..2ab7be3be --- /dev/null +++ b/packages/twenty-server/src/utils/get-server-url.ts @@ -0,0 +1,11 @@ +import { Request } from 'express'; + +export const getServerUrl = ( + request: Request, + serverUrlEnv: string, +): string => { + if (serverUrlEnv?.endsWith('/')) + return serverUrlEnv.substring(0, serverUrlEnv.length - 1); + + return serverUrlEnv || `${request.protocol}://${request.get('host')}`; +};