diff --git a/packages/twenty-docker/docker-compose.yml b/packages/twenty-docker/docker-compose.yml index 04cd1c753a9..627808948cc 100644 --- a/packages/twenty-docker/docker-compose.yml +++ b/packages/twenty-docker/docker-compose.yml @@ -38,7 +38,6 @@ services: # EMAIL_FROM_ADDRESS: ${EMAIL_FROM_ADDRESS:-contact@yourdomain.com} # EMAIL_FROM_NAME: ${EMAIL_FROM_NAME:-"John from YourDomain"} - # EMAIL_SYSTEM_ADDRESS: ${EMAIL_SYSTEM_ADDRESS:-system@yourdomain.com} # EMAIL_DRIVER: ${EMAIL_DRIVER:-smtp} # EMAIL_SMTP_HOST: ${EMAIL_SMTP_HOST:-smtp.gmail.com} # EMAIL_SMTP_PORT: ${EMAIL_SMTP_PORT:-465} @@ -92,7 +91,6 @@ services: # EMAIL_FROM_ADDRESS: ${EMAIL_FROM_ADDRESS:-contact@yourdomain.com} # EMAIL_FROM_NAME: ${EMAIL_FROM_NAME:-"John from YourDomain"} - # EMAIL_SYSTEM_ADDRESS: ${EMAIL_SYSTEM_ADDRESS:-system@yourdomain.com} # EMAIL_DRIVER: ${EMAIL_DRIVER:-smtp} # EMAIL_SMTP_HOST: ${EMAIL_SMTP_HOST:-smtp.gmail.com} # EMAIL_SMTP_PORT: ${EMAIL_SMTP_PORT:-465} diff --git a/packages/twenty-docker/helm/twenty/values.schema.json b/packages/twenty-docker/helm/twenty/values.schema.json index 523042bfab5..ef75efc6d66 100644 --- a/packages/twenty-docker/helm/twenty/values.schema.json +++ b/packages/twenty-docker/helm/twenty/values.schema.json @@ -61,7 +61,6 @@ "EMAIL_SMTP_NO_TLS": { "type": "boolean" }, "EMAIL_FROM_ADDRESS": { "type": "string" }, "EMAIL_FROM_NAME": { "type": "string" }, - "EMAIL_SYSTEM_ADDRESS": { "type": "string" }, "IS_EMAIL_VERIFICATION_REQUIRED": { "type": "boolean" }, "EMAIL_VERIFICATION_TOKEN_EXPIRES_IN": { "type": "string" }, "PASSWORD_RESET_TOKEN_EXPIRES_IN": { "type": "string" } diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index f9f532d00b6..c86036bf70f 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -813,7 +813,6 @@ export type ClientConfig = { calendarBookingPageId?: Maybe; canManageFeatureFlags: Scalars['Boolean']; captcha: Captcha; - chromeExtensionId?: Maybe; defaultSubdomain?: Maybe; frontDomain: Scalars['String']; isAttachmentPreviewEnabled: Scalars['Boolean']; @@ -898,6 +897,7 @@ export type ConfigVariables = { }; export enum ConfigVariablesGroup { + ADVANCED_SETTINGS = 'ADVANCED_SETTINGS', ANALYTICS_CONFIG = 'ANALYTICS_CONFIG', AWS_SES_SETTINGS = 'AWS_SES_SETTINGS', BILLING_CONFIG = 'BILLING_CONFIG', @@ -905,21 +905,17 @@ export enum ConfigVariablesGroup { CLOUDFLARE_CONFIG = 'CLOUDFLARE_CONFIG', CODE_INTERPRETER_CONFIG = 'CODE_INTERPRETER_CONFIG', EMAIL_SETTINGS = 'EMAIL_SETTINGS', - EXCEPTION_HANDLER = 'EXCEPTION_HANDLER', GOOGLE_AUTH = 'GOOGLE_AUTH', LLM = 'LLM', LOGGING = 'LOGGING', LOGIC_FUNCTION_CONFIG = 'LOGIC_FUNCTION_CONFIG', - METERING = 'METERING', MICROSOFT_AUTH = 'MICROSOFT_AUTH', - OTHER = 'OTHER', RATE_LIMITING = 'RATE_LIMITING', SERVER_CONFIG = 'SERVER_CONFIG', SSL = 'SSL', STORAGE_CONFIG = 'STORAGE_CONFIG', SUPPORT_CHAT_CONFIG = 'SUPPORT_CHAT_CONFIG', - TOKENS_DURATION = 'TOKENS_DURATION', - TWO_FACTOR_AUTHENTICATION = 'TWO_FACTOR_AUTHENTICATION' + TOKENS_DURATION = 'TOKENS_DURATION' } export type ConfigVariablesGroupData = { diff --git a/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx b/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx index 34fded19dc2..1fbb47380d5 100644 --- a/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx +++ b/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx @@ -8,8 +8,6 @@ import { GotoHotkeysEffectsProvider } from '@/app/effect-components/GotoHotkeysE import { PageChangeEffect } from '@/app/effect-components/PageChangeEffect'; import { AuthProvider } from '@/auth/components/AuthProvider'; import { CaptchaProvider } from '@/captcha/components/CaptchaProvider'; -import { ChromeExtensionSidecarEffect } from '@/chrome-extension-sidecar/components/ChromeExtensionSidecarEffect'; -import { ChromeExtensionSidecarProvider } from '@/chrome-extension-sidecar/components/ChromeExtensionSidecarProvider'; import { ClientConfigProvider } from '@/client-config/components/ClientConfigProvider'; import { ClientConfigProviderEffect } from '@/client-config/components/ClientConfigProviderEffect'; import { MainContextStoreProvider } from '@/context-store/components/MainContextStoreProvider'; @@ -51,45 +49,42 @@ export const AppRouterProviders = () => { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/twenty-front/src/modules/chrome-extension-sidecar/components/ChromeExtensionSidecarEffect.tsx b/packages/twenty-front/src/modules/chrome-extension-sidecar/components/ChromeExtensionSidecarEffect.tsx deleted file mode 100644 index 63db20737ca..00000000000 --- a/packages/twenty-front/src/modules/chrome-extension-sidecar/components/ChromeExtensionSidecarEffect.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { tokenPairState } from '@/auth/states/tokenPairState'; -import { isLoadingTokensFromExtensionState } from '@/chrome-extension-sidecar/states/isLoadingTokensFromExtensionState'; -import { chromeExtensionIdState } from '@/client-config/states/chromeExtensionIdState'; -import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; -import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; -import { useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { isDefined } from 'twenty-shared/utils'; -import { isInFrame } from '~/utils/isInIframe'; - -export const ChromeExtensionSidecarEffect = () => { - const navigate = useNavigate(); - const setTokenPair = useSetAtomState(tokenPairState); - const chromeExtensionId = useAtomStateValue(chromeExtensionIdState); - const setIsLoadingTokensFromExtension = useSetAtomState( - isLoadingTokensFromExtensionState, - ); - - useEffect(() => { - if (isInFrame() && isDefined(chromeExtensionId)) { - window.parent.postMessage( - 'loaded', - `chrome-extension://${chromeExtensionId}`, - ); - - const handleWindowEvents = (event: MessageEvent) => { - if (event.origin === `chrome-extension://${chromeExtensionId}`) { - switch (event.data.type) { - case 'tokens': { - setTokenPair(event.data.value); - setIsLoadingTokensFromExtension(true); - break; - } - case 'navigate': - navigate(event.data.value); - break; - default: - break; - } - } else { - setIsLoadingTokensFromExtension(false); - return; - } - }; - window.addEventListener('message', handleWindowEvents); - return () => { - window.removeEventListener('message', handleWindowEvents); - }; - } - }, [ - chromeExtensionId, - setIsLoadingTokensFromExtension, - setTokenPair, - navigate, - ]); - - return <>; -}; diff --git a/packages/twenty-front/src/modules/chrome-extension-sidecar/components/ChromeExtensionSidecarProvider.tsx b/packages/twenty-front/src/modules/chrome-extension-sidecar/components/ChromeExtensionSidecarProvider.tsx deleted file mode 100644 index 9409540ead2..00000000000 --- a/packages/twenty-front/src/modules/chrome-extension-sidecar/components/ChromeExtensionSidecarProvider.tsx +++ /dev/null @@ -1,46 +0,0 @@ -// const StyledContainer = styled.div` -// align-items: center; -// display: flex; -// flex-direction: column; -// height: 100vh; -// justify-content: center; -// `; - -// const AppInaccessible = ({ message }: { message: string }) => { -// return ( -// -// twenty-icon -//

{message}

-//
-// ); -// }; - -export const ChromeExtensionSidecarProvider: React.FC< - React.PropsWithChildren -> = ({ children }) => { - return <>{children}; - - // TODO: this is conflictting with storybook tests - // if (!isInFrame()) return <>{children}; - - // if (!isDefined(chromeExtensionId)) - // return ( - // - // ); - - // if (isDefined(isLoadingTokensFromExtension) && !isLoadingTokensFromExtension) - // return ( - // - // ); - - // return isLoadingTokensFromExtension && <>{children}; -}; diff --git a/packages/twenty-front/src/modules/chrome-extension-sidecar/states/isLoadingTokensFromExtensionState.ts b/packages/twenty-front/src/modules/chrome-extension-sidecar/states/isLoadingTokensFromExtensionState.ts deleted file mode 100644 index fb92881de77..00000000000 --- a/packages/twenty-front/src/modules/chrome-extension-sidecar/states/isLoadingTokensFromExtensionState.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState'; -export const isLoadingTokensFromExtensionState = createAtomState< - boolean | null ->({ - key: 'isLoadingTokensFromExtensionState', - defaultValue: null, -}); diff --git a/packages/twenty-front/src/modules/client-config/hooks/useClientConfig.ts b/packages/twenty-front/src/modules/client-config/hooks/useClientConfig.ts index 329771c28cf..efd8970b613 100644 --- a/packages/twenty-front/src/modules/client-config/hooks/useClientConfig.ts +++ b/packages/twenty-front/src/modules/client-config/hooks/useClientConfig.ts @@ -6,7 +6,6 @@ import { billingState } from '@/client-config/states/billingState'; import { calendarBookingPageIdState } from '@/client-config/states/calendarBookingPageIdState'; import { canManageFeatureFlagsState } from '@/client-config/states/canManageFeatureFlagsState'; import { captchaState } from '@/client-config/states/captchaState'; -import { chromeExtensionIdState } from '@/client-config/states/chromeExtensionIdState'; import { isAnalyticsEnabledState } from '@/client-config/states/isAnalyticsEnabledState'; import { isAttachmentPreviewEnabledState } from '@/client-config/states/isAttachmentPreviewEnabledState'; import { isConfigVariablesInDbEnabledState } from '@/client-config/states/isConfigVariablesInDbEnabledState'; @@ -67,8 +66,6 @@ export const useClientConfig = (): UseClientConfigResult => { const setCaptcha = useSetAtomState(captchaState); - const setChromeExtensionId = useSetAtomState(chromeExtensionIdState); - const setApiConfig = useSetAtomState(apiConfigState); const setCanManageFeatureFlags = useSetAtomState(canManageFeatureFlagsState); @@ -170,7 +167,6 @@ export const useClientConfig = (): UseClientConfigResult => { siteKey: clientConfig?.captcha?.siteKey, }); - setChromeExtensionId(clientConfig?.chromeExtensionId); setApiConfig(clientConfig?.api); setDomainConfiguration({ defaultSubdomain: clientConfig?.defaultSubdomain, @@ -219,7 +215,6 @@ export const useClientConfig = (): UseClientConfigResult => { setCalendarBookingPageId, setCanManageFeatureFlags, setCaptcha, - setChromeExtensionId, setClientConfigApiStatus, setDomainConfiguration, setIsGoogleCalendarEnabled, diff --git a/packages/twenty-front/src/modules/client-config/states/chromeExtensionIdState.ts b/packages/twenty-front/src/modules/client-config/states/chromeExtensionIdState.ts deleted file mode 100644 index 743e8fb0fa1..00000000000 --- a/packages/twenty-front/src/modules/client-config/states/chromeExtensionIdState.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState'; -export const chromeExtensionIdState = createAtomState< - string | null | undefined ->({ - key: 'chromeExtensionIdState', - defaultValue: null, -}); diff --git a/packages/twenty-front/src/modules/client-config/types/ClientConfig.ts b/packages/twenty-front/src/modules/client-config/types/ClientConfig.ts index 3e589dcae9d..4a6ad3884cc 100644 --- a/packages/twenty-front/src/modules/client-config/types/ClientConfig.ts +++ b/packages/twenty-front/src/modules/client-config/types/ClientConfig.ts @@ -19,7 +19,6 @@ export type ClientConfig = { calendarBookingPageId?: string; canManageFeatureFlags: boolean; captcha: Captcha; - chromeExtensionId?: string; defaultSubdomain?: string; frontDomain: string; isAttachmentPreviewEnabled: boolean; diff --git a/packages/twenty-front/src/modules/client-config/utils/__tests__/clientConfigUtils.test.ts b/packages/twenty-front/src/modules/client-config/utils/__tests__/clientConfigUtils.test.ts index 6887a0f7a5a..857f69a4d6a 100644 --- a/packages/twenty-front/src/modules/client-config/utils/__tests__/clientConfigUtils.test.ts +++ b/packages/twenty-front/src/modules/client-config/utils/__tests__/clientConfigUtils.test.ts @@ -34,7 +34,6 @@ const mockClientConfig = { provider: undefined, siteKey: undefined, }, - chromeExtensionId: undefined, api: { mutationMaximumAffectedRecords: 100, }, diff --git a/packages/twenty-front/src/testing/mock-data/config.ts b/packages/twenty-front/src/testing/mock-data/config.ts index 0672401277d..7e75ac15a58 100644 --- a/packages/twenty-front/src/testing/mock-data/config.ts +++ b/packages/twenty-front/src/testing/mock-data/config.ts @@ -15,7 +15,6 @@ export const mockedClientConfig: ClientConfig = { }, frontDomain: 'localhost', defaultSubdomain: 'app', - chromeExtensionId: 'MOCKED_EXTENSION_ID', analyticsEnabled: true, support: { supportDriver: SupportDriver.FRONT, diff --git a/packages/twenty-front/src/utils/__tests__/isInIframe.test.ts b/packages/twenty-front/src/utils/__tests__/isInIframe.test.ts deleted file mode 100644 index c10cdb3034a..00000000000 --- a/packages/twenty-front/src/utils/__tests__/isInIframe.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { isInFrame } from '~/utils/isInIframe'; - -describe('isInFrame', () => { - it('should return a boolean value', () => { - const result = isInFrame(); - expect(typeof result).toBe('boolean'); - }); - - it('should not throw an error when called', () => { - expect(() => isInFrame()).not.toThrow(); - }); -}); diff --git a/packages/twenty-front/src/utils/isInIframe.ts b/packages/twenty-front/src/utils/isInIframe.ts deleted file mode 100644 index 797c6a8e018..00000000000 --- a/packages/twenty-front/src/utils/isInIframe.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const isInFrame = () => { - try { - return window.self !== window.top; - } catch { - return true; - } -}; diff --git a/packages/twenty-sdk/src/clients/generated/metadata/schema.graphql b/packages/twenty-sdk/src/clients/generated/metadata/schema.graphql index f49b58e4dad..42fa256a283 100644 --- a/packages/twenty-sdk/src/clients/generated/metadata/schema.graphql +++ b/packages/twenty-sdk/src/clients/generated/metadata/schema.graphql @@ -2404,7 +2404,6 @@ type ClientConfig { isAttachmentPreviewEnabled: Boolean! sentry: Sentry! captcha: Captcha! - chromeExtensionId: String api: ApiConfig! canManageFeatureFlags: Boolean! publicFeatureFlags: [PublicFeatureFlag!]! @@ -2460,9 +2459,7 @@ enum ConfigVariablesGroup { MICROSOFT_AUTH EMAIL_SETTINGS LOGGING - METERING - EXCEPTION_HANDLER - OTHER + ADVANCED_SETTINGS BILLING_CONFIG CAPTCHA_CONFIG CLOUDFLARE_CONFIG @@ -2473,7 +2470,6 @@ enum ConfigVariablesGroup { SUPPORT_CHAT_CONFIG ANALYTICS_CONFIG TOKENS_DURATION - TWO_FACTOR_AUTHENTICATION AWS_SES_SETTINGS } diff --git a/packages/twenty-sdk/src/clients/generated/metadata/schema.ts b/packages/twenty-sdk/src/clients/generated/metadata/schema.ts index 98c3b52ac47..7f2c70c3269 100644 --- a/packages/twenty-sdk/src/clients/generated/metadata/schema.ts +++ b/packages/twenty-sdk/src/clients/generated/metadata/schema.ts @@ -2047,7 +2047,6 @@ export interface ClientConfig { isAttachmentPreviewEnabled: Scalars['Boolean'] sentry: Sentry captcha: Captcha - chromeExtensionId?: Scalars['String'] api: ApiConfig canManageFeatureFlags: Scalars['Boolean'] publicFeatureFlags: PublicFeatureFlag[] @@ -2088,7 +2087,7 @@ export interface ConfigVariablesGroupData { __typename: 'ConfigVariablesGroupData' } -export type ConfigVariablesGroup = 'SERVER_CONFIG' | 'RATE_LIMITING' | 'STORAGE_CONFIG' | 'GOOGLE_AUTH' | 'MICROSOFT_AUTH' | 'EMAIL_SETTINGS' | 'LOGGING' | 'METERING' | 'EXCEPTION_HANDLER' | 'OTHER' | 'BILLING_CONFIG' | 'CAPTCHA_CONFIG' | 'CLOUDFLARE_CONFIG' | 'LLM' | 'LOGIC_FUNCTION_CONFIG' | 'CODE_INTERPRETER_CONFIG' | 'SSL' | 'SUPPORT_CHAT_CONFIG' | 'ANALYTICS_CONFIG' | 'TOKENS_DURATION' | 'TWO_FACTOR_AUTHENTICATION' | 'AWS_SES_SETTINGS' +export type ConfigVariablesGroup = 'SERVER_CONFIG' | 'RATE_LIMITING' | 'STORAGE_CONFIG' | 'GOOGLE_AUTH' | 'MICROSOFT_AUTH' | 'EMAIL_SETTINGS' | 'LOGGING' | 'ADVANCED_SETTINGS' | 'BILLING_CONFIG' | 'CAPTCHA_CONFIG' | 'CLOUDFLARE_CONFIG' | 'LLM' | 'LOGIC_FUNCTION_CONFIG' | 'CODE_INTERPRETER_CONFIG' | 'SSL' | 'SUPPORT_CHAT_CONFIG' | 'ANALYTICS_CONFIG' | 'TOKENS_DURATION' | 'AWS_SES_SETTINGS' export interface ConfigVariables { groups: ConfigVariablesGroupData[] @@ -5052,7 +5051,6 @@ export interface ClientConfigGenqlSelection{ isAttachmentPreviewEnabled?: boolean | number sentry?: SentryGenqlSelection captcha?: CaptchaGenqlSelection - chromeExtensionId?: boolean | number api?: ApiConfigGenqlSelection canManageFeatureFlags?: boolean | number publicFeatureFlags?: PublicFeatureFlagGenqlSelection @@ -8846,9 +8844,7 @@ export const enumConfigVariablesGroup = { MICROSOFT_AUTH: 'MICROSOFT_AUTH' as const, EMAIL_SETTINGS: 'EMAIL_SETTINGS' as const, LOGGING: 'LOGGING' as const, - METERING: 'METERING' as const, - EXCEPTION_HANDLER: 'EXCEPTION_HANDLER' as const, - OTHER: 'OTHER' as const, + ADVANCED_SETTINGS: 'ADVANCED_SETTINGS' as const, BILLING_CONFIG: 'BILLING_CONFIG' as const, CAPTCHA_CONFIG: 'CAPTCHA_CONFIG' as const, CLOUDFLARE_CONFIG: 'CLOUDFLARE_CONFIG' as const, @@ -8859,7 +8855,6 @@ export const enumConfigVariablesGroup = { SUPPORT_CHAT_CONFIG: 'SUPPORT_CHAT_CONFIG' as const, ANALYTICS_CONFIG: 'ANALYTICS_CONFIG' as const, TOKENS_DURATION: 'TOKENS_DURATION' as const, - TWO_FACTOR_AUTHENTICATION: 'TWO_FACTOR_AUTHENTICATION' as const, AWS_SES_SETTINGS: 'AWS_SES_SETTINGS' as const } diff --git a/packages/twenty-sdk/src/clients/generated/metadata/types.ts b/packages/twenty-sdk/src/clients/generated/metadata/types.ts index 7bbf480605e..56cf7e885d0 100644 --- a/packages/twenty-sdk/src/clients/generated/metadata/types.ts +++ b/packages/twenty-sdk/src/clients/generated/metadata/types.ts @@ -4666,9 +4666,6 @@ export default { "captcha": [ 253 ], - "chromeExtensionId": [ - 1 - ], "api": [ 255 ], diff --git a/packages/twenty-server/.env.example b/packages/twenty-server/.env.example index 98198786b60..089ad16faf0 100644 --- a/packages/twenty-server/.env.example +++ b/packages/twenty-server/.env.example @@ -57,7 +57,6 @@ FRONTEND_URL=http://localhost:3001 # IS_EMAIL_VERIFICATION_REQUIRED=false # EMAIL_VERIFICATION_TOKEN_EXPIRES_IN=1h # EMAIL_FROM_ADDRESS=contact@yourdomain.com -# EMAIL_SYSTEM_ADDRESS=system@yourdomain.com # EMAIL_FROM_NAME='John from YourDomain' # EMAIL_DRIVER=LOGGER # EMAIL_SMTP_HOST= @@ -71,7 +70,6 @@ FRONTEND_URL=http://localhost:3001 # API_RATE_LIMITING_TTL= # API_RATE_LIMITING_LIMIT= # MUTATION_MAXIMUM_AFFECTED_RECORDS=100 -# CHROME_EXTENSION_ID=bggmipldbceihilonnbpgoeclgbkblkp # PG_SSL_ALLOW_SELF_SIGNED=true # ENTERPRISE_KEY=replace_me_with_a_valid_enterprise_key # SSL_KEY_PATH="./certs/your-cert.key" diff --git a/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.service.ts b/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.service.ts index 844f3db1a3d..7a92b4f1321 100644 --- a/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.service.ts +++ b/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.service.ts @@ -121,6 +121,10 @@ export class AdminPanelService { )) { const { group, description } = metadata; + if (metadata.isHiddenInAdminPanel) { + continue; + } + const envVar: ConfigVariableDTO = { name: varName, description, @@ -142,6 +146,9 @@ export class AdminPanelService { const groups: ConfigVariablesGroupDataDTO[] = Array.from( groupedData.entries(), ) + .filter( + ([name]) => !CONFIG_VARIABLES_GROUP_METADATA[name].isHiddenInAdminPanel, + ) .sort((a, b) => { const positionA = CONFIG_VARIABLES_GROUP_METADATA[a[0]].position; const positionB = CONFIG_VARIABLES_GROUP_METADATA[b[0]].position; diff --git a/packages/twenty-server/src/engine/core-modules/captcha/captcha-driver.factory.ts b/packages/twenty-server/src/engine/core-modules/captcha/captcha-driver.factory.ts new file mode 100644 index 00000000000..5763d033f08 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/captcha/captcha-driver.factory.ts @@ -0,0 +1,79 @@ +import { Injectable } from '@nestjs/common'; + +import { type CaptchaDriver } from 'src/engine/core-modules/captcha/drivers/interfaces/captcha-driver.interface'; + +import { GoogleRecaptchaDriver } from 'src/engine/core-modules/captcha/drivers/google-recaptcha.driver'; +import { TurnstileDriver } from 'src/engine/core-modules/captcha/drivers/turnstile.driver'; +import { CaptchaDriverType } from 'src/engine/core-modules/captcha/interfaces'; +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; +import { DriverFactoryBase } from 'src/engine/core-modules/twenty-config/dynamic-factory.base'; +import { ConfigVariablesGroup } from 'src/engine/core-modules/twenty-config/enums/config-variables-group.enum'; +import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; + +@Injectable() +export class CaptchaDriverFactory extends DriverFactoryBase { + constructor( + twentyConfigService: TwentyConfigService, + private readonly secureHttpClientService: SecureHttpClientService, + ) { + super(twentyConfigService); + } + + protected buildConfigKey(): string { + const driver = this.twentyConfigService.get('CAPTCHA_DRIVER'); + + if (!driver) { + return 'disabled'; + } + + return `${driver}|${this.getConfigGroupHash(ConfigVariablesGroup.CAPTCHA_CONFIG)}`; + } + + protected createDriver(): CaptchaDriver | null { + const driver = this.twentyConfigService.get('CAPTCHA_DRIVER'); + const siteKey = this.twentyConfigService.get('CAPTCHA_SITE_KEY'); + const secretKey = this.twentyConfigService.get('CAPTCHA_SECRET_KEY'); + + if (!driver) { + return null; + } + + if (!siteKey || !secretKey) { + throw new Error('Captcha driver requires site key and secret key'); + } + + const captchaOptions = { siteKey, secretKey }; + + switch (driver) { + case CaptchaDriverType.GOOGLE_RECAPTCHA: + return new GoogleRecaptchaDriver( + captchaOptions, + this.secureHttpClientService.getHttpClient({ + baseURL: 'https://www.google.com/recaptcha/api/siteverify', + }), + ); + + case CaptchaDriverType.TURNSTILE: + return new TurnstileDriver( + captchaOptions, + this.secureHttpClientService.getHttpClient({ + baseURL: + 'https://challenges.cloudflare.com/turnstile/v0/siteverify', + }), + ); + + default: + throw new Error(`Invalid captcha driver type: ${driver}`); + } + } + + getCurrentDriver(): CaptchaDriver | null { + const driver = this.twentyConfigService.get('CAPTCHA_DRIVER'); + + if (!driver) { + return null; + } + + return super.getCurrentDriver(); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/captcha/captcha.module-factory.ts b/packages/twenty-server/src/engine/core-modules/captcha/captcha.module-factory.ts deleted file mode 100644 index 3bcca0b6465..00000000000 --- a/packages/twenty-server/src/engine/core-modules/captcha/captcha.module-factory.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { - type CaptchaDriverOptions, - type CaptchaModuleOptions, -} from 'src/engine/core-modules/captcha/interfaces'; -import { type TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; - -export const captchaModuleFactory = ( - twentyConfigService: TwentyConfigService, -): CaptchaModuleOptions | undefined => { - const driver = twentyConfigService.get('CAPTCHA_DRIVER'); - const siteKey = twentyConfigService.get('CAPTCHA_SITE_KEY'); - const secretKey = twentyConfigService.get('CAPTCHA_SECRET_KEY'); - - if (!driver) { - return; - } - - if (!siteKey || !secretKey) { - throw new Error('Captcha driver requires site key and secret key'); - } - - const captchaOptions: CaptchaDriverOptions = { - siteKey, - secretKey, - }; - - return { - type: driver, - options: captchaOptions, - }; -}; diff --git a/packages/twenty-server/src/engine/core-modules/captcha/captcha.module.ts b/packages/twenty-server/src/engine/core-modules/captcha/captcha.module.ts index c4291d022c9..afaf43bb37c 100644 --- a/packages/twenty-server/src/engine/core-modules/captcha/captcha.module.ts +++ b/packages/twenty-server/src/engine/core-modules/captcha/captcha.module.ts @@ -1,59 +1,17 @@ import { type DynamicModule, Global } from '@nestjs/common'; +import { CaptchaDriverFactory } from 'src/engine/core-modules/captcha/captcha-driver.factory'; import { CaptchaService } from 'src/engine/core-modules/captcha/captcha.service'; -import { CAPTCHA_DRIVER } from 'src/engine/core-modules/captcha/constants/captcha-driver.constants'; -import { GoogleRecaptchaDriver } from 'src/engine/core-modules/captcha/drivers/google-recaptcha.driver'; -import { TurnstileDriver } from 'src/engine/core-modules/captcha/drivers/turnstile.driver'; -import { - CaptchaDriverType, - type CaptchaModuleAsyncOptions, -} from 'src/engine/core-modules/captcha/interfaces'; import { SecureHttpClientModule } from 'src/engine/core-modules/secure-http-client/secure-http-client.module'; -import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; +import { TwentyConfigModule } from 'src/engine/core-modules/twenty-config/twenty-config.module'; @Global() export class CaptchaModule { - static forRoot(options: CaptchaModuleAsyncOptions): DynamicModule { - const provider = { - provide: CAPTCHA_DRIVER, - useFactory: async ( - secureHttpClientService: SecureHttpClientService, - // oxlint-disable-next-line @typescripttypescript/no-explicit-any - ...args: any[] - ) => { - const config = await options.useFactory(...args); - - if (!config) { - return; - } - - switch (config.type) { - case CaptchaDriverType.GOOGLE_RECAPTCHA: - return new GoogleRecaptchaDriver( - config.options, - secureHttpClientService.getHttpClient({ - baseURL: 'https://www.google.com/recaptcha/api/siteverify', - }), - ); - case CaptchaDriverType.TURNSTILE: - return new TurnstileDriver( - config.options, - secureHttpClientService.getHttpClient({ - baseURL: - 'https://challenges.cloudflare.com/turnstile/v0/siteverify', - }), - ); - default: - return; - } - }, - inject: [SecureHttpClientService, ...(options.inject || [])], - }; - + static forRoot(): DynamicModule { return { module: CaptchaModule, - imports: [SecureHttpClientModule], - providers: [CaptchaService, provider], + imports: [TwentyConfigModule, SecureHttpClientModule], + providers: [CaptchaDriverFactory, CaptchaService], exports: [CaptchaService], }; } diff --git a/packages/twenty-server/src/engine/core-modules/captcha/captcha.service.ts b/packages/twenty-server/src/engine/core-modules/captcha/captcha.service.ts index d4a08f80723..9f613650da6 100644 --- a/packages/twenty-server/src/engine/core-modules/captcha/captcha.service.ts +++ b/packages/twenty-server/src/engine/core-modules/captcha/captcha.service.ts @@ -1,21 +1,20 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; -import { CaptchaDriver } from 'src/engine/core-modules/captcha/drivers/interfaces/captcha-driver.interface'; - -import { CAPTCHA_DRIVER } from 'src/engine/core-modules/captcha/constants/captcha-driver.constants'; +import { CaptchaDriverFactory } from 'src/engine/core-modules/captcha/captcha-driver.factory'; +import { type CaptchaDriver } from 'src/engine/core-modules/captcha/drivers/interfaces/captcha-driver.interface'; import { type CaptchaValidateResult } from 'src/engine/core-modules/captcha/interfaces'; @Injectable() export class CaptchaService implements CaptchaDriver { - constructor(@Inject(CAPTCHA_DRIVER) private driver: CaptchaDriver) {} + constructor(private readonly captchaDriverFactory: CaptchaDriverFactory) {} async validate(token: string): Promise { - if (this.driver) { - return await this.driver.validate(token); - } else { - return { - success: true, - }; + const driver = this.captchaDriverFactory.getCurrentDriver(); + + if (!driver) { + return { success: true }; } + + return driver.validate(token); } } diff --git a/packages/twenty-server/src/engine/core-modules/captcha/constants/captcha-driver.constants.ts b/packages/twenty-server/src/engine/core-modules/captcha/constants/captcha-driver.constants.ts deleted file mode 100644 index 1a1ed4d4543..00000000000 --- a/packages/twenty-server/src/engine/core-modules/captcha/constants/captcha-driver.constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const CAPTCHA_DRIVER = Symbol('CAPTCHA_DRIVER'); diff --git a/packages/twenty-server/src/engine/core-modules/captcha/interfaces/captcha.interface.ts b/packages/twenty-server/src/engine/core-modules/captcha/interfaces/captcha.interface.ts index 48d3e85f797..94e3ceee9e9 100644 --- a/packages/twenty-server/src/engine/core-modules/captcha/interfaces/captcha.interface.ts +++ b/packages/twenty-server/src/engine/core-modules/captcha/interfaces/captcha.interface.ts @@ -1,4 +1,3 @@ -import { type FactoryProvider, type ModuleMetadata } from '@nestjs/common'; import { registerEnumType } from '@nestjs/graphql'; export enum CaptchaDriverType { @@ -15,25 +14,4 @@ export type CaptchaDriverOptions = { secretKey: string; }; -export interface GoogleRecaptchaDriverFactoryOptions { - type: CaptchaDriverType.GOOGLE_RECAPTCHA; - options: CaptchaDriverOptions; -} - -export interface TurnstileDriverFactoryOptions { - type: CaptchaDriverType.TURNSTILE; - options: CaptchaDriverOptions; -} - -export type CaptchaModuleOptions = - | GoogleRecaptchaDriverFactoryOptions - | TurnstileDriverFactoryOptions; - -export type CaptchaModuleAsyncOptions = { - useFactory: ( - // oxlint-disable-next-line @typescripttypescript/no-explicit-any - ...args: any[] - ) => CaptchaModuleOptions | Promise | undefined; -} & Pick & - Pick; export type CaptchaValidateResult = { success: boolean; error?: string }; diff --git a/packages/twenty-server/src/engine/core-modules/client-config/client-config.controller.spec.ts b/packages/twenty-server/src/engine/core-modules/client-config/client-config.controller.spec.ts index c1b07666f44..05280e97061 100644 --- a/packages/twenty-server/src/engine/core-modules/client-config/client-config.controller.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/client-config/client-config.controller.spec.ts @@ -84,7 +84,6 @@ describe('ClientConfigController', () => { provider: undefined, siteKey: undefined, }, - chromeExtensionId: undefined, api: { mutationMaximumAffectedRecords: 100, }, diff --git a/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts b/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts index d9b1f1e41fa..e7d07cf5954 100644 --- a/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts @@ -213,9 +213,6 @@ export class ClientConfig { @Field(() => Captcha) captcha: Captcha; - @Field(() => String, { nullable: true }) - chromeExtensionId: string | undefined; - @Field(() => ApiConfig) api: ApiConfig; diff --git a/packages/twenty-server/src/engine/core-modules/client-config/services/client-config.service.spec.ts b/packages/twenty-server/src/engine/core-modules/client-config/services/client-config.service.spec.ts index b5969f71cd8..c702abae947 100644 --- a/packages/twenty-server/src/engine/core-modules/client-config/services/client-config.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/client-config/services/client-config.service.spec.ts @@ -77,7 +77,6 @@ describe('ClientConfigService', () => { SENTRY_FRONT_DSN: 'https://sentry.example.com', CAPTCHA_DRIVER: CaptchaDriverType.GOOGLE_RECAPTCHA, CAPTCHA_SITE_KEY: 'site-key-123', - CHROME_EXTENSION_ID: 'extension-123', MUTATION_MAXIMUM_AFFECTED_RECORDS: 1000, IS_ATTACHMENT_PREVIEW_ENABLED: true, ANALYTICS_ENABLED: true, @@ -145,7 +144,6 @@ describe('ClientConfigService', () => { provider: 'GOOGLE_RECAPTCHA', siteKey: 'site-key-123', }, - chromeExtensionId: 'extension-123', api: { mutationMaximumAffectedRecords: 1000, }, diff --git a/packages/twenty-server/src/engine/core-modules/client-config/services/client-config.service.ts b/packages/twenty-server/src/engine/core-modules/client-config/services/client-config.service.ts index 61f9d139dcf..23a141c27d5 100644 --- a/packages/twenty-server/src/engine/core-modules/client-config/services/client-config.service.ts +++ b/packages/twenty-server/src/engine/core-modules/client-config/services/client-config.service.ts @@ -165,7 +165,6 @@ export class ClientConfigService { provider: captchaProvider ? captchaProvider : undefined, siteKey: this.twentyConfigService.get('CAPTCHA_SITE_KEY'), }, - chromeExtensionId: this.twentyConfigService.get('CHROME_EXTENSION_ID'), api: { mutationMaximumAffectedRecords: this.twentyConfigService.get( 'MUTATION_MAXIMUM_AFFECTED_RECORDS', diff --git a/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter-driver.factory.ts b/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter-driver.factory.ts new file mode 100644 index 00000000000..95444a07605 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter-driver.factory.ts @@ -0,0 +1,72 @@ +import { Injectable } from '@nestjs/common'; + +import { type CodeInterpreterDriver } from 'src/engine/core-modules/code-interpreter/drivers/interfaces/code-interpreter-driver.interface'; + +import { CodeInterpreterDriverType } from 'src/engine/core-modules/code-interpreter/code-interpreter.interface'; +import { DisabledDriver } from 'src/engine/core-modules/code-interpreter/drivers/disabled.driver'; +import { E2BDriver } from 'src/engine/core-modules/code-interpreter/drivers/e2b.driver'; +import { LocalDriver } from 'src/engine/core-modules/code-interpreter/drivers/local.driver'; +import { NodeEnvironment } from 'src/engine/core-modules/twenty-config/interfaces/node-environment.interface'; +import { DriverFactoryBase } from 'src/engine/core-modules/twenty-config/dynamic-factory.base'; +import { ConfigVariablesGroup } from 'src/engine/core-modules/twenty-config/enums/config-variables-group.enum'; +import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; + +@Injectable() +export class CodeInterpreterDriverFactory extends DriverFactoryBase { + constructor(twentyConfigService: TwentyConfigService) { + super(twentyConfigService); + } + + protected buildConfigKey(): string { + const driverType = this.twentyConfigService.get('CODE_INTERPRETER_TYPE'); + + if (driverType === CodeInterpreterDriverType.E_2_B) { + return `e2b|${this.getConfigGroupHash(ConfigVariablesGroup.CODE_INTERPRETER_CONFIG)}`; + } + + return driverType; + } + + protected createDriver(): CodeInterpreterDriver { + const driverType = this.twentyConfigService.get('CODE_INTERPRETER_TYPE'); + const timeoutMs = this.twentyConfigService.get( + 'CODE_INTERPRETER_TIMEOUT_MS', + ); + + switch (driverType) { + case CodeInterpreterDriverType.DISABLED: + return new DisabledDriver( + 'Code interpreter is disabled. Set CODE_INTERPRETER_TYPE to LOCAL (development only) or E2B to enable it.', + ); + + case CodeInterpreterDriverType.LOCAL: { + const nodeEnv = this.twentyConfigService.get('NODE_ENV'); + + if (nodeEnv === NodeEnvironment.PRODUCTION) { + return new DisabledDriver( + 'LOCAL code interpreter driver is not allowed in production. Use E2B driver instead by setting CODE_INTERPRETER_TYPE=E2B and providing E2B_API_KEY.', + ); + } + + return new LocalDriver({ timeoutMs }); + } + + case CodeInterpreterDriverType.E_2_B: { + const apiKey = this.twentyConfigService.get('E2B_API_KEY'); + + if (!apiKey) { + throw new Error( + 'E2B_API_KEY is required when CODE_INTERPRETER_TYPE is E2B', + ); + } + + return new E2BDriver({ apiKey, timeoutMs }); + } + + default: + throw new Error( + `Invalid code interpreter driver type (${driverType}), check your .env file`, + ); + } + } +} diff --git a/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter-module.factory.ts b/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter-module.factory.ts deleted file mode 100644 index 3466921e9f0..00000000000 --- a/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter-module.factory.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { NodeEnvironment } from 'src/engine/core-modules/twenty-config/interfaces/node-environment.interface'; - -import { type TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; - -import { - CodeInterpreterDriverType, - type CodeInterpreterModuleOptions, -} from './code-interpreter.interface'; - -export const codeInterpreterModuleFactory = async ( - twentyConfigService: TwentyConfigService, -): Promise => { - const driverType = twentyConfigService.get('CODE_INTERPRETER_TYPE'); - const timeoutMs = twentyConfigService.get('CODE_INTERPRETER_TIMEOUT_MS'); - - switch (driverType) { - case CodeInterpreterDriverType.LOCAL: { - const nodeEnv = twentyConfigService.get('NODE_ENV'); - - if (nodeEnv === NodeEnvironment.PRODUCTION) { - return { - type: CodeInterpreterDriverType.DISABLED, - options: { - reason: - 'LOCAL code interpreter driver is not allowed in production. Use E2B driver instead by setting CODE_INTERPRETER_TYPE=E2B and providing E2B_API_KEY.', - }, - }; - } - - return { - type: CodeInterpreterDriverType.LOCAL, - options: { timeoutMs }, - }; - } - case CodeInterpreterDriverType.E_2_B: { - const apiKey = twentyConfigService.get('E2B_API_KEY'); - - if (!apiKey) { - throw new Error( - 'E2B_API_KEY is required when CODE_INTERPRETER_TYPE is E2B', - ); - } - - return { - type: CodeInterpreterDriverType.E_2_B, - options: { - apiKey, - timeoutMs, - }, - }; - } - case CodeInterpreterDriverType.DISABLED: { - return { - type: CodeInterpreterDriverType.DISABLED, - options: { - reason: - 'Code interpreter is disabled. Set CODE_INTERPRETER_TYPE to LOCAL (development only) or E2B to enable it.', - }, - }; - } - default: - throw new Error( - `Invalid code interpreter driver type (${driverType}), check your .env file`, - ); - } -}; diff --git a/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter.constants.ts b/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter.constants.ts index 955c36b57d2..e82d93e5ac7 100644 --- a/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter.constants.ts +++ b/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter.constants.ts @@ -1,3 +1 @@ -export const CODE_INTERPRETER_DRIVER = Symbol('CODE_INTERPRETER_DRIVER'); - export const DEFAULT_CODE_INTERPRETER_TIMEOUT_MS = 300_000; diff --git a/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter.interface.ts b/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter.interface.ts index 57494a06f6b..fd48480d0a3 100644 --- a/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter.interface.ts +++ b/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter.interface.ts @@ -1,37 +1,5 @@ -import { type FactoryProvider, type ModuleMetadata } from '@nestjs/common'; - -import { type E2BDriverOptions } from './drivers/e2b.driver'; -import { type LocalDriverOptions } from './drivers/local.driver'; - export enum CodeInterpreterDriverType { LOCAL = 'LOCAL', E_2_B = 'E_2_B', DISABLED = 'DISABLED', } - -export type LocalDriverFactoryOptions = { - type: CodeInterpreterDriverType.LOCAL; - options: LocalDriverOptions; -}; - -export type E2BDriverFactoryOptions = { - type: CodeInterpreterDriverType.E_2_B; - options: E2BDriverOptions; -}; - -export type DisabledDriverFactoryOptions = { - type: CodeInterpreterDriverType.DISABLED; - options: { reason: string }; -}; - -export type CodeInterpreterModuleOptions = - | LocalDriverFactoryOptions - | E2BDriverFactoryOptions - | DisabledDriverFactoryOptions; - -export type CodeInterpreterModuleAsyncOptions = { - useFactory: ( - ...args: unknown[] - ) => CodeInterpreterModuleOptions | Promise; -} & Pick & - Pick; diff --git a/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter.module.ts b/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter.module.ts index a4ffdd0d42b..1f07b951ee8 100644 --- a/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter.module.ts +++ b/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter.module.ts @@ -1,42 +1,16 @@ import { type DynamicModule, Global } from '@nestjs/common'; -import { CODE_INTERPRETER_DRIVER } from './code-interpreter.constants'; -import { - CodeInterpreterDriverType, - type CodeInterpreterModuleAsyncOptions, -} from './code-interpreter.interface'; -import { CodeInterpreterService } from './code-interpreter.service'; - -import { DisabledDriver } from './drivers/disabled.driver'; -import { E2BDriver } from './drivers/e2b.driver'; -import { LocalDriver } from './drivers/local.driver'; +import { CodeInterpreterDriverFactory } from 'src/engine/core-modules/code-interpreter/code-interpreter-driver.factory'; +import { CodeInterpreterService } from 'src/engine/core-modules/code-interpreter/code-interpreter.service'; +import { TwentyConfigModule } from 'src/engine/core-modules/twenty-config/twenty-config.module'; @Global() export class CodeInterpreterModule { - static forRootAsync( - options: CodeInterpreterModuleAsyncOptions, - ): DynamicModule { - const provider = { - provide: CODE_INTERPRETER_DRIVER, - useFactory: async (...args: unknown[]) => { - const config = await options.useFactory(...args); - - switch (config.type) { - case CodeInterpreterDriverType.LOCAL: - return new LocalDriver(config.options); - case CodeInterpreterDriverType.E_2_B: - return new E2BDriver(config.options); - case CodeInterpreterDriverType.DISABLED: - return new DisabledDriver(config.options.reason); - } - }, - inject: options.inject ?? [], - }; - + static forRoot(): DynamicModule { return { module: CodeInterpreterModule, - imports: options.imports ?? [], - providers: [CodeInterpreterService, provider], + imports: [TwentyConfigModule], + providers: [CodeInterpreterDriverFactory, CodeInterpreterService], exports: [CodeInterpreterService], }; } diff --git a/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter.service.ts b/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter.service.ts index 370217c3a3b..4f328b2b488 100644 --- a/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter.service.ts +++ b/packages/twenty-server/src/engine/core-modules/code-interpreter/code-interpreter.service.ts @@ -1,19 +1,18 @@ -import { Inject, Injectable } from '@nestjs/common'; - -import { CODE_INTERPRETER_DRIVER } from './code-interpreter.constants'; +import { Injectable } from '@nestjs/common'; +import { CodeInterpreterDriverFactory } from 'src/engine/core-modules/code-interpreter/code-interpreter-driver.factory'; import { type CodeExecutionResult, type CodeInterpreterDriver, type ExecutionContext, type InputFile, type StreamCallbacks, -} from './drivers/interfaces/code-interpreter-driver.interface'; +} from 'src/engine/core-modules/code-interpreter/drivers/interfaces/code-interpreter-driver.interface'; @Injectable() export class CodeInterpreterService implements CodeInterpreterDriver { constructor( - @Inject(CODE_INTERPRETER_DRIVER) private driver: CodeInterpreterDriver, + private readonly codeInterpreterDriverFactory: CodeInterpreterDriverFactory, ) {} execute( @@ -22,6 +21,8 @@ export class CodeInterpreterService implements CodeInterpreterDriver { context?: ExecutionContext, callbacks?: StreamCallbacks, ): Promise { - return this.driver.execute(code, files, context, callbacks); + const driver = this.codeInterpreterDriverFactory.getCurrentDriver(); + + return driver.execute(code, files, context, callbacks); } } diff --git a/packages/twenty-server/src/engine/core-modules/core-engine.module.ts b/packages/twenty-server/src/engine/core-modules/core-engine.module.ts index b5114e4a5b6..fe563e90f7b 100644 --- a/packages/twenty-server/src/engine/core-modules/core-engine.module.ts +++ b/packages/twenty-server/src/engine/core-modules/core-engine.module.ts @@ -22,9 +22,7 @@ import { BillingModule } from 'src/engine/core-modules/billing/billing.module'; import { CacheStorageModule } from 'src/engine/core-modules/cache-storage/cache-storage.module'; import { TimelineCalendarEventModule } from 'src/engine/core-modules/calendar/timeline-calendar-event.module'; import { CaptchaModule } from 'src/engine/core-modules/captcha/captcha.module'; -import { captchaModuleFactory } from 'src/engine/core-modules/captcha/captcha.module-factory'; import { CloudflareModule } from 'src/engine/core-modules/cloudflare/cloudflare.module'; -import { codeInterpreterModuleFactory } from 'src/engine/core-modules/code-interpreter/code-interpreter-module.factory'; import { CodeInterpreterModule } from 'src/engine/core-modules/code-interpreter/code-interpreter.module'; import { DnsManagerModule } from 'src/engine/core-modules/dns-manager/dns-manager.module'; import { EmailModule } from 'src/engine/core-modules/email/email.module'; @@ -51,7 +49,6 @@ import { PublicDomainModule } from 'src/engine/core-modules/public-domain/public import { RedisClientModule } from 'src/engine/core-modules/redis-client/redis-client.module'; import { RedisClientService } from 'src/engine/core-modules/redis-client/redis-client.service'; import { SearchModule } from 'src/engine/core-modules/search/search.module'; -import { logicFunctionModuleFactory } from 'src/engine/core-modules/logic-function/logic-function-drivers/factories/logic-function-module.factory'; import { LogicFunctionModule } from 'src/engine/core-modules/logic-function/logic-function.module'; import { WorkspaceSSOModule } from 'src/engine/core-modules/sso/sso.module'; import { TelemetryModule } from 'src/engine/core-modules/telemetry/telemetry.module'; @@ -72,8 +69,6 @@ import { MetadataEventEmitterModule } from 'src/engine/metadata-event-emitter/me import { WorkspaceEventEmitterModule } from 'src/engine/workspace-event-emitter/workspace-event-emitter.module'; import { ChannelSyncModule } from 'src/modules/connected-account/channel-sync/channel-sync.module'; import { DashboardModule } from 'src/modules/dashboard/dashboard.module'; -import { LogicFunctionResourceService } from 'src/engine/core-modules/logic-function/logic-function-resource/logic-function-resource.service'; - import { AuditModule } from './audit/audit.module'; import { ClientConfigModule } from './client-config/client-config.module'; import { EventLogsModule } from './event-logs/event-logs.module'; @@ -142,24 +137,15 @@ import { FileModule } from './file/file.module'; inject: [TwentyConfigService, HttpAdapterHost], }), EmailModule.forRoot(), - CaptchaModule.forRoot({ - useFactory: captchaModuleFactory, - inject: [TwentyConfigService], - }), + CaptchaModule.forRoot(), EventEmitterModule.forRoot({ wildcard: true, }), CacheStorageModule, AiModelsModule, AiBillingModule, - LogicFunctionModule.forRootAsync({ - useFactory: logicFunctionModuleFactory, - inject: [TwentyConfigService, LogicFunctionResourceService], - }), - CodeInterpreterModule.forRootAsync({ - useFactory: codeInterpreterModuleFactory, - inject: [TwentyConfigService], - }), + LogicFunctionModule.forRoot(), + CodeInterpreterModule.forRoot(), SearchModule, ApiKeyModule, PageLayoutModule, diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/logic-function-driver.constants.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/logic-function-driver.constants.ts deleted file mode 100644 index a4c9b02e77d..00000000000 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/logic-function-driver.constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const LOGIC_FUNCTION_DRIVER = Symbol('LOGIC_FUNCTION_DRIVER'); diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/factories/logic-function-module.factory.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/factories/logic-function-module.factory.ts deleted file mode 100644 index f7f72819d26..00000000000 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/factories/logic-function-module.factory.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { fromNodeProviderChain } from '@aws-sdk/credential-providers'; - -import { - LogicFunctionDriverType, - type LogicFunctionModuleOptions, -} from 'src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface'; - -import { type TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; -import type { LogicFunctionResourceService } from 'src/engine/core-modules/logic-function/logic-function-resource/logic-function-resource.service'; - -export const logicFunctionModuleFactory = async ( - twentyConfigService: TwentyConfigService, - logicFunctionResourceService: LogicFunctionResourceService, -): Promise => { - const driverType = twentyConfigService.get('LOGIC_FUNCTION_TYPE'); - const options = { logicFunctionResourceService }; - - switch (driverType) { - case LogicFunctionDriverType.DISABLED: { - return { - type: LogicFunctionDriverType.DISABLED, - }; - } - case LogicFunctionDriverType.LOCAL: { - return { - type: LogicFunctionDriverType.LOCAL, - options, - }; - } - case LogicFunctionDriverType.LAMBDA: { - const region = twentyConfigService.get('LOGIC_FUNCTION_LAMBDA_REGION'); - const accessKeyId = twentyConfigService.get( - 'LOGIC_FUNCTION_LAMBDA_ACCESS_KEY_ID', - ); - const secretAccessKey = twentyConfigService.get( - 'LOGIC_FUNCTION_LAMBDA_SECRET_ACCESS_KEY', - ); - const lambdaRole = twentyConfigService.get('LOGIC_FUNCTION_LAMBDA_ROLE'); - - const subhostingRole = twentyConfigService.get( - 'LOGIC_FUNCTION_LAMBDA_SUBHOSTING_ROLE', - ); - - const s3BucketName = twentyConfigService.get('STORAGE_S3_NAME'); - - const layerBucket = - twentyConfigService.get('LOGIC_FUNCTION_LAMBDA_LAYER_BUCKET') ?? - s3BucketName ?? - 'twenty-lambda-layer'; - - const layerBucketRegion = - twentyConfigService.get('LOGIC_FUNCTION_LAMBDA_LAYER_BUCKET_REGION') ?? - region; - - return { - type: LogicFunctionDriverType.LAMBDA, - options: { - ...options, - credentials: accessKeyId - ? { - accessKeyId, - secretAccessKey, - } - : fromNodeProviderChain({ - clientConfig: { region }, - }), - region, - lambdaRole, - subhostingRole, - layerBucket, - layerBucketRegion, - }, - }; - } - default: - throw new Error( - `Invalid logic function executor driver type (${driverType}), check your .env file`, - ); - } -}; diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface.ts index d6d89147fad..fe02053f3b4 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface.ts @@ -1,10 +1,6 @@ -import type { FactoryProvider, ModuleMetadata } from '@nestjs/common'; - import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type'; import { type LogicFunctionExecutionStatus } from 'src/engine/metadata-modules/logic-function/dtos/logic-function-execution-result.dto'; import { type FlatLogicFunction } from 'src/engine/metadata-modules/logic-function/types/flat-logic-function.type'; -import type { LocalDriverOptions } from 'src/engine/core-modules/logic-function/logic-function-drivers/drivers/local.driver'; -import type { LambdaDriverOptions } from 'src/engine/core-modules/logic-function/logic-function-drivers/drivers/lambda.driver'; export type LogicFunctionExecuteError = { errorType: string; @@ -54,30 +50,3 @@ export enum LogicFunctionDriverType { LAMBDA = 'LAMBDA', LOCAL = 'LOCAL', } - -export interface DisabledDriverFactoryOptions { - type: LogicFunctionDriverType.DISABLED; -} - -export interface LocalDriverFactoryOptions { - type: LogicFunctionDriverType.LOCAL; - options: LocalDriverOptions; -} - -export interface LambdaDriverFactoryOptions { - type: LogicFunctionDriverType.LAMBDA; - options: LambdaDriverOptions; -} - -export type LogicFunctionModuleOptions = - | DisabledDriverFactoryOptions - | LocalDriverFactoryOptions - | LambdaDriverFactoryOptions; - -export type LogicFunctionModuleAsyncOptions = { - useFactory: ( - // oxlint-disable-next-line @typescripttypescript/no-explicit-any - ...args: any[] - ) => LogicFunctionModuleOptions | Promise; -} & Pick & - Pick; diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/logic-function-driver.factory.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/logic-function-driver.factory.ts new file mode 100644 index 00000000000..ce997519b89 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/logic-function-driver.factory.ts @@ -0,0 +1,94 @@ +import { Injectable } from '@nestjs/common'; + +import { fromNodeProviderChain } from '@aws-sdk/credential-providers'; + +import { + type LogicFunctionDriver, + LogicFunctionDriverType, +} from 'src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface'; + +import { DisabledDriver } from 'src/engine/core-modules/logic-function/logic-function-drivers/drivers/disabled.driver'; +import { LambdaDriver } from 'src/engine/core-modules/logic-function/logic-function-drivers/drivers/lambda.driver'; +import { LocalDriver } from 'src/engine/core-modules/logic-function/logic-function-drivers/drivers/local.driver'; +import { LogicFunctionResourceService } from 'src/engine/core-modules/logic-function/logic-function-resource/logic-function-resource.service'; +import { DriverFactoryBase } from 'src/engine/core-modules/twenty-config/dynamic-factory.base'; +import { ConfigVariablesGroup } from 'src/engine/core-modules/twenty-config/enums/config-variables-group.enum'; +import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; + +@Injectable() +export class LogicFunctionDriverFactory extends DriverFactoryBase { + constructor( + twentyConfigService: TwentyConfigService, + private readonly logicFunctionResourceService: LogicFunctionResourceService, + ) { + super(twentyConfigService); + } + + protected buildConfigKey(): string { + const driverType = this.twentyConfigService.get('LOGIC_FUNCTION_TYPE'); + + if (driverType === LogicFunctionDriverType.LAMBDA) { + return `lambda|${this.getConfigGroupHash(ConfigVariablesGroup.LOGIC_FUNCTION_CONFIG)}`; + } + + return driverType; + } + + protected createDriver(): LogicFunctionDriver { + const driverType = this.twentyConfigService.get('LOGIC_FUNCTION_TYPE'); + + switch (driverType) { + case LogicFunctionDriverType.DISABLED: + return new DisabledDriver(); + + case LogicFunctionDriverType.LOCAL: + return new LocalDriver({ + logicFunctionResourceService: this.logicFunctionResourceService, + }); + + case LogicFunctionDriverType.LAMBDA: { + const region = this.twentyConfigService.get( + 'LOGIC_FUNCTION_LAMBDA_REGION', + ); + const accessKeyId = this.twentyConfigService.get( + 'LOGIC_FUNCTION_LAMBDA_ACCESS_KEY_ID', + ); + const secretAccessKey = this.twentyConfigService.get( + 'LOGIC_FUNCTION_LAMBDA_SECRET_ACCESS_KEY', + ); + const lambdaRole = this.twentyConfigService.get( + 'LOGIC_FUNCTION_LAMBDA_ROLE', + ); + const subhostingRole = this.twentyConfigService.get( + 'LOGIC_FUNCTION_LAMBDA_SUBHOSTING_ROLE', + ); + const s3BucketName = this.twentyConfigService.get('STORAGE_S3_NAME'); + const layerBucket = + this.twentyConfigService.get('LOGIC_FUNCTION_LAMBDA_LAYER_BUCKET') ?? + s3BucketName ?? + 'twenty-lambda-layer'; + const layerBucketRegion = + this.twentyConfigService.get( + 'LOGIC_FUNCTION_LAMBDA_LAYER_BUCKET_REGION', + ) ?? region; + + return new LambdaDriver({ + logicFunctionResourceService: this.logicFunctionResourceService, + credentials: accessKeyId + ? { accessKeyId, secretAccessKey } + : fromNodeProviderChain({ clientConfig: { region } }), + region, + lambdaRole, + subhostingRole, + layerBucket, + layerBucketRegion, + }); + } + + default: + throw new Error( + `Invalid logic function driver type (${driverType}), check your .env file`, + ); + } + } +} diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/logic-function-drivers.module.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/logic-function-drivers.module.ts deleted file mode 100644 index cb6fd0f0a26..00000000000 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/logic-function-drivers.module.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { type DynamicModule, Module } from '@nestjs/common'; - -import { - LogicFunctionDriverType, - LogicFunctionModuleAsyncOptions, -} from 'src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface'; - -import { DisabledDriver } from 'src/engine/core-modules/logic-function/logic-function-drivers/drivers/disabled.driver'; -import { LambdaDriver } from 'src/engine/core-modules/logic-function/logic-function-drivers/drivers/lambda.driver'; -import { LocalDriver } from 'src/engine/core-modules/logic-function/logic-function-drivers/drivers/local.driver'; -import { LOGIC_FUNCTION_DRIVER } from 'src/engine/core-modules/logic-function/logic-function-drivers/constants/logic-function-driver.constants'; - -@Module({}) -export class LogicFunctionDriversModule { - static forRootAsync(options: LogicFunctionModuleAsyncOptions): DynamicModule { - const provider = { - provide: LOGIC_FUNCTION_DRIVER, - // oxlint-disable-next-line @typescripttypescript/no-explicit-any - useFactory: async (...args: any[]) => { - const config = await options.useFactory(...args); - - switch (config?.type) { - case LogicFunctionDriverType.DISABLED: - return new DisabledDriver(); - case LogicFunctionDriverType.LOCAL: - return new LocalDriver(config.options); - case LogicFunctionDriverType.LAMBDA: - return new LambdaDriver(config.options); - default: { - const unknownConfig = config as { type?: string }; - - throw new Error( - `Unknown logic function executor driver type: ${unknownConfig?.type}`, - ); - } - } - }, - inject: options.inject || [], - }; - - return { - module: LogicFunctionDriversModule, - imports: options.imports || [], - providers: [provider], - exports: [LOGIC_FUNCTION_DRIVER], - }; - } -} diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service.ts index b367629110c..f9faa78409a 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { DEFAULT_API_KEY_NAME, @@ -8,7 +8,6 @@ import { import { isDefined } from 'twenty-shared/utils'; import { - LogicFunctionDriver, type LogicFunctionExecuteResult, type LogicFunctionTranspileParams, type LogicFunctionTranspileResult, @@ -19,7 +18,7 @@ import type { FlatApplicationVariable } from 'src/engine/core-modules/applicatio import { AuditService } from 'src/engine/core-modules/audit/services/audit.service'; import { LOGIC_FUNCTION_EXECUTED_EVENT } from 'src/engine/core-modules/audit/utils/events/workspace-event/logic-function/logic-function-executed'; import { ApplicationTokenService } from 'src/engine/core-modules/auth/token/services/application-token.service'; -import { LOGIC_FUNCTION_DRIVER } from 'src/engine/core-modules/logic-function/logic-function-drivers/constants/logic-function-driver.constants'; +import { LogicFunctionDriverFactory } from 'src/engine/core-modules/logic-function/logic-function-drivers/logic-function-driver.factory'; import { buildEnvVar } from 'src/engine/core-modules/logic-function/logic-function-executor/utils/build-env-var'; import { SecretEncryptionService } from 'src/engine/core-modules/secret-encryption/secret-encryption.service'; import { ThrottlerService } from 'src/engine/core-modules/throttler/throttler.service'; @@ -49,8 +48,7 @@ export enum LogicFunctionExecutionExceptionCode { @Injectable() export class LogicFunctionExecutorService { constructor( - @Inject(LOGIC_FUNCTION_DRIVER) - private driver: LogicFunctionDriver, + private readonly logicFunctionDriverFactory: LogicFunctionDriverFactory, private readonly throttlerService: ThrottlerService, private readonly twentyConfigService: TwentyConfigService, private readonly workspaceCacheService: WorkspaceCacheService, @@ -84,7 +82,9 @@ export class LogicFunctionExecutorService { _flatLogicFunction: flatLogicFunction, }); - const resultLogicFunction = await this.driver.execute({ + const driver = this.logicFunctionDriverFactory.getCurrentDriver(); + + const resultLogicFunction = await driver.execute({ flatLogicFunction, flatApplication, applicationUniversalIdentifier: flatApplication.universalIdentifier, @@ -106,7 +106,9 @@ export class LogicFunctionExecutorService { async transpile( params: LogicFunctionTranspileParams, ): Promise { - return this.driver.transpile(params); + const driver = this.logicFunctionDriverFactory.getCurrentDriver(); + + return driver.transpile(params); } private async throttleExecution(workspaceId: string) { diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function.module.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function.module.ts index 7f0d1ddcd2d..17683d505e0 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function.module.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function.module.ts @@ -1,26 +1,26 @@ import { type DynamicModule, Global, Module } from '@nestjs/common'; -import { LogicFunctionModuleAsyncOptions } from 'src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface'; - -import { LogicFunctionDriversModule } from 'src/engine/core-modules/logic-function/logic-function-drivers/logic-function-drivers.module'; +import { LogicFunctionDriverFactory } from 'src/engine/core-modules/logic-function/logic-function-drivers/logic-function-driver.factory'; import { LogicFunctionResourceModule } from 'src/engine/core-modules/logic-function/logic-function-resource/logic-function-resource.module'; import { LogicFunctionTriggerModule } from 'src/engine/core-modules/logic-function/logic-function-trigger/logic-function-trigger.module'; import { LogicFunctionExecutorModule } from 'src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.module'; +import { TwentyConfigModule } from 'src/engine/core-modules/twenty-config/twenty-config.module'; @Global() @Module({}) export class LogicFunctionModule { - static forRootAsync(options: LogicFunctionModuleAsyncOptions): DynamicModule { + static forRoot(): DynamicModule { return { module: LogicFunctionModule, imports: [ - LogicFunctionDriversModule.forRootAsync(options), + TwentyConfigModule, LogicFunctionResourceModule, LogicFunctionTriggerModule, LogicFunctionExecutorModule, ], + providers: [LogicFunctionDriverFactory], exports: [ - LogicFunctionDriversModule, + LogicFunctionDriverFactory, LogicFunctionResourceModule, LogicFunctionTriggerModule, LogicFunctionExecutorModule, diff --git a/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts b/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts index e7ce5afd024..e8ae3df8aa5 100644 --- a/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts +++ b/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts @@ -44,7 +44,7 @@ import { export class ConfigVariables { @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Enable or disable password authentication for users', type: ConfigVariableType.BOOLEAN, }) @@ -52,7 +52,7 @@ export class ConfigVariables { AUTH_PASSWORD_ENABLED = true; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Prefills tim@apple.dev in the login form, used in local development for quicker sign-in', type: ConfigVariableType.BOOLEAN, @@ -62,7 +62,7 @@ export class ConfigVariables { SIGN_IN_PREFILLED = false; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Require email verification for user accounts', type: ConfigVariableType.BOOLEAN, }) @@ -70,7 +70,7 @@ export class ConfigVariables { IS_EMAIL_VERIFICATION_REQUIRED = false; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Enable safe mode for outbound requests (prevents private IPs and other security risks). Applies to HTTP workflow actions, webhooks, and IMAP/SMTP/CalDAV connections.', type: ConfigVariableType.BOOLEAN, @@ -155,14 +155,14 @@ export class ConfigVariables { MESSAGING_PROVIDER_GMAIL_ENABLED = false; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Enable or disable the IMAP messaging integration', type: ConfigVariableType.BOOLEAN, }) IS_IMAP_SMTP_CALDAV_ENABLED = true; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: "Enable or disable requests to twenty-icons to get companies' icons", type: ConfigVariableType.BOOLEAN, @@ -216,7 +216,7 @@ export class ConfigVariables { AUTH_MICROSOFT_APIS_CALLBACK_URL: string; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Enable or disable the seeding of standard record page layouts', type: ConfigVariableType.BOOLEAN, @@ -333,13 +333,6 @@ export class ConfigVariables { }) EMAIL_FROM_ADDRESS = 'noreply@yourdomain.com'; - @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.EMAIL_SETTINGS, - description: 'Email address used for system notifications', - type: ConfigVariableType.STRING, - }) - EMAIL_SYSTEM_ADDRESS = 'system@yourdomain.com'; - @ConfigVariablesMetadata({ group: ConfigVariablesGroup.EMAIL_SETTINGS, description: 'Name used in the From header for outgoing emails', @@ -396,7 +389,7 @@ export class ConfigVariables { EMAIL_SMTP_PASSWORD: string; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'When enabled, only server admins can create new workspaces. Ignored during initial setup when no workspace exists.', type: ConfigVariableType.BOOLEAN, @@ -475,7 +468,6 @@ export class ConfigVariables { description: 'Type of function execution (local or Lambda)', type: ConfigVariableType.ENUM, options: Object.values(LogicFunctionDriverType), - isEnvOnly: true, }) @IsOptional() @CastToUpperSnakeCase() @@ -596,7 +588,6 @@ export class ConfigVariables { 'Code interpreter driver type - LOCAL for development (unsafe), E2B for sandboxed execution', type: ConfigVariableType.STRING, options: Object.values(CodeInterpreterDriverType), - isEnvOnly: true, }) @IsOptional() @CastToUpperSnakeCase() @@ -742,7 +733,6 @@ export class ConfigVariables { group: ConfigVariablesGroup.SERVER_CONFIG, description: 'Url for the frontend application', type: ConfigVariableType.STRING, - isEnvOnly: true, }) @IsUrl({ require_tld: false, require_protocol: true }) @IsOptional() @@ -758,16 +748,9 @@ export class ConfigVariables { DEFAULT_SUBDOMAIN = 'app'; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, - description: 'ID for the Chrome extension', - type: ConfigVariableType.STRING, - }) - @IsOptional() - CHROME_EXTENSION_ID: string; - - @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Page ID for Cal.com booking integration', + isHiddenInAdminPanel: true, type: ConfigVariableType.STRING, }) @IsOptional() @@ -805,7 +788,7 @@ export class ConfigVariables { LOG_LEVELS: LogLevel[] = ['log', 'error', 'warn']; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.METERING, + group: ConfigVariablesGroup.LOGGING, description: 'Driver used for collect metrics (OpenTelemetry or Console)', type: ConfigVariableType.ARRAY, options: ['OpenTelemetry', 'Console'], @@ -816,16 +799,7 @@ export class ConfigVariables { METER_DRIVER: MeterDriver[] = []; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.METERING, - description: 'Endpoint URL for the OpenTelemetry collector', - type: ConfigVariableType.STRING, - isEnvOnly: true, - }) - @IsOptional() - OTLP_COLLECTOR_ENDPOINT_URL: string; - - @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.EXCEPTION_HANDLER, + group: ConfigVariablesGroup.LOGGING, description: 'Driver used for logging (only console for now)', type: ConfigVariableType.ENUM, options: Object.values(LoggerDriverType), @@ -836,7 +810,7 @@ export class ConfigVariables { LOGGER_DRIVER: LoggerDriverType = LoggerDriverType.CONSOLE; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.EXCEPTION_HANDLER, + group: ConfigVariablesGroup.LOGGING, description: 'Data Source Name (DSN) for Sentry logging', type: ConfigVariableType.STRING, isSensitive: true, @@ -847,7 +821,7 @@ export class ConfigVariables { SENTRY_DSN: string; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.EXCEPTION_HANDLER, + group: ConfigVariablesGroup.LOGGING, description: 'Front-end DSN for Sentry logging', type: ConfigVariableType.STRING, isSensitive: true, @@ -857,7 +831,7 @@ export class ConfigVariables { ) SENTRY_FRONT_DSN: string; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.EXCEPTION_HANDLER, + group: ConfigVariablesGroup.LOGGING, description: 'Environment name for Sentry logging', type: ConfigVariableType.STRING, }) @@ -912,9 +886,9 @@ export class ConfigVariables { PG_DATABASE_URL: string; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.SERVER_CONFIG, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, isSensitive: true, - description: 'Database connection URL', + description: 'Optional PostgreSQL replica connection URL for read queries', type: ConfigVariableType.STRING, isEnvOnly: true, }) @@ -938,16 +912,7 @@ export class ConfigVariables { PG_SSL_ALLOW_SELF_SIGNED = false; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.SERVER_CONFIG, - description: 'Enable pg connection pool sharing across tenants', - isEnvOnly: true, - type: ConfigVariableType.BOOLEAN, - }) - @IsOptional() - PG_ENABLE_POOL_SHARING = true; - - @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.SERVER_CONFIG, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Maximum number of clients in pg connection pool', isEnvOnly: true, type: ConfigVariableType.NUMBER, @@ -957,7 +922,7 @@ export class ConfigVariables { PG_POOL_MAX_CONNECTIONS = 10; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.SERVER_CONFIG, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Idle timeout in milliseconds for pg connection pool clients', isEnvOnly: true, type: ConfigVariableType.NUMBER, @@ -967,7 +932,7 @@ export class ConfigVariables { PG_POOL_IDLE_TIMEOUT_MS = 600000; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.SERVER_CONFIG, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Allow idle pg connection pool clients to exit', isEnvOnly: true, type: ConfigVariableType.BOOLEAN, @@ -1007,7 +972,7 @@ export class ConfigVariables { REDIS_URL: string; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.SERVER_CONFIG, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, isSensitive: true, description: 'Optional separate Redis connection for queues with a different eviction policy (advanced production use case, most self-hosters do not need this)', @@ -1228,7 +1193,7 @@ export class ConfigVariables { CLOUDFLARE_PUBLIC_DOMAIN_ZONE_ID: string; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.CLOUDFLARE_CONFIG, description: 'Random string to validate queries from Cloudflare', type: ConfigVariableType.STRING, isSensitive: true, @@ -1237,7 +1202,7 @@ export class ConfigVariables { CLOUDFLARE_WEBHOOK_SECRET: string; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.CLOUDFLARE_CONFIG, description: 'Id to generate value for CNAME record to validate ownership and manage ssl for custom hostname with Cloudflare', type: ConfigVariableType.STRING, @@ -1414,13 +1379,12 @@ export class ConfigVariables { group: ConfigVariablesGroup.SERVER_CONFIG, description: 'Enable or disable multi-workspace support', type: ConfigVariableType.BOOLEAN, - isEnvOnly: true, }) @IsOptional() IS_MULTIWORKSPACE_ENABLED = false; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Number of inactive days before sending a deletion warning for workspaces. Used in the workspace deletion cron job to determine when to send warning emails.', type: ConfigVariableType.NUMBER, @@ -1433,7 +1397,7 @@ export class ConfigVariables { WORKSPACE_INACTIVE_DAYS_BEFORE_NOTIFICATION = 7; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Number of inactive days before soft deleting workspaces', type: ConfigVariableType.NUMBER, }) @@ -1445,7 +1409,7 @@ export class ConfigVariables { WORKSPACE_INACTIVE_DAYS_BEFORE_SOFT_DELETION = 14; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Number of inactive days before deleting workspaces', type: ConfigVariableType.NUMBER, }) @@ -1453,7 +1417,7 @@ export class ConfigVariables { WORKSPACE_INACTIVE_DAYS_BEFORE_DELETION = 21; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Maximum number of workspaces that can be deleted in a single execution', type: ConfigVariableType.NUMBER, @@ -1503,7 +1467,6 @@ export class ConfigVariables { description: 'Driver for captcha integration', type: ConfigVariableType.ENUM, options: Object.values(CaptchaDriverType), - isEnvOnly: true, }) @IsOptional() @CastToUpperSnakeCase() @@ -1537,7 +1500,7 @@ export class ConfigVariables { ENTERPRISE_KEY: string; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.SERVER_CONFIG, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, isSensitive: true, description: 'Signed enterprise validity token (JWT). Used as fallback when no token is stored in the database.', @@ -1549,13 +1512,14 @@ export class ConfigVariables { @ConfigVariablesMetadata({ group: ConfigVariablesGroup.SERVER_CONFIG, description: 'Base URL for the Enterprise API on twenty.com', + isHiddenInAdminPanel: true, type: ConfigVariableType.STRING, }) @IsOptional() ENTERPRISE_API_URL: string = 'https://twenty.com/api/enterprise'; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Health monitoring time window in minutes', type: ConfigVariableType.NUMBER, }) @@ -1564,7 +1528,7 @@ export class ConfigVariables { HEALTH_METRICS_TIME_WINDOW_IN_MINUTES = 5; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Enable or disable the attachment preview feature', type: ConfigVariableType.BOOLEAN, }) @@ -1576,13 +1540,14 @@ export class ConfigVariables { description: 'Twenty server version', type: ConfigVariableType.STRING, isEnvOnly: true, + isHiddenInAdminPanel: true, }) @IsOptionalOrEmptyString() @IsTwentySemVer() APP_VERSION?: string; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Enable or disable google map api usage', type: ConfigVariableType.BOOLEAN, }) @@ -1590,7 +1555,7 @@ export class ConfigVariables { IS_MAPS_AND_ADDRESS_AUTOCOMPLETE_ENABLED = false; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, isSensitive: true, description: 'Google map api key for places and map', type: ConfigVariableType.STRING, @@ -1599,20 +1564,22 @@ export class ConfigVariables { GOOGLE_MAP_API_KEY: string; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, isSensitive: true, description: 'Mintlify API key for documentation search', isEnvOnly: true, + isHiddenInAdminPanel: true, type: ConfigVariableType.STRING, }) @IsOptional() MINTLIFY_API_KEY: string; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, isSensitive: true, description: 'Mintlify subdomain for documentation search', isEnvOnly: true, + isHiddenInAdminPanel: true, type: ConfigVariableType.STRING, }) @IsOptional() @@ -1663,7 +1630,7 @@ export class ConfigVariables { AWS_SES_ACCOUNT_ID: string; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.SERVER_CONFIG, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Timeout in milliseconds for primary database queries', type: ConfigVariableType.NUMBER, isEnvOnly: true, @@ -1673,7 +1640,7 @@ export class ConfigVariables { PG_DATABASE_PRIMARY_TIMEOUT_MS: number = 10000; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.SERVER_CONFIG, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Timeout in milliseconds for replica database queries', type: ConfigVariableType.NUMBER, isEnvOnly: true, @@ -1683,7 +1650,7 @@ export class ConfigVariables { PG_DATABASE_REPLICA_TIMEOUT_MS: number = 10000; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.SERVER_CONFIG, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Default npm registry URL for resolving app packages (e.g. https://registry.npmjs.org)', type: ConfigVariableType.STRING, @@ -1693,7 +1660,7 @@ export class ConfigVariables { APP_REGISTRY_URL: string = 'https://registry.npmjs.org'; @ConfigVariablesMetadata({ - group: ConfigVariablesGroup.SERVER_CONFIG, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, isSensitive: true, description: 'Auth token for the default npm registry (for private packages)', diff --git a/packages/twenty-server/src/engine/core-modules/twenty-config/constants/config-variables-group-metadata.ts b/packages/twenty-server/src/engine/core-modules/twenty-config/constants/config-variables-group-metadata.ts index 33ebc1de47a..d0f59581bde 100644 --- a/packages/twenty-server/src/engine/core-modules/twenty-config/constants/config-variables-group-metadata.ts +++ b/packages/twenty-server/src/engine/core-modules/twenty-config/constants/config-variables-group-metadata.ts @@ -4,6 +4,7 @@ type GroupMetadata = { position: number; description: string; isHiddenOnLoad: boolean; + isHiddenInAdminPanel: boolean; }; export const CONFIG_VARIABLES_GROUP_METADATA: Record< @@ -14,126 +15,129 @@ export const CONFIG_VARIABLES_GROUP_METADATA: Record< position: 100, description: '', isHiddenOnLoad: false, + isHiddenInAdminPanel: false, }, [ConfigVariablesGroup.RATE_LIMITING]: { position: 200, description: 'We use this to limit the number of requests to the server. This is useful to prevent abuse.', isHiddenOnLoad: true, + isHiddenInAdminPanel: false, }, [ConfigVariablesGroup.STORAGE_CONFIG]: { position: 300, description: 'By default, file uploads are stored on the local filesystem, which is suitable for traditional servers. However, for ephemeral deployment servers, it is essential to configure the variables here to set up an S3-compatible file system. This ensures that files remain unaffected by server redeploys.', isHiddenOnLoad: false, + isHiddenInAdminPanel: false, }, [ConfigVariablesGroup.GOOGLE_AUTH]: { position: 400, description: 'Configure Google integration (login, calendar, email)', isHiddenOnLoad: false, + isHiddenInAdminPanel: false, }, [ConfigVariablesGroup.MICROSOFT_AUTH]: { position: 500, description: 'Configure Microsoft integration (login, calendar, email)', isHiddenOnLoad: false, + isHiddenInAdminPanel: false, }, [ConfigVariablesGroup.EMAIL_SETTINGS]: { position: 600, description: 'This is used for emails that are sent by the app such as invitations to join a workspace. This is not used to email CRM contacts.', isHiddenOnLoad: false, + isHiddenInAdminPanel: false, }, [ConfigVariablesGroup.LOGGING]: { position: 700, - description: '', - isHiddenOnLoad: true, - }, - [ConfigVariablesGroup.EXCEPTION_HANDLER]: { - position: 800, description: - 'By default, exceptions are sent to the logs. This should be enough for most self-hosting use-cases. For our cloud app we use Sentry.', + 'Configure logging, exception handling, and metrics collection. By default, exceptions go to console and metrics are disabled.', isHiddenOnLoad: true, + isHiddenInAdminPanel: false, }, - [ConfigVariablesGroup.METERING]: { - position: 900, - description: - 'By default, metrics are sent to the console. OpenTelemetry collector can be set up for self-hosting use-cases.', - isHiddenOnLoad: true, - }, - [ConfigVariablesGroup.OTHER]: { + [ConfigVariablesGroup.ADVANCED_SETTINGS]: { position: 1000, description: - "The variables in this section are mostly used for internal purposes (running our Cloud offering), but shouldn't usually be required for a simple self-hosted instance", + 'Variables that are rarely changed. Most self-hosted instances will not need to modify these.', isHiddenOnLoad: true, + isHiddenInAdminPanel: false, }, [ConfigVariablesGroup.BILLING_CONFIG]: { position: 1100, description: 'We use Stripe in our Cloud app to charge customers. Not relevant to Self-hosters.', isHiddenOnLoad: true, + isHiddenInAdminPanel: true, }, [ConfigVariablesGroup.CAPTCHA_CONFIG]: { position: 1200, description: 'This protects critical endpoints like login and signup with a captcha to prevent bot attacks. Likely unnecessary for self-hosting scenarios.', isHiddenOnLoad: true, + isHiddenInAdminPanel: false, }, [ConfigVariablesGroup.CLOUDFLARE_CONFIG]: { position: 1300, description: '', isHiddenOnLoad: true, + isHiddenInAdminPanel: true, }, [ConfigVariablesGroup.LLM]: { position: 1400, description: - 'Configure the LLM provider and model to use for the app. This is experimental and not linked to any public feature.', + 'Configure the LLM provider and model to use for the app. We recommend editing these settings through the AI tab in the admin panel instead.', isHiddenOnLoad: true, + isHiddenInAdminPanel: false, }, [ConfigVariablesGroup.LOGIC_FUNCTION_CONFIG]: { position: 1500, description: 'In our multi-tenant cloud app, we offload untrusted custom code from workflows to a function execution system (Lambda) for enhanced security and scalability. Self-hosters with a single tenant can typically ignore this configuration.', isHiddenOnLoad: true, + isHiddenInAdminPanel: false, }, [ConfigVariablesGroup.CODE_INTERPRETER_CONFIG]: { position: 1550, description: 'Configure the code interpreter for AI data analysis. Use LOCAL for development (unsafe) or E2B for sandboxed execution.', isHiddenOnLoad: true, + isHiddenInAdminPanel: false, }, [ConfigVariablesGroup.SSL]: { position: 1600, description: 'Configure this if you want to setup SSL on your server or full end-to-end encryption. If you just want basic HTTPS, a simple setup like Cloudflare in flexible mode might be easier.', isHiddenOnLoad: true, + isHiddenInAdminPanel: false, }, [ConfigVariablesGroup.SUPPORT_CHAT_CONFIG]: { position: 1700, description: 'We use this to setup a small support chat on the bottom left. Currently powered by Front.', isHiddenOnLoad: true, + isHiddenInAdminPanel: true, }, [ConfigVariablesGroup.ANALYTICS_CONFIG]: { position: 1800, description: - 'We’re running a test to perform analytics within the app. This will evolve.', + "We're running a test to perform analytics within the app. This will evolve.", isHiddenOnLoad: true, + isHiddenInAdminPanel: true, }, [ConfigVariablesGroup.TOKENS_DURATION]: { position: 1900, description: - 'These have been set to sensible default so you probably don’t need to change them unless you have a specific use-case.', - isHiddenOnLoad: true, - }, - [ConfigVariablesGroup.TWO_FACTOR_AUTHENTICATION]: { - position: 2000, - description: - 'These have been set to sensible default so you probably don’t need to change them unless you have a specific use-case.', + "These have been set to sensible default so you probably don't need to change them unless you have a specific use-case.", isHiddenOnLoad: true, + isHiddenInAdminPanel: false, }, [ConfigVariablesGroup.AWS_SES_SETTINGS]: { position: 2100, - description: 'Configure AWS SES settings for emailing domains', + description: + 'Configure AWS SES settings for emailing domains (mass outbound / feature not released yet)', isHiddenOnLoad: true, + isHiddenInAdminPanel: true, }, }; diff --git a/packages/twenty-server/src/engine/core-modules/twenty-config/conversion/__tests__/config-value-converter.service.spec.ts b/packages/twenty-server/src/engine/core-modules/twenty-config/conversion/__tests__/config-value-converter.service.spec.ts index 037d4a90d5e..261f145a52f 100644 --- a/packages/twenty-server/src/engine/core-modules/twenty-config/conversion/__tests__/config-value-converter.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/twenty-config/conversion/__tests__/config-value-converter.service.spec.ts @@ -101,7 +101,7 @@ describe('ConfigValueConverterService', () => { jest.spyOn(TypedReflect, 'getMetadata').mockReturnValueOnce({ AUTH_PASSWORD_ENABLED: { type: ConfigVariableType.BOOLEAN, - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Enable or disable password authentication for users', }, }); @@ -281,7 +281,7 @@ describe('ConfigValueConverterService', () => { jest.spyOn(TypedReflect, 'getMetadata').mockReturnValueOnce({ AUTH_PASSWORD_ENABLED: { type: ConfigVariableType.BOOLEAN, - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Enable or disable password authentication for users', }, }); @@ -434,7 +434,7 @@ describe('ConfigValueConverterService', () => { jest.spyOn(TypedReflect, 'getMetadata').mockReturnValueOnce({ CUSTOM_OBJECT: { type: 'unknown-type' as ConfigVariableType, - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Custom object', }, }); @@ -478,7 +478,7 @@ describe('ConfigValueConverterService', () => { jest.spyOn(TypedReflect, 'getMetadata').mockReturnValueOnce({ AUTH_PASSWORD_ENABLED: { type: ConfigVariableType.BOOLEAN, - group: ConfigVariablesGroup.OTHER, + group: ConfigVariablesGroup.ADVANCED_SETTINGS, description: 'Enable or disable password authentication for users', }, }); diff --git a/packages/twenty-server/src/engine/core-modules/twenty-config/decorators/config-variables-metadata.decorator.ts b/packages/twenty-server/src/engine/core-modules/twenty-config/decorators/config-variables-metadata.decorator.ts index 5a976e91773..5e8c26f3e81 100644 --- a/packages/twenty-server/src/engine/core-modules/twenty-config/decorators/config-variables-metadata.decorator.ts +++ b/packages/twenty-server/src/engine/core-modules/twenty-config/decorators/config-variables-metadata.decorator.ts @@ -15,6 +15,7 @@ export interface ConfigVariablesMetadataOptions { description: string; isSensitive?: boolean; isEnvOnly?: boolean; + isHiddenInAdminPanel?: boolean; type: ConfigVariableType; options?: ConfigVariableOptions; } diff --git a/packages/twenty-server/src/engine/core-modules/twenty-config/enums/config-variables-group.enum.ts b/packages/twenty-server/src/engine/core-modules/twenty-config/enums/config-variables-group.enum.ts index fb7aa670867..f14e954956e 100644 --- a/packages/twenty-server/src/engine/core-modules/twenty-config/enums/config-variables-group.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/twenty-config/enums/config-variables-group.enum.ts @@ -6,9 +6,7 @@ export enum ConfigVariablesGroup { MICROSOFT_AUTH = 'MICROSOFT_AUTH', EMAIL_SETTINGS = 'EMAIL_SETTINGS', LOGGING = 'LOGGING', - METERING = 'METERING', - EXCEPTION_HANDLER = 'EXCEPTION_HANDLER', - OTHER = 'OTHER', + ADVANCED_SETTINGS = 'ADVANCED_SETTINGS', BILLING_CONFIG = 'BILLING_CONFIG', CAPTCHA_CONFIG = 'CAPTCHA_CONFIG', CLOUDFLARE_CONFIG = 'CLOUDFLARE_CONFIG', @@ -19,6 +17,5 @@ export enum ConfigVariablesGroup { SUPPORT_CHAT_CONFIG = 'SUPPORT_CHAT_CONFIG', ANALYTICS_CONFIG = 'ANALYTICS_CONFIG', TOKENS_DURATION = 'TOKENS_DURATION', - TWO_FACTOR_AUTHENTICATION = 'TWO_FACTOR_AUTHENTICATION', AWS_SES_SETTINGS = 'AWS_SES_SETTINGS', } diff --git a/packages/twenty-server/test/integration/utils/create-app.ts b/packages/twenty-server/test/integration/utils/create-app.ts index 4b9e1eee865..3b8b725f189 100644 --- a/packages/twenty-server/test/integration/utils/create-app.ts +++ b/packages/twenty-server/test/integration/utils/create-app.ts @@ -14,7 +14,7 @@ import { CommandModule } from 'src/command/command.module'; import { settings } from 'src/engine/constants/settings'; import { StripeSDKMockService } from 'src/engine/core-modules/billing/stripe/stripe-sdk/mocks/stripe-sdk-mock.service'; import { StripeSDKService } from 'src/engine/core-modules/billing/stripe/stripe-sdk/services/stripe-sdk.service'; -import { CAPTCHA_DRIVER } from 'src/engine/core-modules/captcha/constants/captcha-driver.constants'; +import { CaptchaDriverFactory } from 'src/engine/core-modules/captcha/captcha-driver.factory'; import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service'; import { ExceptionHandlerMockService } from 'src/engine/core-modules/exception-handler/mocks/exception-handler-mock.service'; import { MockedUnhandledExceptionFilter } from 'src/engine/core-modules/exception-handler/mocks/mock-unhandled-exception.filter'; @@ -67,9 +67,11 @@ export const createApp = async ( .useValue(stripeSDKMockService) .overrideProvider(ExceptionHandlerService) .useValue(mockExceptionHandlerService) - .overrideProvider(CAPTCHA_DRIVER) + .overrideProvider(CaptchaDriverFactory) .useValue({ - validate: async () => ({ success: true }), + getCurrentDriver: () => ({ + validate: async () => ({ success: true }), + }), }) .overrideProvider(QUEUE_DRIVER) .useValue(syncDriver);