Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Environment variables in admin panel (read only) - backend #9943

Merged
merged 24 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
49a2dfe
decorators and enum
ehconitin Jan 30, 2025
0d798a7
WIP: backend - env getAll, new method in admin for grouping
ehconitin Jan 30, 2025
96bbf4e
remove extra isDefined
ehconitin Jan 30, 2025
77c0bd0
missing async
ehconitin Jan 30, 2025
4addc34
typo
ehconitin Jan 30, 2025
ddcc906
review
ehconitin Jan 31, 2025
b10264e
remove includeSensitive arg
ehconitin Jan 31, 2025
5b67dd5
Merge remote-tracking branch 'upstream/main' into env-var-backend
ehconitin Jan 31, 2025
7154b6d
positions for groups and alphabetical sorting for subgroups
ehconitin Jan 31, 2025
111ed1d
masking of APP_SECRET, REDIS_URL and POSTGRES_URL
ehconitin Jan 31, 2025
c51656c
Merge remote-tracking branch 'upstream/main' into env-var-backend
ehconitin Jan 31, 2025
fde7ffc
remove log
ehconitin Jan 31, 2025
214d39f
grep :)
ehconitin Jan 31, 2025
5eec6c3
Merge remote-tracking branch 'upstream/main' into env-var-backend
ehconitin Jan 31, 2025
532d7dc
unit tests for getEnvironmentVariablesGrouped
ehconitin Jan 31, 2025
5801cb9
one file per dto
ehconitin Jan 31, 2025
39a1565
unit test for position const
ehconitin Jan 31, 2025
eb7ad78
unit test on environment gatAll()
ehconitin Jan 31, 2025
bceb8b5
tests on util
ehconitin Jan 31, 2025
ed98fd2
flag redis url sensitive
ehconitin Jan 31, 2025
a18bddb
Merge branch 'main' into env-var-backend
FelixMalfait Feb 2, 2025
a12bc2f
Merge remote-tracking branch 'upstream/main' into env-var-backend
ehconitin Feb 3, 2025
e5411f1
add hide const and some other review
ehconitin Feb 3, 2025
4f64017
Merge remote-tracking branch 'upstream/main' into env-var-backend
ehconitin Feb 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 129 additions & 1 deletion packages/twenty-front/src/generated/graphql.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as Apollo from '@apollo/client';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
Expand Down Expand Up @@ -389,6 +389,69 @@ export type EmailPasswordResetLink = {
success: Scalars['Boolean'];
};

export type EnvironmentVariable = {
__typename?: 'EnvironmentVariable';
description: Scalars['String'];
name: Scalars['String'];
sensitive: Scalars['Boolean'];
value: Scalars['String'];
};

export enum EnvironmentVariablesGroup {
Analytics = 'Analytics',
Authentication = 'Authentication',
Billing = 'Billing',
Cache = 'Cache',
Database = 'Database',
Email = 'Email',
Frontend = 'Frontend',
LLM = 'LLM',
Logging = 'Logging',
QueueConfig = 'QueueConfig',
Security = 'Security',
ServerConfig = 'ServerConfig',
Serverless = 'Serverless',
Storage = 'Storage',
Support = 'Support',
Workspace = 'Workspace'
}

export type EnvironmentVariablesGroupData = {
__typename?: 'EnvironmentVariablesGroupData';
groupName: EnvironmentVariablesGroup;
standalone: Array<EnvironmentVariable>;
subgroups: Array<EnvironmentVariablesSubgroupData>;
};

export type EnvironmentVariablesOutput = {
__typename?: 'EnvironmentVariablesOutput';
groups: Array<EnvironmentVariablesGroupData>;
};

export enum EnvironmentVariablesSubGroup {
CloudflareConfig = 'CloudflareConfig',
EmailSettings = 'EmailSettings',
FrontSupportConfig = 'FrontSupportConfig',
GoogleAuth = 'GoogleAuth',
LambdaConfig = 'LambdaConfig',
MicrosoftAuth = 'MicrosoftAuth',
PasswordAuth = 'PasswordAuth',
RateLimiting = 'RateLimiting',
S3Config = 'S3Config',
SSL = 'SSL',
SentryConfig = 'SentryConfig',
SmtpConfig = 'SmtpConfig',
StripeConfig = 'StripeConfig',
TinybirdConfig = 'TinybirdConfig',
Tokens = 'Tokens'
}

export type EnvironmentVariablesSubgroupData = {
__typename?: 'EnvironmentVariablesSubgroupData';
subgroupName: EnvironmentVariablesSubGroup;
variables: Array<EnvironmentVariable>;
};

export type ExecuteServerlessFunctionInput = {
/** Id of the serverless function to execute */
id: Scalars['UUID'];
Expand Down Expand Up @@ -1166,6 +1229,7 @@ export type Query = {
findWorkspaceFromInviteHash: Workspace;
findWorkspaceInvitations: Array<WorkspaceInvitation>;
getAvailablePackages: Scalars['JSON'];
getEnvironmentVariablesGrouped: EnvironmentVariablesOutput;
getHostnameDetails?: Maybe<CustomHostnameDetails>;
getPostgresCredentials?: Maybe<PostgresCredentials>;
getProductPrices: BillingProductPricesOutput;
Expand Down Expand Up @@ -1221,6 +1285,11 @@ export type QueryGetAvailablePackagesArgs = {
};


export type QueryGetEnvironmentVariablesGroupedArgs = {
includeSensitive?: Scalars['Boolean'];
};


export type QueryGetProductPricesArgs = {
product: Scalars['String'];
};
Expand Down Expand Up @@ -2138,6 +2207,13 @@ export type UserLookupAdminPanelMutationVariables = Exact<{

export type UserLookupAdminPanelMutation = { __typename?: 'Mutation', userLookupAdminPanel: { __typename?: 'UserLookup', user: { __typename?: 'UserInfo', id: string, email: string, firstName?: string | null, lastName?: string | null }, workspaces: Array<{ __typename?: 'WorkspaceInfo', id: string, name: string, logo?: string | null, totalUsers: number, allowImpersonation: boolean, users: Array<{ __typename?: 'UserInfo', id: string, email: string, firstName?: string | null, lastName?: string | null }>, featureFlags: Array<{ __typename?: 'FeatureFlag', key: FeatureFlagKey, value: boolean }> }> } };

export type GetEnvironmentVariablesGroupedQueryVariables = Exact<{
includeSensitive?: Scalars['Boolean'];
}>;


export type GetEnvironmentVariablesGroupedQuery = { __typename?: 'Query', getEnvironmentVariablesGrouped: { __typename?: 'EnvironmentVariablesOutput', groups: Array<{ __typename?: 'EnvironmentVariablesGroupData', groupName: EnvironmentVariablesGroup, standalone: Array<{ __typename?: 'EnvironmentVariable', name: string, description: string, value: string, sensitive: boolean }>, subgroups: Array<{ __typename?: 'EnvironmentVariablesSubgroupData', subgroupName: EnvironmentVariablesSubGroup, variables: Array<{ __typename?: 'EnvironmentVariable', name: string, description: string, value: string, sensitive: boolean }> }> }> } };

export type UpdateLabPublicFeatureFlagMutationVariables = Exact<{
input: UpdateLabPublicFeatureFlagInput;
}>;
Expand Down Expand Up @@ -3749,6 +3825,58 @@ export function useUserLookupAdminPanelMutation(baseOptions?: Apollo.MutationHoo
export type UserLookupAdminPanelMutationHookResult = ReturnType<typeof useUserLookupAdminPanelMutation>;
export type UserLookupAdminPanelMutationResult = Apollo.MutationResult<UserLookupAdminPanelMutation>;
export type UserLookupAdminPanelMutationOptions = Apollo.BaseMutationOptions<UserLookupAdminPanelMutation, UserLookupAdminPanelMutationVariables>;
export const GetEnvironmentVariablesGroupedDocument = gql`
query GetEnvironmentVariablesGrouped($includeSensitive: Boolean! = false) {
getEnvironmentVariablesGrouped(includeSensitive: $includeSensitive) {
groups {
groupName
standalone {
name
description
value
sensitive
}
subgroups {
subgroupName
variables {
name
description
value
sensitive
}
}
}
}
}
`;

/**
* __useGetEnvironmentVariablesGroupedQuery__
*
* To run a query within a React component, call `useGetEnvironmentVariablesGroupedQuery` and pass it any options that fit your needs.
* When your component renders, `useGetEnvironmentVariablesGroupedQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useGetEnvironmentVariablesGroupedQuery({
* variables: {
* includeSensitive: // value for 'includeSensitive'
* },
* });
*/
export function useGetEnvironmentVariablesGroupedQuery(baseOptions?: Apollo.QueryHookOptions<GetEnvironmentVariablesGroupedQuery, GetEnvironmentVariablesGroupedQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<GetEnvironmentVariablesGroupedQuery, GetEnvironmentVariablesGroupedQueryVariables>(GetEnvironmentVariablesGroupedDocument, options);
}
export function useGetEnvironmentVariablesGroupedLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetEnvironmentVariablesGroupedQuery, GetEnvironmentVariablesGroupedQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<GetEnvironmentVariablesGroupedQuery, GetEnvironmentVariablesGroupedQueryVariables>(GetEnvironmentVariablesGroupedDocument, options);
}
export type GetEnvironmentVariablesGroupedQueryHookResult = ReturnType<typeof useGetEnvironmentVariablesGroupedQuery>;
export type GetEnvironmentVariablesGroupedLazyQueryHookResult = ReturnType<typeof useGetEnvironmentVariablesGroupedLazyQuery>;
export type GetEnvironmentVariablesGroupedQueryResult = Apollo.QueryResult<GetEnvironmentVariablesGroupedQuery, GetEnvironmentVariablesGroupedQueryVariables>;
export const UpdateLabPublicFeatureFlagDocument = gql`
mutation UpdateLabPublicFeatureFlag($input: UpdateLabPublicFeatureFlagInput!) {
updateLabPublicFeatureFlag(input: $input)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { gql } from '@apollo/client';

export const GET_ENVIRONMENT_VARIABLES_GROUPED = gql`
query GetEnvironmentVariablesGrouped($includeSensitive: Boolean! = false) {
getEnvironmentVariablesGrouped(includeSensitive: $includeSensitive) {
groups {
groupName
standalone {
ehconitin marked this conversation as resolved.
Show resolved Hide resolved
name
description
value
sensitive
}
ehconitin marked this conversation as resolved.
Show resolved Hide resolved
subgroups {
subgroupName
variables {
name
description
value
sensitive
}
}
}
}
}
`;
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { UseFilters, UseGuards } from '@nestjs/common';
import { Args, Mutation, Resolver } from '@nestjs/graphql';
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';

import { AdminPanelService } from 'src/engine/core-modules/admin-panel/admin-panel.service';
import { EnvironmentVariablesOutput } from 'src/engine/core-modules/admin-panel/dtos/environment-variables.output';
import { GetEnvironmentVariablesInput } from 'src/engine/core-modules/admin-panel/dtos/get-environment-variables.input';
import { ImpersonateInput } from 'src/engine/core-modules/admin-panel/dtos/impersonate.input';
import { ImpersonateOutput } from 'src/engine/core-modules/admin-panel/dtos/impersonate.output';
import { UpdateWorkspaceFeatureFlagInput } from 'src/engine/core-modules/admin-panel/dtos/update-workspace-feature-flag.input';
Expand Down Expand Up @@ -46,4 +48,14 @@ export class AdminPanelResolver {

return true;
}

@UseGuards(WorkspaceAuthGuard, UserAuthGuard, ImpersonateGuard)
@Query(() => EnvironmentVariablesOutput)
async getEnvironmentVariablesGrouped(
@Args() args: GetEnvironmentVariablesInput,
): Promise<EnvironmentVariablesOutput> {
return this.adminService.getEnvironmentVariablesGrouped(
args.includeSensitive,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@ import { InjectRepository } from '@nestjs/typeorm';

import { Repository } from 'typeorm';

import {
EnvironmentVariable,
EnvironmentVariablesGroupData,
EnvironmentVariablesOutput,
} from 'src/engine/core-modules/admin-panel/dtos/environment-variables.output';
import { UserLookup } from 'src/engine/core-modules/admin-panel/dtos/user-lookup.entity';
import {
AuthException,
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 { 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';
import {
Expand All @@ -25,6 +33,7 @@ import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.
export class AdminPanelService {
constructor(
private readonly loginTokenService: LoginTokenService,
private readonly environmentService: EnvironmentService,
@InjectRepository(User, 'core')
private readonly userRepository: Repository<User>,
@InjectRepository(Workspace, 'core')
Expand Down Expand Up @@ -157,4 +166,65 @@ export class AdminPanelService {
});
}
}

getEnvironmentVariablesGrouped(
includeSensitive: boolean,
ehconitin marked this conversation as resolved.
Show resolved Hide resolved
): EnvironmentVariablesOutput {
const rawEnvVars = this.environmentService.getAll(includeSensitive);
const groupedData = new Map<
EnvironmentVariablesGroup,
{
standalone: EnvironmentVariable[];
subgroups: Map<EnvironmentVariablesSubGroup, EnvironmentVariable[]>;
}
>();

for (const [varName, { value, metadata }] of Object.entries(rawEnvVars)) {
const { group, subGroup, description } = metadata;
ehconitin marked this conversation as resolved.
Show resolved Hide resolved

const envVar: EnvironmentVariable = {
name: varName,
description,
value: String(value),
ehconitin marked this conversation as resolved.
Show resolved Hide resolved
sensitive: metadata.sensitive ?? false,
};

let currentGroup = groupedData.get(group);

if (!currentGroup) {
currentGroup = {
standalone: [],
subgroups: new Map(),
};
groupedData.set(group, currentGroup);
}

if (subGroup) {
let subgroupVars = currentGroup.subgroups.get(subGroup);

if (!subgroupVars) {
subgroupVars = [];
currentGroup.subgroups.set(subGroup, subgroupVars);
}
subgroupVars.push(envVar);
} else {
currentGroup.standalone.push(envVar);
}
}

const groups: EnvironmentVariablesGroupData[] = Array.from(
groupedData.entries(),
).map(([groupName, data]) => ({
groupName,
standalone: data.standalone,
subgroups: Array.from(data.subgroups.entries()).map(
([subgroupName, variables]) => ({
subgroupName,
variables,
}),
),
}));

return { groups };
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';

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';

registerEnumType(EnvironmentVariablesGroup, {
name: 'EnvironmentVariablesGroup',
});

registerEnumType(EnvironmentVariablesSubGroup, {
name: 'EnvironmentVariablesSubGroup',
});

@ObjectType()
export class EnvironmentVariable {
@Field()
name: string;

@Field()
description: string;

@Field()
value: string;
ehconitin marked this conversation as resolved.
Show resolved Hide resolved

@Field()
sensitive: boolean;
ehconitin marked this conversation as resolved.
Show resolved Hide resolved
}

@ObjectType()
export class EnvironmentVariablesSubgroupData {
ehconitin marked this conversation as resolved.
Show resolved Hide resolved
@Field(() => [EnvironmentVariable])
variables: EnvironmentVariable[];

@Field(() => EnvironmentVariablesSubGroup)
subgroupName: EnvironmentVariablesSubGroup;
}

@ObjectType()
export class EnvironmentVariablesGroupData {
@Field(() => [EnvironmentVariable])
standalone: EnvironmentVariable[];

@Field(() => [EnvironmentVariablesSubgroupData])
subgroups: EnvironmentVariablesSubgroupData[];

@Field(() => EnvironmentVariablesGroup)
groupName: EnvironmentVariablesGroup;
}

@ObjectType()
export class EnvironmentVariablesOutput {
@Field(() => [EnvironmentVariablesGroupData])
groups: EnvironmentVariablesGroupData[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ArgsType, Field } from '@nestjs/graphql';

@ArgsType()
export class GetEnvironmentVariablesInput {
@Field(() => Boolean, { defaultValue: false })
includeSensitive: boolean;
ehconitin marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading