diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index dd0c36310..1cf1fbebd 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -400,29 +400,7 @@ export type EnvironmentVariable = { }; export enum EnvironmentVariablesGroup { - Authentication = 'Authentication', - Email = 'Email', - Logging = 'Logging', - Other = 'Other', - ServerConfig = 'ServerConfig', - Workspace = 'Workspace' -} - -export type EnvironmentVariablesGroupData = { - __typename?: 'EnvironmentVariablesGroupData'; - description: Scalars['String']; - isHiddenOnLoad: Scalars['Boolean']; - name: EnvironmentVariablesGroup; - subgroups: Array; - variables: Array; -}; - -export type EnvironmentVariablesOutput = { - __typename?: 'EnvironmentVariablesOutput'; - groups: Array; -}; - -export enum EnvironmentVariablesSubGroup { + AuthenticationTokensDuration = 'AuthenticationTokensDuration', BillingConfig = 'BillingConfig', CaptchaConfig = 'CaptchaConfig', CloudflareConfig = 'CloudflareConfig', @@ -430,10 +408,12 @@ export enum EnvironmentVariablesSubGroup { ExceptionHandler = 'ExceptionHandler', GoogleAuth = 'GoogleAuth', LLM = 'LLM', + Logging = 'Logging', MicrosoftAuth = 'MicrosoftAuth', - PasswordAuth = 'PasswordAuth', + Other = 'Other', RateLimiting = 'RateLimiting', SSL = 'SSL', + ServerConfig = 'ServerConfig', ServerlessConfig = 'ServerlessConfig', StorageConfig = 'StorageConfig', SupportChatConfig = 'SupportChatConfig', @@ -441,13 +421,19 @@ export enum EnvironmentVariablesSubGroup { TokensDuration = 'TokensDuration' } -export type EnvironmentVariablesSubgroupData = { - __typename?: 'EnvironmentVariablesSubgroupData'; +export type EnvironmentVariablesGroupData = { + __typename?: 'EnvironmentVariablesGroupData'; description: Scalars['String']; - name: EnvironmentVariablesSubGroup; + isHiddenOnLoad: Scalars['Boolean']; + name: EnvironmentVariablesGroup; variables: Array; }; +export type EnvironmentVariablesOutput = { + __typename?: 'EnvironmentVariablesOutput'; + groups: Array; +}; + export type ExecuteServerlessFunctionInput = { /** Id of the serverless function to execute */ id: Scalars['UUID']; @@ -2248,7 +2234,7 @@ export type UserLookupAdminPanelMutation = { __typename?: 'Mutation', userLookup export type GetEnvironmentVariablesGroupedQueryVariables = Exact<{ [key: string]: never; }>; -export type GetEnvironmentVariablesGroupedQuery = { __typename?: 'Query', getEnvironmentVariablesGrouped: { __typename?: 'EnvironmentVariablesOutput', groups: Array<{ __typename?: 'EnvironmentVariablesGroupData', name: EnvironmentVariablesGroup, description: string, isHiddenOnLoad: boolean, variables: Array<{ __typename?: 'EnvironmentVariable', name: string, description: string, value: string, sensitive: boolean }>, subgroups: Array<{ __typename?: 'EnvironmentVariablesSubgroupData', name: EnvironmentVariablesSubGroup, description: string, variables: Array<{ __typename?: 'EnvironmentVariable', name: string, description: string, value: string, sensitive: boolean }> }> }> } }; +export type GetEnvironmentVariablesGroupedQuery = { __typename?: 'Query', getEnvironmentVariablesGrouped: { __typename?: 'EnvironmentVariablesOutput', groups: Array<{ __typename?: 'EnvironmentVariablesGroupData', name: EnvironmentVariablesGroup, description: string, isHiddenOnLoad: boolean, variables: Array<{ __typename?: 'EnvironmentVariable', name: string, description: string, value: string, sensitive: boolean }> }> } }; export type UpdateLabPublicFeatureFlagMutationVariables = Exact<{ input: UpdateLabPublicFeatureFlagInput; @@ -3907,16 +3893,6 @@ export const GetEnvironmentVariablesGroupedDocument = gql` value sensitive } - subgroups { - name - description - variables { - name - description - value - sensitive - } - } } } } diff --git a/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminEnvVariables.tsx b/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminEnvVariables.tsx index 564f7b3dd..ab3e0485b 100644 --- a/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminEnvVariables.tsx +++ b/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminEnvVariables.tsx @@ -8,14 +8,6 @@ const StyledGroupContainer = styled.div` margin-bottom: ${({ theme }) => theme.spacing(6)}; `; -const StyledSubGroupContainer = styled.div` - margin-top: ${({ theme }) => theme.spacing(4)}; - background-color: ${({ theme }) => theme.background.secondary}; - border-radius: ${({ theme }) => theme.border.radius.sm}; - border: 1px solid ${({ theme }) => theme.border.color.medium}; - padding: ${({ theme }) => theme.spacing(4)}; -`; - const StyledGroupVariablesContainer = styled.div` background-color: ${({ theme }) => theme.background.secondary}; border-radius: ${({ theme }) => theme.border.radius.sm}; @@ -25,20 +17,24 @@ const StyledGroupVariablesContainer = styled.div` padding-right: ${({ theme }) => theme.spacing(4)}; `; -const StyledSubGroupTitle = styled.div` - color: ${({ theme }) => theme.font.color.secondary}; - font-size: ${({ theme }) => theme.font.size.md}; - font-weight: ${({ theme }) => theme.font.weight.medium}; - margin-bottom: ${({ theme }) => theme.spacing(2)}; -`; - -const StyledSubGroupDescription = styled.div``; - const StyledGroupDescription = styled.div` margin-bottom: ${({ theme }) => theme.spacing(4)}; `; -const StyledShowMoreButton = styled(Button)``; +const StyledButtonsRow = styled.div` + display: flex; + flex-wrap: wrap; + gap: ${({ theme }) => theme.spacing(2)}; + margin-bottom: ${({ theme }) => theme.spacing(6)}; +`; + +const StyledShowMoreButton = styled(Button)<{ isSelected?: boolean }>` + ${({ isSelected, theme }) => + isSelected && + ` + background-color: ${theme.background.transparent.light}; + `} +`; export const SettingsAdminEnvVariables = () => { const { data: environmentVariables } = useGetEnvironmentVariablesGroupedQuery( @@ -47,66 +43,80 @@ export const SettingsAdminEnvVariables = () => { }, ); - const [showHiddenGroups, setShowHiddenGroups] = useState< - Record - >({}); + const [selectedGroup, setSelectedGroup] = useState(null); const toggleGroupVisibility = (groupName: string) => { - setShowHiddenGroups((prev) => ({ - ...prev, - [groupName]: !prev[groupName], - })); + setSelectedGroup(selectedGroup === groupName ? null : groupName); }; + const hiddenGroups = + environmentVariables?.getEnvironmentVariablesGrouped.groups.filter( + (group) => group.isHiddenOnLoad, + ) ?? []; + + const visibleGroups = + environmentVariables?.getEnvironmentVariablesGrouped.groups.filter( + (group) => !group.isHiddenOnLoad, + ) ?? []; + + const selectedGroupData = + environmentVariables?.getEnvironmentVariablesGrouped.groups.find( + (group) => group.name === selectedGroup, + ); + return (
- {environmentVariables?.getEnvironmentVariablesGrouped.groups.map( - (group) => { - const isHidden = - group.isHiddenOnLoad && !showHiddenGroups[group.name]; - if (isHidden === true) { - return ( + {visibleGroups.map((group) => ( + + + {group.description !== '' && ( + {group.description} + )} + {group.variables.length > 0 && ( + + + + )} + + ))} + + {hiddenGroups.length > 0 && ( + <> + + {hiddenGroups.map((group) => ( toggleGroupVisibility(group.name)} - title={ - showHiddenGroups[group.name] ? 'Show Less' : 'Show More...' - } - > - ); - } - return ( - - + {group.name} variables + + ))} + + + {selectedGroupData && ( + + - {group.description !== '' && ( + {selectedGroupData.description !== '' && ( - {group.description} + {selectedGroupData.description} )} - {group.variables.length > 0 && ( + {selectedGroupData.variables.length > 0 && ( - + )} - {group.subgroups.map((subgroup) => ( - - {subgroup.name} - {subgroup.description !== '' && ( - - {subgroup.description} - - )} - - - ))} - ); - }, + )} + )}
); diff --git a/packages/twenty-front/src/modules/settings/admin-panel/graphql/queries/getEnvironmentVariablesGrouped.ts b/packages/twenty-front/src/modules/settings/admin-panel/graphql/queries/getEnvironmentVariablesGrouped.ts index bf4dec428..7c8f94c25 100644 --- a/packages/twenty-front/src/modules/settings/admin-panel/graphql/queries/getEnvironmentVariablesGrouped.ts +++ b/packages/twenty-front/src/modules/settings/admin-panel/graphql/queries/getEnvironmentVariablesGrouped.ts @@ -13,16 +13,6 @@ export const GET_ENVIRONMENT_VARIABLES_GROUPED = gql` value sensitive } - subgroups { - name - description - variables { - name - description - value - sensitive - } - } } } } diff --git a/packages/twenty-server/src/engine/core-modules/admin-panel/__tests__/admin-panel.spec.ts b/packages/twenty-server/src/engine/core-modules/admin-panel/__tests__/admin-panel.spec.ts index ab4c6c6fc..15bb17f22 100644 --- a/packages/twenty-server/src/engine/core-modules/admin-panel/__tests__/admin-panel.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/admin-panel/__tests__/admin-panel.spec.ts @@ -7,13 +7,12 @@ import { AuthExceptionCode, } from 'src/engine/core-modules/auth/auth.exception'; import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service'; -import { EnvironmentVariablesGroup } from 'src/engine/core-modules/environment/enums/environment-variables-group.enum'; +import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service'; import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; import { User } from 'src/engine/core-modules/user/user.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service'; const UserFindOneMock = jest.fn(); const WorkspaceFindOneMock = jest.fn(); @@ -34,31 +33,23 @@ jest.mock( ); jest.mock( - 'src/engine/core-modules/environment/constants/environment-variables-group-metadata', + '../../environment/constants/environment-variables-group-metadata', () => ({ ENVIRONMENT_VARIABLES_GROUP_METADATA: { - GROUP_1: { + SERVER_CONFIG: { position: 100, - description: '', + description: 'Server config description', + isHiddenOnLoad: false, }, - GROUP_2: { + RATE_LIMITING: { position: 200, - description: '', + description: 'Rate limiting description', + isHiddenOnLoad: false, }, - VISIBLE_GROUP: { + OTHER: { position: 300, - description: '', - }, - }, - }), -); - -jest.mock( - 'src/engine/core-modules/environment/constants/environment-variables-sub-group-metadata', - () => ({ - ENVIRONMENT_VARIABLES_SUB_GROUP_METADATA: { - SUBGROUP_1: { - description: '', + description: 'Other description', + isHiddenOnLoad: true, }, }, }), @@ -269,29 +260,35 @@ describe('AdminPanelService', () => { }); describe('getEnvironmentVariablesGrouped', () => { - it('should correctly group environment variables', () => { + it('should correctly group and sort environment variables', () => { EnvironmentServiceGetAllMock.mockReturnValue({ - VAR_1: { - value: 'value1', + SERVER_URL: { + value: 'http://localhost', metadata: { - group: 'GROUP_1', - description: 'Description 1', + group: 'SERVER_CONFIG', + description: 'Server URL', }, }, - VAR_2: { - value: 'value2', + RATE_LIMIT_TTL: { + value: '60', metadata: { - group: 'GROUP_1', - subGroup: 'SUBGROUP_1', - description: 'Description 2', + group: 'RATE_LIMITING', + description: 'Rate limit TTL', + }, + }, + API_KEY: { + value: 'secret-key', + metadata: { + group: 'SERVER_CONFIG', + description: 'API Key', sensitive: true, }, }, - VAR_3: { - value: 'value3', + OTHER_VAR: { + value: 'other', metadata: { - group: 'GROUP_2', - description: 'Description 3', + group: 'OTHER', + description: 'Other var', }, }, }); @@ -301,74 +298,56 @@ describe('AdminPanelService', () => { expect(result).toEqual({ groups: [ { - name: 'GROUP_1', - description: '', + name: 'SERVER_CONFIG', + description: 'Server config description', + isHiddenOnLoad: false, variables: [ { - name: 'VAR_1', - value: 'value1', - description: 'Description 1', - sensitive: false, + name: 'API_KEY', + value: 'secret-key', + description: 'API Key', + sensitive: true, }, - ], - subgroups: [ { - name: 'SUBGROUP_1', - description: '', - variables: [ - { - name: 'VAR_2', - value: 'value2', - description: 'Description 2', - sensitive: true, - }, - ], + name: 'SERVER_URL', + value: 'http://localhost', + description: 'Server URL', + sensitive: false, }, ], }, { - name: 'GROUP_2', - description: '', + name: 'RATE_LIMITING', + description: 'Rate limiting description', + isHiddenOnLoad: false, variables: [ { - name: 'VAR_3', - value: 'value3', - description: 'Description 3', + name: 'RATE_LIMIT_TTL', + value: '60', + description: 'Rate limit TTL', + sensitive: false, + }, + ], + }, + { + name: 'OTHER', + description: 'Other description', + isHiddenOnLoad: true, + variables: [ + { + name: 'OTHER_VAR', + value: 'other', + description: 'Other var', sensitive: false, }, ], - subgroups: [], }, ], }); - }); - it('should sort groups by position and variables alphabetically', () => { - EnvironmentServiceGetAllMock.mockReturnValue({ - Z_VAR: { - value: 'valueZ', - metadata: { - group: 'GROUP_1', - description: 'Description Z', - }, - }, - A_VAR: { - value: 'valueA', - metadata: { - group: 'GROUP_1', - description: 'Description A', - }, - }, - }); - - const result = service.getEnvironmentVariablesGrouped(); - - const group = result.groups.find( - (g) => g.name === ('GROUP_1' as EnvironmentVariablesGroup), - ); - - expect(group?.variables[0].name).toBe('A_VAR'); - expect(group?.variables[1].name).toBe('Z_VAR'); + expect(result.groups[0].name).toBe('SERVER_CONFIG'); + expect(result.groups[1].name).toBe('RATE_LIMITING'); + expect(result.groups[2].name).toBe('OTHER'); }); it('should handle empty environment variables', () => { @@ -380,5 +359,25 @@ describe('AdminPanelService', () => { groups: [], }); }); + + it('should handle variables with undefined metadata fields', () => { + EnvironmentServiceGetAllMock.mockReturnValue({ + TEST_VAR: { + value: 'test', + metadata: { + group: 'SERVER_CONFIG', + }, + }, + }); + + const result = service.getEnvironmentVariablesGrouped(); + + expect(result.groups[0].variables[0]).toEqual({ + name: 'TEST_VAR', + value: 'test', + description: undefined, + sensitive: false, + }); + }); }); }); 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 614cac570..01cb52e4f 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 @@ -12,10 +12,9 @@ import { AuthExceptionCode, } from 'src/engine/core-modules/auth/auth.exception'; import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service'; +import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service'; import { ENVIRONMENT_VARIABLES_GROUP_METADATA } from 'src/engine/core-modules/environment/constants/environment-variables-group-metadata'; -import { ENVIRONMENT_VARIABLES_SUB_GROUP_METADATA } from 'src/engine/core-modules/environment/constants/environment-variables-sub-group-metadata'; import { EnvironmentVariablesGroup } from 'src/engine/core-modules/environment/enums/environment-variables-group.enum'; -import { EnvironmentVariablesSubGroup } from 'src/engine/core-modules/environment/enums/environment-variables-sub-group.enum'; import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; @@ -28,7 +27,6 @@ import { User } from 'src/engine/core-modules/user/user.entity'; import { userValidator } from 'src/engine/core-modules/user/user.validate'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate'; -import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service'; @Injectable() export class AdminPanelService { @@ -175,14 +173,11 @@ export class AdminPanelService { const rawEnvVars = this.environmentService.getAll(); const groupedData = new Map< EnvironmentVariablesGroup, - { - variables: EnvironmentVariable[]; - subgroups: Map; - } + EnvironmentVariable[] >(); for (const [varName, { value, metadata }] of Object.entries(rawEnvVars)) { - const { group, subGroup, description } = metadata; + const { group, description } = metadata; const envVar: EnvironmentVariable = { name: varName, @@ -191,27 +186,11 @@ export class AdminPanelService { sensitive: metadata.sensitive ?? false, }; - let currentGroup = groupedData.get(group); - - if (!currentGroup) { - currentGroup = { - variables: [], - subgroups: new Map(), - }; - groupedData.set(group, currentGroup); + if (!groupedData.has(group)) { + groupedData.set(group, []); } - if (subGroup) { - let subgroupVars = currentGroup.subgroups.get(subGroup); - - if (!subgroupVars) { - subgroupVars = []; - currentGroup.subgroups.set(subGroup, subgroupVars); - } - subgroupVars.push(envVar); - } else { - currentGroup.variables.push(envVar); - } + groupedData.get(group)?.push(envVar); } const groups: EnvironmentVariablesGroupData[] = Array.from( @@ -223,20 +202,12 @@ export class AdminPanelService { return positionA - positionB; }) - .map(([name, data]) => ({ + .map(([name, variables]) => ({ name, description: ENVIRONMENT_VARIABLES_GROUP_METADATA[name].description, isHiddenOnLoad: ENVIRONMENT_VARIABLES_GROUP_METADATA[name].isHiddenOnLoad, - variables: data.variables.sort((a, b) => a.name.localeCompare(b.name)), - subgroups: Array.from(data.subgroups.entries()) - .sort((a, b) => a[0].localeCompare(b[0])) - .map(([name, variables]) => ({ - name, - description: - ENVIRONMENT_VARIABLES_SUB_GROUP_METADATA[name].description, - variables, - })), + variables: variables.sort((a, b) => a.name.localeCompare(b.name)), })); return { groups }; diff --git a/packages/twenty-server/src/engine/core-modules/admin-panel/dtos/environment-variables-group.dto.ts b/packages/twenty-server/src/engine/core-modules/admin-panel/dtos/environment-variables-group.dto.ts index 507e08ab8..bc75ceb2c 100644 --- a/packages/twenty-server/src/engine/core-modules/admin-panel/dtos/environment-variables-group.dto.ts +++ b/packages/twenty-server/src/engine/core-modules/admin-panel/dtos/environment-variables-group.dto.ts @@ -3,7 +3,6 @@ import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; import { EnvironmentVariablesGroup } from 'src/engine/core-modules/environment/enums/environment-variables-group.enum'; import { EnvironmentVariable } from './environment-variable.dto'; -import { EnvironmentVariablesSubgroupData } from './environment-variables-subgroup.dto'; registerEnumType(EnvironmentVariablesGroup, { name: 'EnvironmentVariablesGroup', @@ -14,9 +13,6 @@ export class EnvironmentVariablesGroupData { @Field(() => [EnvironmentVariable]) variables: EnvironmentVariable[]; - @Field(() => [EnvironmentVariablesSubgroupData]) - subgroups: EnvironmentVariablesSubgroupData[]; - @Field(() => EnvironmentVariablesGroup) name: EnvironmentVariablesGroup; diff --git a/packages/twenty-server/src/engine/core-modules/admin-panel/dtos/environment-variables-subgroup.dto.ts b/packages/twenty-server/src/engine/core-modules/admin-panel/dtos/environment-variables-subgroup.dto.ts deleted file mode 100644 index c2b647b66..000000000 --- a/packages/twenty-server/src/engine/core-modules/admin-panel/dtos/environment-variables-subgroup.dto.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; - -import { EnvironmentVariablesSubGroup } from 'src/engine/core-modules/environment/enums/environment-variables-sub-group.enum'; - -import { EnvironmentVariable } from './environment-variable.dto'; - -registerEnumType(EnvironmentVariablesSubGroup, { - name: 'EnvironmentVariablesSubGroup', -}); - -@ObjectType() -export class EnvironmentVariablesSubgroupData { - @Field(() => [EnvironmentVariable]) - variables: EnvironmentVariable[]; - - @Field(() => EnvironmentVariablesSubGroup) - name: EnvironmentVariablesSubGroup; - - @Field(() => String, { defaultValue: '' }) - description: string; -} diff --git a/packages/twenty-server/src/engine/core-modules/environment/constants/environment-variables-group-metadata.ts b/packages/twenty-server/src/engine/core-modules/environment/constants/environment-variables-group-metadata.ts index ade3e1b31..73d476e49 100644 --- a/packages/twenty-server/src/engine/core-modules/environment/constants/environment-variables-group-metadata.ts +++ b/packages/twenty-server/src/engine/core-modules/environment/constants/environment-variables-group-metadata.ts @@ -15,30 +15,102 @@ export const ENVIRONMENT_VARIABLES_GROUP_METADATA: Record< description: '', isHiddenOnLoad: false, }, - [EnvironmentVariablesGroup.Authentication]: { + [EnvironmentVariablesGroup.RateLimiting]: { + position: 200, + description: + 'We use this to limit the number of requests to the server. This is useful to prevent abuse.', + isHiddenOnLoad: false, + }, + [EnvironmentVariablesGroup.StorageConfig]: { + 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, + }, + [EnvironmentVariablesGroup.GoogleAuth]: { position: 400, - description: '', + description: 'Configure Google integration (login, calendar, email)', isHiddenOnLoad: false, }, - [EnvironmentVariablesGroup.Email]: { - position: 800, - description: '', + [EnvironmentVariablesGroup.MicrosoftAuth]: { + position: 500, + description: 'Configure Microsoft integration (login, calendar, email)', isHiddenOnLoad: false, }, - [EnvironmentVariablesGroup.Workspace]: { - position: 1000, - description: '', + [EnvironmentVariablesGroup.EmailSettings]: { + 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, }, [EnvironmentVariablesGroup.Logging]: { - position: 1200, + position: 700, description: '', isHiddenOnLoad: false, }, + [EnvironmentVariablesGroup.ExceptionHandler]: { + 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.', + isHiddenOnLoad: false, + }, [EnvironmentVariablesGroup.Other]: { - position: 1700, + position: 900, 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", isHiddenOnLoad: true, }, + [EnvironmentVariablesGroup.BillingConfig]: { + position: 1000, + description: + 'We use Stripe in our Cloud app to charge customers. Not relevant to Self-hosters.', + isHiddenOnLoad: true, + }, + [EnvironmentVariablesGroup.CaptchaConfig]: { + position: 1100, + description: + 'This protects critical endpoints like login and signup with a captcha to prevent bot attacks. Likely unnecessary for self-hosting scenarios.', + isHiddenOnLoad: true, + }, + [EnvironmentVariablesGroup.CloudflareConfig]: { + position: 1200, + description: '', + isHiddenOnLoad: true, + }, + [EnvironmentVariablesGroup.LLM]: { + position: 1300, + description: + 'Configure the LLM provider and model to use for the app. This is experimental and not linked to any public feature.', + isHiddenOnLoad: true, + }, + [EnvironmentVariablesGroup.ServerlessConfig]: { + position: 1400, + description: + 'In our multi-tenant cloud app, we offload untrusted custom code from workflows to a serverless system (Lambda) for enhanced security and scalability. Self-hosters with a single tenant can typically ignore this configuration.', + isHiddenOnLoad: true, + }, + [EnvironmentVariablesGroup.SSL]: { + position: 1500, + 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, + }, + [EnvironmentVariablesGroup.SupportChatConfig]: { + position: 1600, + description: + 'We use this to setup a small support chat on the bottom left. Currently powered by Front.', + isHiddenOnLoad: true, + }, + [EnvironmentVariablesGroup.TinybirdConfig]: { + position: 1700, + description: + 'We’re running a test to perform analytics within the app. This will evolve.', + isHiddenOnLoad: true, + }, + [EnvironmentVariablesGroup.TokensDuration]: { + position: 1800, + 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, + }, }; diff --git a/packages/twenty-server/src/engine/core-modules/environment/constants/environment-variables-group-position.ts b/packages/twenty-server/src/engine/core-modules/environment/constants/environment-variables-group-position.ts deleted file mode 100644 index 5577ac889..000000000 --- a/packages/twenty-server/src/engine/core-modules/environment/constants/environment-variables-group-position.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { EnvironmentVariablesGroup } from 'src/engine/core-modules/environment/enums/environment-variables-group.enum'; - -export const ENVIRONMENT_VARIABLES_GROUP_METADATA: Record< - EnvironmentVariablesGroup, - { position: number; description: string } -> = { - [EnvironmentVariablesGroup.ServerConfig]: { - position: 100, - description: '', - }, - [EnvironmentVariablesGroup.Authentication]: { - position: 400, - description: '', - }, - [EnvironmentVariablesGroup.Email]: { - position: 800, - description: '', - }, - [EnvironmentVariablesGroup.Workspace]: { - position: 1000, - description: '', - }, - [EnvironmentVariablesGroup.Logging]: { - position: 1200, - description: '', - }, - [EnvironmentVariablesGroup.Other]: { - position: 1700, - description: '', - }, -}; diff --git a/packages/twenty-server/src/engine/core-modules/environment/constants/environment-variables-sub-group-metadata.ts b/packages/twenty-server/src/engine/core-modules/environment/constants/environment-variables-sub-group-metadata.ts deleted file mode 100644 index 3555677e4..000000000 --- a/packages/twenty-server/src/engine/core-modules/environment/constants/environment-variables-sub-group-metadata.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { EnvironmentVariablesSubGroup } from 'src/engine/core-modules/environment/enums/environment-variables-sub-group.enum'; - -type SubGroupMetadata = { - description: string; -}; - -export const ENVIRONMENT_VARIABLES_SUB_GROUP_METADATA: Record< - EnvironmentVariablesSubGroup, - SubGroupMetadata -> = { - [EnvironmentVariablesSubGroup.PasswordAuth]: { - description: '', - }, - [EnvironmentVariablesSubGroup.GoogleAuth]: { - description: 'Configure Google integration (login, calendar, email)', - }, - [EnvironmentVariablesSubGroup.MicrosoftAuth]: { - description: 'Configure Microsoft integration (login, calendar, email)', - }, - [EnvironmentVariablesSubGroup.EmailSettings]: { - 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.', - }, - [EnvironmentVariablesSubGroup.StorageConfig]: { - description: - "By default, file uploads are stored on the local filesystem, which is suitable for traditional servers. However, for ephemeral deployment servers, it's essential to configure the variables here to set up an S3-compatible file system. This ensures that files remain unaffected by server redeploys.", - }, - [EnvironmentVariablesSubGroup.TokensDuration]: { - description: - "These have been set to sensible default so you probably don't need to change them unless you have a specific use-case.", - }, - [EnvironmentVariablesSubGroup.SSL]: { - 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.', - }, - [EnvironmentVariablesSubGroup.RateLimiting]: { - description: - 'We use this to limit the number of requests to the server. This is useful to prevent abuse.', - }, - [EnvironmentVariablesSubGroup.TinybirdConfig]: { - description: - "We're running a test to perform analytics within the app. This will evolve.", - }, - [EnvironmentVariablesSubGroup.BillingConfig]: { - description: - 'We use Stripe in our Cloud app to charge customers. Not relevant to Self-hosters.', - }, - [EnvironmentVariablesSubGroup.ExceptionHandler]: { - 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.', - }, - [EnvironmentVariablesSubGroup.SupportChatConfig]: { - description: - 'We use this to setup a small support chat on the bottom left. Currently powered by Front.', - }, - [EnvironmentVariablesSubGroup.CloudflareConfig]: { - description: '', - }, - [EnvironmentVariablesSubGroup.CaptchaConfig]: { - description: - 'This protects critical endpoints like login and signup with a captcha to prevent bot attacks. Likely unnecessary for self-hosting scenarios.', - }, - [EnvironmentVariablesSubGroup.ServerlessConfig]: { - description: - 'In our multi-tenant cloud app, we offload untrusted custom code from workflows to a serverless system (Lambda) for enhanced security and scalability. Self-hosters with a single tenant can typically ignore this configuration.', - }, - [EnvironmentVariablesSubGroup.LLM]: { - description: - 'Configure the LLM provider and model to use for the app. This is experimental and not linked to any public feature.', - }, -}; diff --git a/packages/twenty-server/src/engine/core-modules/environment/decorators/environment-variables-metadata.decorator.ts b/packages/twenty-server/src/engine/core-modules/environment/decorators/environment-variables-metadata.decorator.ts index 87a283647..3297df9c2 100644 --- a/packages/twenty-server/src/engine/core-modules/environment/decorators/environment-variables-metadata.decorator.ts +++ b/packages/twenty-server/src/engine/core-modules/environment/decorators/environment-variables-metadata.decorator.ts @@ -1,12 +1,10 @@ import { registerDecorator, ValidationOptions } from 'class-validator'; import { EnvironmentVariablesGroup } from 'src/engine/core-modules/environment/enums/environment-variables-group.enum'; -import { EnvironmentVariablesSubGroup } from 'src/engine/core-modules/environment/enums/environment-variables-sub-group.enum'; import { TypedReflect } from 'src/utils/typed-reflect'; export interface EnvironmentVariablesMetadataOptions { group: EnvironmentVariablesGroup; - subGroup?: EnvironmentVariablesSubGroup; description: string; sensitive?: boolean; } diff --git a/packages/twenty-server/src/engine/core-modules/environment/enums/environment-variables-group.enum.ts b/packages/twenty-server/src/engine/core-modules/environment/enums/environment-variables-group.enum.ts index 5b5c85ff4..905004723 100644 --- a/packages/twenty-server/src/engine/core-modules/environment/enums/environment-variables-group.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/environment/enums/environment-variables-group.enum.ts @@ -1,8 +1,20 @@ export enum EnvironmentVariablesGroup { - Authentication = 'authentication', - Email = 'email', ServerConfig = 'server-config', + RateLimiting = 'rate-limiting', + StorageConfig = 'storage-config', + GoogleAuth = 'google-auth', + MicrosoftAuth = 'microsoft-auth', + EmailSettings = 'email-settings', Logging = 'logging', - Workspace = 'workspace', + ExceptionHandler = 'exception-handler', Other = 'other', + BillingConfig = 'billing-config', + CaptchaConfig = 'captcha-config', + CloudflareConfig = 'cloudflare-config', + LLM = 'llm', + ServerlessConfig = 'serverless-config', + SSL = 'ssl', + SupportChatConfig = 'support-chat-config', + TinybirdConfig = 'tinybird-config', + TokensDuration = 'tokens-duration', } diff --git a/packages/twenty-server/src/engine/core-modules/environment/enums/environment-variables-sub-group.enum.ts b/packages/twenty-server/src/engine/core-modules/environment/enums/environment-variables-sub-group.enum.ts deleted file mode 100644 index b0987f307..000000000 --- a/packages/twenty-server/src/engine/core-modules/environment/enums/environment-variables-sub-group.enum.ts +++ /dev/null @@ -1,18 +0,0 @@ -export enum EnvironmentVariablesSubGroup { - PasswordAuth = 'password-auth', - GoogleAuth = 'google-auth', - MicrosoftAuth = 'microsoft-auth', - EmailSettings = 'email-settings', - StorageConfig = 'storage-config', - TokensDuration = 'tokens-duration', - SSL = 'ssl', - RateLimiting = 'rate-limiting', - TinybirdConfig = 'tinybird-config', - BillingConfig = 'billing-config', - ExceptionHandler = 'exception-handler', - SupportChatConfig = 'support-chat-config', - CloudflareConfig = 'cloudflare-config', - CaptchaConfig = 'captcha-config', - ServerlessConfig = 'serverless-config', - LLM = 'llm', -} diff --git a/packages/twenty-server/src/engine/core-modules/environment/environment-variables.ts b/packages/twenty-server/src/engine/core-modules/environment/environment-variables.ts index 4d0b22e76..03065864c 100644 --- a/packages/twenty-server/src/engine/core-modules/environment/environment-variables.ts +++ b/packages/twenty-server/src/engine/core-modules/environment/environment-variables.ts @@ -29,7 +29,6 @@ import { IsAWSRegion } from 'src/engine/core-modules/environment/decorators/is-a import { IsDuration } from 'src/engine/core-modules/environment/decorators/is-duration.decorator'; import { IsStrictlyLowerThan } from 'src/engine/core-modules/environment/decorators/is-strictly-lower-than.decorator'; import { EnvironmentVariablesGroup } from 'src/engine/core-modules/environment/enums/environment-variables-group.enum'; -import { EnvironmentVariablesSubGroup } from 'src/engine/core-modules/environment/enums/environment-variables-sub-group.enum'; import { ExceptionHandlerDriver } from 'src/engine/core-modules/exception-handler/interfaces'; import { StorageDriverType } from 'src/engine/core-modules/file-storage/interfaces'; import { LoggerDriverType } from 'src/engine/core-modules/logger/interfaces'; @@ -67,8 +66,7 @@ export class EnvironmentVariables { IS_EMAIL_VERIFICATION_REQUIRED = false; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.TokensDuration, + group: EnvironmentVariablesGroup.TokensDuration, description: 'Duration for which the email verification token is valid', }) @IsDuration() @@ -76,8 +74,7 @@ export class EnvironmentVariables { EMAIL_VERIFICATION_TOKEN_EXPIRES_IN = '1h'; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.TokensDuration, + group: EnvironmentVariablesGroup.TokensDuration, description: 'Duration for which the password reset token is valid', }) @IsDuration() @@ -85,23 +82,20 @@ export class EnvironmentVariables { PASSWORD_RESET_TOKEN_EXPIRES_IN = '5m'; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Authentication, - subGroup: EnvironmentVariablesSubGroup.GoogleAuth, + group: EnvironmentVariablesGroup.GoogleAuth, description: 'Enable or disable the Google Calendar integration', }) @CastToBoolean() CALENDAR_PROVIDER_GOOGLE_ENABLED = false; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Authentication, - subGroup: EnvironmentVariablesSubGroup.GoogleAuth, + group: EnvironmentVariablesGroup.GoogleAuth, description: 'Callback URL for Google Auth APIs', }) AUTH_GOOGLE_APIS_CALLBACK_URL: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Authentication, - subGroup: EnvironmentVariablesSubGroup.GoogleAuth, + group: EnvironmentVariablesGroup.GoogleAuth, description: 'Enable or disable Google Single Sign-On (SSO)', }) @CastToBoolean() @@ -110,8 +104,7 @@ export class EnvironmentVariables { AUTH_GOOGLE_ENABLED = false; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Authentication, - subGroup: EnvironmentVariablesSubGroup.GoogleAuth, + group: EnvironmentVariablesGroup.GoogleAuth, sensitive: true, description: 'Client ID for Google authentication', }) @@ -120,8 +113,7 @@ export class EnvironmentVariables { AUTH_GOOGLE_CLIENT_ID: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Authentication, - subGroup: EnvironmentVariablesSubGroup.GoogleAuth, + group: EnvironmentVariablesGroup.GoogleAuth, sensitive: true, description: 'Client secret for Google authentication', }) @@ -130,8 +122,7 @@ export class EnvironmentVariables { AUTH_GOOGLE_CLIENT_SECRET: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Authentication, - subGroup: EnvironmentVariablesSubGroup.GoogleAuth, + group: EnvironmentVariablesGroup.GoogleAuth, sensitive: true, description: 'Callback URL for Google authentication', }) @@ -140,16 +131,14 @@ export class EnvironmentVariables { AUTH_GOOGLE_CALLBACK_URL: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Authentication, - subGroup: EnvironmentVariablesSubGroup.GoogleAuth, + group: EnvironmentVariablesGroup.GoogleAuth, description: 'Enable or disable the Gmail messaging integration', }) @CastToBoolean() MESSAGING_PROVIDER_GMAIL_ENABLED = false; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Authentication, - subGroup: EnvironmentVariablesSubGroup.MicrosoftAuth, + group: EnvironmentVariablesGroup.MicrosoftAuth, description: 'Enable or disable Microsoft authentication', }) @CastToBoolean() @@ -158,8 +147,7 @@ export class EnvironmentVariables { AUTH_MICROSOFT_ENABLED = false; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Authentication, - subGroup: EnvironmentVariablesSubGroup.MicrosoftAuth, + group: EnvironmentVariablesGroup.MicrosoftAuth, sensitive: true, description: 'Client ID for Microsoft authentication', }) @@ -168,8 +156,7 @@ export class EnvironmentVariables { AUTH_MICROSOFT_CLIENT_ID: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Authentication, - subGroup: EnvironmentVariablesSubGroup.MicrosoftAuth, + group: EnvironmentVariablesGroup.MicrosoftAuth, sensitive: true, description: 'Client secret for Microsoft authentication', }) @@ -178,8 +165,7 @@ export class EnvironmentVariables { AUTH_MICROSOFT_CLIENT_SECRET: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Authentication, - subGroup: EnvironmentVariablesSubGroup.MicrosoftAuth, + group: EnvironmentVariablesGroup.MicrosoftAuth, sensitive: true, description: 'Callback URL for Microsoft authentication', }) @@ -188,8 +174,7 @@ export class EnvironmentVariables { AUTH_MICROSOFT_CALLBACK_URL: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Authentication, - subGroup: EnvironmentVariablesSubGroup.MicrosoftAuth, + group: EnvironmentVariablesGroup.MicrosoftAuth, sensitive: true, description: 'Callback URL for Microsoft APIs', }) @@ -198,16 +183,14 @@ export class EnvironmentVariables { AUTH_MICROSOFT_APIS_CALLBACK_URL: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Authentication, - subGroup: EnvironmentVariablesSubGroup.MicrosoftAuth, + group: EnvironmentVariablesGroup.MicrosoftAuth, description: 'Enable or disable the Microsoft messaging integration', }) @CastToBoolean() MESSAGING_PROVIDER_MICROSOFT_ENABLED = false; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Authentication, - subGroup: EnvironmentVariablesSubGroup.MicrosoftAuth, + group: EnvironmentVariablesGroup.MicrosoftAuth, description: 'Enable or disable the Microsoft Calendar integration', }) @CastToBoolean() @@ -224,8 +207,7 @@ export class EnvironmentVariables { ACCESS_TOKEN_SECRET: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.TokensDuration, + group: EnvironmentVariablesGroup.TokensDuration, description: 'Duration for which the access token is valid', }) @IsDuration() @@ -233,16 +215,14 @@ export class EnvironmentVariables { ACCESS_TOKEN_EXPIRES_IN = '30m'; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.TokensDuration, + group: EnvironmentVariablesGroup.TokensDuration, description: 'Duration for which the refresh token is valid', }) @IsOptional() REFRESH_TOKEN_EXPIRES_IN = '60d'; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Authentication, - subGroup: EnvironmentVariablesSubGroup.TokensDuration, + group: EnvironmentVariablesGroup.TokensDuration, description: 'Cooldown period for refreshing tokens', }) @IsDuration() @@ -250,8 +230,7 @@ export class EnvironmentVariables { REFRESH_TOKEN_COOL_DOWN = '1m'; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Authentication, - subGroup: EnvironmentVariablesSubGroup.TokensDuration, + group: EnvironmentVariablesGroup.TokensDuration, description: 'Duration for which the login token is valid', }) @IsDuration() @@ -259,8 +238,7 @@ export class EnvironmentVariables { LOGIN_TOKEN_EXPIRES_IN = '15m'; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.TokensDuration, + group: EnvironmentVariablesGroup.TokensDuration, description: 'Duration for which the file token is valid', }) @IsDuration() @@ -268,8 +246,7 @@ export class EnvironmentVariables { FILE_TOKEN_EXPIRES_IN = '1d'; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.TokensDuration, + group: EnvironmentVariablesGroup.TokensDuration, description: 'Duration for which the invitation token is valid', }) @IsDuration() @@ -277,73 +254,63 @@ export class EnvironmentVariables { INVITATION_TOKEN_EXPIRES_IN = '30d'; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.TokensDuration, + group: EnvironmentVariablesGroup.TokensDuration, description: 'Duration for which the short-term token is valid', }) SHORT_TERM_TOKEN_EXPIRES_IN = '5m'; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Email, - subGroup: EnvironmentVariablesSubGroup.EmailSettings, + group: EnvironmentVariablesGroup.EmailSettings, description: 'Email address used as the sender for outgoing emails', }) EMAIL_FROM_ADDRESS = 'noreply@yourdomain.com'; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Email, - subGroup: EnvironmentVariablesSubGroup.EmailSettings, + group: EnvironmentVariablesGroup.EmailSettings, description: 'Email address used for system notifications', }) EMAIL_SYSTEM_ADDRESS = 'system@yourdomain.com'; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Email, - subGroup: EnvironmentVariablesSubGroup.EmailSettings, + group: EnvironmentVariablesGroup.EmailSettings, description: 'Name used in the From header for outgoing emails', }) EMAIL_FROM_NAME = 'Felix from Twenty'; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Email, - subGroup: EnvironmentVariablesSubGroup.EmailSettings, + group: EnvironmentVariablesGroup.EmailSettings, description: 'Email driver to use for sending emails', }) EMAIL_DRIVER: EmailDriver = EmailDriver.Logger; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Email, - subGroup: EnvironmentVariablesSubGroup.EmailSettings, + group: EnvironmentVariablesGroup.EmailSettings, description: 'SMTP host for sending emails', }) EMAIL_SMTP_HOST: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Email, - subGroup: EnvironmentVariablesSubGroup.EmailSettings, + group: EnvironmentVariablesGroup.EmailSettings, description: 'SMTP port for sending emails', }) @CastToPositiveNumber() EMAIL_SMTP_PORT = 587; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Email, - subGroup: EnvironmentVariablesSubGroup.EmailSettings, + group: EnvironmentVariablesGroup.EmailSettings, description: 'SMTP user for authentication', }) EMAIL_SMTP_USER: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Email, - subGroup: EnvironmentVariablesSubGroup.EmailSettings, + group: EnvironmentVariablesGroup.EmailSettings, sensitive: true, description: 'SMTP password for authentication', }) EMAIL_SMTP_PASSWORD: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.ServerConfig, - subGroup: EnvironmentVariablesSubGroup.StorageConfig, + group: EnvironmentVariablesGroup.StorageConfig, description: 'Type of storage to use (local or S3)', }) @IsEnum(StorageDriverType) @@ -351,8 +318,7 @@ export class EnvironmentVariables { STORAGE_TYPE: StorageDriverType = StorageDriverType.Local; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.ServerConfig, - subGroup: EnvironmentVariablesSubGroup.StorageConfig, + group: EnvironmentVariablesGroup.StorageConfig, description: 'Local path for storage when using local storage type', }) @IsString() @@ -360,8 +326,7 @@ export class EnvironmentVariables { STORAGE_LOCAL_PATH = '.local-storage'; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.ServerConfig, - subGroup: EnvironmentVariablesSubGroup.StorageConfig, + group: EnvironmentVariablesGroup.StorageConfig, description: 'S3 region for storage when using S3 storage type', }) @ValidateIf((env) => env.STORAGE_TYPE === StorageDriverType.S3) @@ -369,8 +334,7 @@ export class EnvironmentVariables { STORAGE_S3_REGION: AwsRegion; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.ServerConfig, - subGroup: EnvironmentVariablesSubGroup.StorageConfig, + group: EnvironmentVariablesGroup.StorageConfig, description: 'S3 bucket name for storage when using S3 storage type', }) @ValidateIf((env) => env.STORAGE_TYPE === StorageDriverType.S3) @@ -378,8 +342,7 @@ export class EnvironmentVariables { STORAGE_S3_NAME: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.ServerConfig, - subGroup: EnvironmentVariablesSubGroup.StorageConfig, + group: EnvironmentVariablesGroup.StorageConfig, description: 'S3 endpoint for storage when using S3 storage type', }) @ValidateIf((env) => env.STORAGE_TYPE === StorageDriverType.S3) @@ -388,8 +351,7 @@ export class EnvironmentVariables { STORAGE_S3_ENDPOINT: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.ServerConfig, - subGroup: EnvironmentVariablesSubGroup.StorageConfig, + group: EnvironmentVariablesGroup.StorageConfig, sensitive: true, description: 'S3 access key ID for authentication when using S3 storage type', @@ -400,8 +362,7 @@ export class EnvironmentVariables { STORAGE_S3_ACCESS_KEY_ID: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.ServerConfig, - subGroup: EnvironmentVariablesSubGroup.StorageConfig, + group: EnvironmentVariablesGroup.StorageConfig, sensitive: true, description: 'S3 secret access key for authentication when using S3 storage type', @@ -412,8 +373,7 @@ export class EnvironmentVariables { STORAGE_S3_SECRET_ACCESS_KEY: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.ServerlessConfig, + group: EnvironmentVariablesGroup.ServerlessConfig, description: 'Type of serverless execution (local or Lambda)', }) @IsEnum(ServerlessDriverType) @@ -421,8 +381,7 @@ export class EnvironmentVariables { SERVERLESS_TYPE: ServerlessDriverType = ServerlessDriverType.Local; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.ServerlessConfig, + group: EnvironmentVariablesGroup.ServerlessConfig, description: 'Throttle limit for serverless function execution', }) @CastToPositiveNumber() @@ -430,16 +389,14 @@ export class EnvironmentVariables { // milliseconds @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.ServerlessConfig, + group: EnvironmentVariablesGroup.ServerlessConfig, description: 'Time-to-live for serverless function execution throttle', }) @CastToPositiveNumber() SERVERLESS_FUNCTION_EXEC_THROTTLE_TTL = 1000; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.ServerlessConfig, + group: EnvironmentVariablesGroup.ServerlessConfig, description: 'Region for AWS Lambda functions', }) @ValidateIf((env) => env.SERVERLESS_TYPE === ServerlessDriverType.Lambda) @@ -447,8 +404,7 @@ export class EnvironmentVariables { SERVERLESS_LAMBDA_REGION: AwsRegion; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.ServerlessConfig, + group: EnvironmentVariablesGroup.ServerlessConfig, description: 'IAM role for AWS Lambda functions', }) @ValidateIf((env) => env.SERVERLESS_TYPE === ServerlessDriverType.Lambda) @@ -457,8 +413,7 @@ export class EnvironmentVariables { SERVERLESS_LAMBDA_ROLE: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.ServerlessConfig, + group: EnvironmentVariablesGroup.ServerlessConfig, sensitive: true, description: 'Access key ID for AWS Lambda functions', }) @@ -468,8 +423,7 @@ export class EnvironmentVariables { SERVERLESS_LAMBDA_ACCESS_KEY_ID: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.ServerlessConfig, + group: EnvironmentVariablesGroup.ServerlessConfig, sensitive: true, description: 'Secret access key for AWS Lambda functions', }) @@ -479,8 +433,7 @@ export class EnvironmentVariables { SERVERLESS_LAMBDA_SECRET_ACCESS_KEY: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.TinybirdConfig, + group: EnvironmentVariablesGroup.TinybirdConfig, description: 'Enable or disable analytics for telemetry', }) @CastToBoolean() @@ -498,8 +451,7 @@ export class EnvironmentVariables { TELEMETRY_ENABLED = true; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.TinybirdConfig, + group: EnvironmentVariablesGroup.TinybirdConfig, sensitive: true, description: 'Ingest token for Tinybird analytics', }) @@ -508,8 +460,7 @@ export class EnvironmentVariables { TINYBIRD_INGEST_TOKEN: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.TinybirdConfig, + group: EnvironmentVariablesGroup.TinybirdConfig, sensitive: true, description: 'Workspace UUID for Tinybird analytics', }) @@ -518,8 +469,7 @@ export class EnvironmentVariables { TINYBIRD_WORKSPACE_UUID: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.TinybirdConfig, + group: EnvironmentVariablesGroup.TinybirdConfig, sensitive: true, description: 'JWT token for Tinybird analytics', }) @@ -528,8 +478,7 @@ export class EnvironmentVariables { TINYBIRD_GENERATE_JWT_TOKEN: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.BillingConfig, + group: EnvironmentVariablesGroup.BillingConfig, description: 'Enable or disable billing features', }) @CastToBoolean() @@ -538,8 +487,7 @@ export class EnvironmentVariables { IS_BILLING_ENABLED = false; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.BillingConfig, + group: EnvironmentVariablesGroup.BillingConfig, description: 'Link required for billing plan', }) @IsString() @@ -547,8 +495,7 @@ export class EnvironmentVariables { BILLING_PLAN_REQUIRED_LINK: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.BillingConfig, + group: EnvironmentVariablesGroup.BillingConfig, description: 'Duration of free trial with credit card in days', }) @IsNumber() @@ -558,8 +505,7 @@ export class EnvironmentVariables { BILLING_FREE_TRIAL_WITH_CREDIT_CARD_DURATION_IN_DAYS = 30; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.BillingConfig, + group: EnvironmentVariablesGroup.BillingConfig, description: 'Duration of free trial without credit card in days', }) @IsNumber() @@ -569,8 +515,7 @@ export class EnvironmentVariables { BILLING_FREE_TRIAL_WITHOUT_CREDIT_CARD_DURATION_IN_DAYS = 7; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.BillingConfig, + group: EnvironmentVariablesGroup.BillingConfig, sensitive: true, description: 'Stripe API key for billing', }) @@ -579,8 +524,7 @@ export class EnvironmentVariables { BILLING_STRIPE_API_KEY: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.BillingConfig, + group: EnvironmentVariablesGroup.BillingConfig, sensitive: true, description: 'Stripe webhook secret for billing', }) @@ -589,9 +533,8 @@ export class EnvironmentVariables { BILLING_STRIPE_WEBHOOK_SECRET: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, + group: EnvironmentVariablesGroup.BillingConfig, sensitive: true, - subGroup: EnvironmentVariablesSubGroup.BillingConfig, description: 'Base plan product ID for Stripe billing', }) @IsString() @@ -667,8 +610,7 @@ export class EnvironmentVariables { LOG_LEVELS: LogLevel[] = ['log', 'error', 'warn']; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Logging, - subGroup: EnvironmentVariablesSubGroup.ExceptionHandler, + group: EnvironmentVariablesGroup.ExceptionHandler, description: 'Driver used for logging (only console for now)', }) @IsEnum(LoggerDriverType) @@ -676,8 +618,7 @@ export class EnvironmentVariables { LOGGER_DRIVER: LoggerDriverType = LoggerDriverType.Console; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Logging, - subGroup: EnvironmentVariablesSubGroup.ExceptionHandler, + group: EnvironmentVariablesGroup.ExceptionHandler, description: 'Data Source Name (DSN) for Sentry logging', }) @ValidateIf( @@ -687,8 +628,7 @@ export class EnvironmentVariables { SENTRY_DSN: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Logging, - subGroup: EnvironmentVariablesSubGroup.ExceptionHandler, + group: EnvironmentVariablesGroup.ExceptionHandler, description: 'Front-end DSN for Sentry logging', }) @ValidateIf( @@ -698,8 +638,7 @@ export class EnvironmentVariables { SENTRY_FRONT_DSN: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Logging, - subGroup: EnvironmentVariablesSubGroup.ExceptionHandler, + group: EnvironmentVariablesGroup.ExceptionHandler, description: 'Release version for Sentry logging', }) @ValidateIf( @@ -710,8 +649,7 @@ export class EnvironmentVariables { SENTRY_RELEASE: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Logging, - subGroup: EnvironmentVariablesSubGroup.ExceptionHandler, + group: EnvironmentVariablesGroup.ExceptionHandler, description: 'Environment name for Sentry logging', }) @ValidateIf( @@ -722,8 +660,7 @@ export class EnvironmentVariables { SENTRY_ENVIRONMENT: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.SupportChatConfig, + group: EnvironmentVariablesGroup.SupportChatConfig, description: 'Driver used for support chat integration', }) @IsEnum(SupportDriver) @@ -731,8 +668,7 @@ export class EnvironmentVariables { SUPPORT_DRIVER: SupportDriver = SupportDriver.None; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.SupportChatConfig, + group: EnvironmentVariablesGroup.SupportChatConfig, sensitive: true, description: 'Chat ID for the support front integration', }) @@ -741,8 +677,7 @@ export class EnvironmentVariables { SUPPORT_FRONT_CHAT_ID: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.SupportChatConfig, + group: EnvironmentVariablesGroup.SupportChatConfig, sensitive: true, description: 'HMAC key for the support front integration', }) @@ -775,8 +710,7 @@ export class EnvironmentVariables { PG_SSL_ALLOW_SELF_SIGNED = false; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.TokensDuration, + group: EnvironmentVariablesGroup.TokensDuration, description: 'Time-to-live for cache storage in seconds', }) @CastToPositiveNumber() @@ -829,8 +763,7 @@ export class EnvironmentVariables { APP_SECRET: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.ServerConfig, - subGroup: EnvironmentVariablesSubGroup.RateLimiting, + group: EnvironmentVariablesGroup.RateLimiting, description: 'Maximum number of records affected by mutations', }) @CastToPositiveNumber() @@ -839,16 +772,14 @@ export class EnvironmentVariables { MUTATION_MAXIMUM_AFFECTED_RECORDS = 100; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.ServerConfig, - subGroup: EnvironmentVariablesSubGroup.RateLimiting, + group: EnvironmentVariablesGroup.RateLimiting, description: 'Time-to-live for API rate limiting in milliseconds', }) @CastToPositiveNumber() API_RATE_LIMITING_TTL = 100; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.ServerConfig, - subGroup: EnvironmentVariablesSubGroup.RateLimiting, + group: EnvironmentVariablesGroup.RateLimiting, description: 'Maximum number of requests allowed in the rate limiting window', }) @@ -856,8 +787,7 @@ export class EnvironmentVariables { API_RATE_LIMITING_LIMIT = 500; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.SSL, + group: EnvironmentVariablesGroup.SSL, description: 'Path to the SSL key for enabling HTTPS in local development', }) @IsString() @@ -865,8 +795,7 @@ export class EnvironmentVariables { SSL_KEY_PATH: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.SSL, + group: EnvironmentVariablesGroup.SSL, description: 'Path to the SSL certificate for enabling HTTPS in local development', }) @@ -875,8 +804,7 @@ export class EnvironmentVariables { SSL_CERT_PATH: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.CloudflareConfig, + group: EnvironmentVariablesGroup.CloudflareConfig, sensitive: true, description: 'API key for Cloudflare integration', }) @@ -885,8 +813,7 @@ export class EnvironmentVariables { CLOUDFLARE_API_KEY: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.CloudflareConfig, + group: EnvironmentVariablesGroup.CloudflareConfig, description: 'Zone ID for Cloudflare integration', }) @IsString() @@ -894,38 +821,33 @@ export class EnvironmentVariables { CLOUDFLARE_ZONE_ID: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.LLM, + group: EnvironmentVariablesGroup.LLM, description: 'Driver for the LLM chat model', }) LLM_CHAT_MODEL_DRIVER: LLMChatModelDriver; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.LLM, + group: EnvironmentVariablesGroup.LLM, sensitive: true, description: 'API key for OpenAI integration', }) OPENAI_API_KEY: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.LLM, + group: EnvironmentVariablesGroup.LLM, sensitive: true, description: 'Secret key for Langfuse integration', }) LANGFUSE_SECRET_KEY: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.LLM, + group: EnvironmentVariablesGroup.LLM, description: 'Public key for Langfuse integration', }) LANGFUSE_PUBLIC_KEY: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.LLM, + group: EnvironmentVariablesGroup.LLM, description: 'Driver for LLM tracing', }) LLM_TRACING_DRIVER: LLMTracingDriver = LLMTracingDriver.Console; @@ -984,24 +906,21 @@ export class EnvironmentVariables { MAX_NUMBER_OF_WORKSPACES_DELETED_PER_EXECUTION = 5; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.ServerConfig, - subGroup: EnvironmentVariablesSubGroup.RateLimiting, + group: EnvironmentVariablesGroup.RateLimiting, description: 'Throttle limit for workflow execution', }) @CastToPositiveNumber() WORKFLOW_EXEC_THROTTLE_LIMIT = 10; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.ServerConfig, - subGroup: EnvironmentVariablesSubGroup.RateLimiting, + group: EnvironmentVariablesGroup.RateLimiting, description: 'Time-to-live for workflow execution throttle in milliseconds', }) @CastToPositiveNumber() WORKFLOW_EXEC_THROTTLE_TTL = 1000; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.CaptchaConfig, + group: EnvironmentVariablesGroup.CaptchaConfig, description: 'Driver for captcha integration', }) @IsEnum(CaptchaDriverType) @@ -1009,8 +928,7 @@ export class EnvironmentVariables { CAPTCHA_DRIVER?: CaptchaDriverType; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.CaptchaConfig, + group: EnvironmentVariablesGroup.CaptchaConfig, sensitive: true, description: 'Site key for captcha integration', }) @@ -1019,8 +937,7 @@ export class EnvironmentVariables { CAPTCHA_SITE_KEY?: string; @EnvironmentVariablesMetadata({ - group: EnvironmentVariablesGroup.Other, - subGroup: EnvironmentVariablesSubGroup.CaptchaConfig, + group: EnvironmentVariablesGroup.CaptchaConfig, sensitive: true, description: 'Secret key for captcha integration', })