Fix-issue-370 (#9996)

Fixes the issue from introduced when alowing gmail and outlook.

fixes https://github.com/twentyhq/core-team-issues/issues/370
This commit is contained in:
Guillim
2025-02-04 15:20:35 +01:00
committed by GitHub
parent b9b7700155
commit 53b51c8bba
19 changed files with 338 additions and 70 deletions

View File

@ -258,6 +258,10 @@ export type ClientConfig = {
defaultSubdomain?: Maybe<Scalars['String']['output']>; defaultSubdomain?: Maybe<Scalars['String']['output']>;
frontDomain: Scalars['String']['output']; frontDomain: Scalars['String']['output'];
isEmailVerificationRequired: Scalars['Boolean']['output']; isEmailVerificationRequired: Scalars['Boolean']['output'];
isGoogleCalendarEnabled: Scalars['Boolean']['output'];
isGoogleMessagingEnabled: Scalars['Boolean']['output'];
isMicrosoftCalendarEnabled: Scalars['Boolean']['output'];
isMicrosoftMessagingEnabled: Scalars['Boolean']['output'];
isMultiWorkspaceEnabled: Scalars['Boolean']['output']; isMultiWorkspaceEnabled: Scalars['Boolean']['output'];
publicFeatureFlags: Array<PublicFeatureFlag>; publicFeatureFlags: Array<PublicFeatureFlag>;
sentry: Sentry; sentry: Sentry;
@ -462,6 +466,69 @@ export type EmailPasswordResetLink = {
success: Scalars['Boolean']['output']; success: Scalars['Boolean']['output'];
}; };
export type EnvironmentVariable = {
__typename?: 'EnvironmentVariable';
description: Scalars['String']['output'];
name: Scalars['String']['output'];
sensitive: Scalars['Boolean']['output'];
value: Scalars['String']['output'];
};
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;
subgroups: Array<EnvironmentVariablesSubgroupData>;
variables: Array<EnvironmentVariable>;
};
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 = { export type ExecuteServerlessFunctionInput = {
/** Id of the serverless function to execute */ /** Id of the serverless function to execute */
id: Scalars['UUID']['input']; id: Scalars['UUID']['input'];
@ -819,7 +886,7 @@ export type Mutation = {
track: Analytics; track: Analytics;
unsyncRemoteTable: RemoteTable; unsyncRemoteTable: RemoteTable;
updateBillingSubscription: BillingUpdateOutput; updateBillingSubscription: BillingUpdateOutput;
updateLabPublicFeatureFlag: Scalars['Boolean']['output']; updateLabPublicFeatureFlag: FeatureFlag;
updateOneField: Field; updateOneField: Field;
updateOneObject: Object; updateOneObject: Object;
updateOneRemoteServer: RemoteServer; updateOneRemoteServer: RemoteServer;
@ -1314,10 +1381,12 @@ export type Query = {
findWorkspaceFromInviteHash: Workspace; findWorkspaceFromInviteHash: Workspace;
findWorkspaceInvitations: Array<WorkspaceInvitation>; findWorkspaceInvitations: Array<WorkspaceInvitation>;
getAvailablePackages: Scalars['JSON']['output']; getAvailablePackages: Scalars['JSON']['output'];
getEnvironmentVariablesGrouped: EnvironmentVariablesOutput;
getHostnameDetails?: Maybe<CustomHostnameDetails>; getHostnameDetails?: Maybe<CustomHostnameDetails>;
getPostgresCredentials?: Maybe<PostgresCredentials>; getPostgresCredentials?: Maybe<PostgresCredentials>;
getProductPrices: BillingProductPricesOutput; getProductPrices: BillingProductPricesOutput;
getPublicWorkspaceDataBySubdomain: PublicWorkspaceDataOutput; getPublicWorkspaceDataBySubdomain: PublicWorkspaceDataOutput;
getRoles: Array<RoleDto>;
getServerlessFunctionSourceCode?: Maybe<Scalars['JSON']['output']>; getServerlessFunctionSourceCode?: Maybe<Scalars['JSON']['output']>;
getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal; getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal;
getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal; getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal;
@ -1584,6 +1653,16 @@ export type ResendEmailVerificationTokenOutput = {
success: Scalars['Boolean']['output']; success: Scalars['Boolean']['output'];
}; };
export type RoleDto = {
__typename?: 'RoleDTO';
canUpdateAllSettings: Scalars['Boolean']['output'];
description?: Maybe<Scalars['String']['output']>;
id: Scalars['String']['output'];
isEditable: Scalars['Boolean']['output'];
label: Scalars['String']['output'];
workspaceMembers: Array<WorkspaceMember>;
};
export type RunWorkflowVersionInput = { export type RunWorkflowVersionInput = {
/** Execution result in JSON format */ /** Execution result in JSON format */
payload?: InputMaybe<Scalars['JSON']['input']>; payload?: InputMaybe<Scalars['JSON']['input']>;
@ -1822,7 +1901,6 @@ export type UpdateFieldInput = {
description?: InputMaybe<Scalars['String']['input']>; description?: InputMaybe<Scalars['String']['input']>;
icon?: InputMaybe<Scalars['String']['input']>; icon?: InputMaybe<Scalars['String']['input']>;
isActive?: InputMaybe<Scalars['Boolean']['input']>; isActive?: InputMaybe<Scalars['Boolean']['input']>;
isCustom?: InputMaybe<Scalars['Boolean']['input']>;
isLabelSyncedWithName?: InputMaybe<Scalars['Boolean']['input']>; isLabelSyncedWithName?: InputMaybe<Scalars['Boolean']['input']>;
isNullable?: InputMaybe<Scalars['Boolean']['input']>; isNullable?: InputMaybe<Scalars['Boolean']['input']>;
isSystem?: InputMaybe<Scalars['Boolean']['input']>; isSystem?: InputMaybe<Scalars['Boolean']['input']>;

View File

@ -251,6 +251,10 @@ export type ClientConfig = {
defaultSubdomain?: Maybe<Scalars['String']>; defaultSubdomain?: Maybe<Scalars['String']>;
frontDomain: Scalars['String']; frontDomain: Scalars['String'];
isEmailVerificationRequired: Scalars['Boolean']; isEmailVerificationRequired: Scalars['Boolean'];
isGoogleCalendarEnabled: Scalars['Boolean'];
isGoogleMessagingEnabled: Scalars['Boolean'];
isMicrosoftCalendarEnabled: Scalars['Boolean'];
isMicrosoftMessagingEnabled: Scalars['Boolean'];
isMultiWorkspaceEnabled: Scalars['Boolean']; isMultiWorkspaceEnabled: Scalars['Boolean'];
publicFeatureFlags: Array<PublicFeatureFlag>; publicFeatureFlags: Array<PublicFeatureFlag>;
sentry: Sentry; sentry: Sentry;
@ -1246,6 +1250,7 @@ export type Query = {
getPostgresCredentials?: Maybe<PostgresCredentials>; getPostgresCredentials?: Maybe<PostgresCredentials>;
getProductPrices: BillingProductPricesOutput; getProductPrices: BillingProductPricesOutput;
getPublicWorkspaceDataBySubdomain: PublicWorkspaceDataOutput; getPublicWorkspaceDataBySubdomain: PublicWorkspaceDataOutput;
getRoles: Array<RoleDto>;
getServerlessFunctionSourceCode?: Maybe<Scalars['JSON']>; getServerlessFunctionSourceCode?: Maybe<Scalars['JSON']>;
getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal; getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal;
getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal; getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal;
@ -1444,6 +1449,16 @@ export type ResendEmailVerificationTokenOutput = {
success: Scalars['Boolean']; success: Scalars['Boolean'];
}; };
export type RoleDto = {
__typename?: 'RoleDTO';
canUpdateAllSettings: Scalars['Boolean'];
description?: Maybe<Scalars['String']>;
id: Scalars['String'];
isEditable: Scalars['Boolean'];
label: Scalars['String'];
workspaceMembers: Array<WorkspaceMember>;
};
export type RunWorkflowVersionInput = { export type RunWorkflowVersionInput = {
/** Execution result in JSON format */ /** Execution result in JSON format */
payload?: InputMaybe<Scalars['JSON']>; payload?: InputMaybe<Scalars['JSON']>;
@ -2192,7 +2207,7 @@ export type UpdateBillingSubscriptionMutation = { __typename?: 'Mutation', updat
export type GetClientConfigQueryVariables = Exact<{ [key: string]: never; }>; export type GetClientConfigQueryVariables = Exact<{ [key: string]: never; }>;
export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, isMultiWorkspaceEnabled: boolean, isEmailVerificationRequired: boolean, defaultSubdomain?: string | null, frontDomain: string, debugMode: boolean, analyticsEnabled: boolean, chromeExtensionId?: string | null, canManageFeatureFlags: boolean, billing: { __typename?: 'Billing', isBillingEnabled: boolean, billingUrl?: string | null, trialPeriods: Array<{ __typename?: 'BillingTrialPeriodDTO', duration: number, isCreditCardRequired: boolean }> }, authProviders: { __typename?: 'AuthProviders', google: boolean, password: boolean, microsoft: boolean, sso: Array<{ __typename?: 'SSOIdentityProvider', id: string, name: string, type: IdentityProviderType, status: SsoIdentityProviderStatus, issuer: string }> }, support: { __typename?: 'Support', supportDriver: string, supportFrontChatId?: string | null }, sentry: { __typename?: 'Sentry', dsn?: string | null, environment?: string | null, release?: string | null }, captcha: { __typename?: 'Captcha', provider?: CaptchaDriverType | null, siteKey?: string | null }, api: { __typename?: 'ApiConfig', mutationMaximumAffectedRecords: number }, publicFeatureFlags: Array<{ __typename?: 'PublicFeatureFlag', key: FeatureFlagKey, metadata: { __typename?: 'PublicFeatureFlagMetadata', label: string, description: string, imagePath: string } }> } }; export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, isMultiWorkspaceEnabled: boolean, isEmailVerificationRequired: boolean, defaultSubdomain?: string | null, frontDomain: string, debugMode: boolean, analyticsEnabled: boolean, chromeExtensionId?: string | null, canManageFeatureFlags: boolean, isMicrosoftMessagingEnabled: boolean, isMicrosoftCalendarEnabled: boolean, isGoogleMessagingEnabled: boolean, isGoogleCalendarEnabled: boolean, billing: { __typename?: 'Billing', isBillingEnabled: boolean, billingUrl?: string | null, trialPeriods: Array<{ __typename?: 'BillingTrialPeriodDTO', duration: number, isCreditCardRequired: boolean }> }, authProviders: { __typename?: 'AuthProviders', google: boolean, password: boolean, microsoft: boolean, sso: Array<{ __typename?: 'SSOIdentityProvider', id: string, name: string, type: IdentityProviderType, status: SsoIdentityProviderStatus, issuer: string }> }, support: { __typename?: 'Support', supportDriver: string, supportFrontChatId?: string | null }, sentry: { __typename?: 'Sentry', dsn?: string | null, environment?: string | null, release?: string | null }, captcha: { __typename?: 'Captcha', provider?: CaptchaDriverType | null, siteKey?: string | null }, api: { __typename?: 'ApiConfig', mutationMaximumAffectedRecords: number }, publicFeatureFlags: Array<{ __typename?: 'PublicFeatureFlag', key: FeatureFlagKey, metadata: { __typename?: 'PublicFeatureFlagMetadata', label: string, description: string, imagePath: string } }> } };
export type SkipSyncEmailOnboardingStepMutationVariables = Exact<{ [key: string]: never; }>; export type SkipSyncEmailOnboardingStepMutationVariables = Exact<{ [key: string]: never; }>;
@ -3677,6 +3692,10 @@ export const GetClientConfigDocument = gql`
imagePath imagePath
} }
} }
isMicrosoftMessagingEnabled
isMicrosoftCalendarEnabled
isGoogleMessagingEnabled
isGoogleCalendarEnabled
} }
} }
`; `;

View File

@ -9,6 +9,10 @@ import { isAnalyticsEnabledState } from '@/client-config/states/isAnalyticsEnabl
import { isDebugModeState } from '@/client-config/states/isDebugModeState'; import { isDebugModeState } from '@/client-config/states/isDebugModeState';
import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState'; import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState';
import { isEmailVerificationRequiredState } from '@/client-config/states/isEmailVerificationRequiredState'; import { isEmailVerificationRequiredState } from '@/client-config/states/isEmailVerificationRequiredState';
import { isGoogleCalendarEnabledState } from '@/client-config/states/isGoogleCalendarEnabledState';
import { isGoogleMessagingEnabledState } from '@/client-config/states/isGoogleMessagingEnabledState';
import { isMicrosoftCalendarEnabledState } from '@/client-config/states/isMicrosoftCalendarEnabledState';
import { isMicrosoftMessagingEnabledState } from '@/client-config/states/isMicrosoftMessagingEnabledState';
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState'; import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
import { labPublicFeatureFlagsState } from '@/client-config/states/labPublicFeatureFlagsState'; import { labPublicFeatureFlagsState } from '@/client-config/states/labPublicFeatureFlagsState';
import { sentryConfigState } from '@/client-config/states/sentryConfigState'; import { sentryConfigState } from '@/client-config/states/sentryConfigState';
@ -57,6 +61,22 @@ export const ClientConfigProviderEffect = () => {
labPublicFeatureFlagsState, labPublicFeatureFlagsState,
); );
const setMicrosoftMessagingEnabled = useSetRecoilState(
isMicrosoftMessagingEnabledState,
);
const setMicrosoftCalendarEnabled = useSetRecoilState(
isMicrosoftCalendarEnabledState,
);
const setGoogleMessagingEnabled = useSetRecoilState(
isGoogleMessagingEnabledState,
);
const setGoogleCalendarEnabled = useSetRecoilState(
isGoogleCalendarEnabledState,
);
const { data, loading, error } = useGetClientConfigQuery({ const { data, loading, error } = useGetClientConfigQuery({
skip: clientConfigApiStatus.isLoaded, skip: clientConfigApiStatus.isLoaded,
}); });
@ -123,6 +143,12 @@ export const ClientConfigProviderEffect = () => {
}); });
setCanManageFeatureFlags(data?.clientConfig?.canManageFeatureFlags); setCanManageFeatureFlags(data?.clientConfig?.canManageFeatureFlags);
setLabPublicFeatureFlags(data?.clientConfig?.publicFeatureFlags); setLabPublicFeatureFlags(data?.clientConfig?.publicFeatureFlags);
setMicrosoftMessagingEnabled(
data?.clientConfig?.isMicrosoftMessagingEnabled,
);
setMicrosoftCalendarEnabled(data?.clientConfig?.isMicrosoftCalendarEnabled);
setGoogleMessagingEnabled(data?.clientConfig?.isGoogleMessagingEnabled);
setGoogleCalendarEnabled(data?.clientConfig?.isGoogleCalendarEnabled);
}, [ }, [
data, data,
setIsDebugMode, setIsDebugMode,
@ -143,6 +169,10 @@ export const ClientConfigProviderEffect = () => {
setAuthProviders, setAuthProviders,
setCanManageFeatureFlags, setCanManageFeatureFlags,
setLabPublicFeatureFlags, setLabPublicFeatureFlags,
setMicrosoftMessagingEnabled,
setMicrosoftCalendarEnabled,
setGoogleMessagingEnabled,
setGoogleCalendarEnabled,
]); ]);
return <></>; return <></>;

View File

@ -56,6 +56,10 @@ export const GET_CLIENT_CONFIG = gql`
imagePath imagePath
} }
} }
isMicrosoftMessagingEnabled
isMicrosoftCalendarEnabled
isGoogleMessagingEnabled
isGoogleCalendarEnabled
} }
} }
`; `;

View File

@ -0,0 +1,6 @@
import { createState } from '@ui/utilities/state/utils/createState';
export const isGoogleCalendarEnabledState = createState<boolean>({
key: 'isGoogleCalendarEnabled',
defaultValue: false,
});

View File

@ -0,0 +1,6 @@
import { createState } from '@ui/utilities/state/utils/createState';
export const isGoogleMessagingEnabledState = createState<boolean>({
key: 'isGoogleMessagingEnabled',
defaultValue: false,
});

View File

@ -0,0 +1,6 @@
import { createState } from '@ui/utilities/state/utils/createState';
export const isMicrosoftCalendarEnabledState = createState<boolean>({
key: 'isMicrosoftCalendarEnabled',
defaultValue: false,
});

View File

@ -0,0 +1,6 @@
import { createState } from '@ui/utilities/state/utils/createState';
export const isMicrosoftMessagingEnabledState = createState<boolean>({
key: 'isMicrosoftMessagingEnabled',
defaultValue: false,
});

View File

@ -1,6 +1,11 @@
import { isGoogleCalendarEnabledState } from '@/client-config/states/isGoogleCalendarEnabledState';
import { isGoogleMessagingEnabledState } from '@/client-config/states/isGoogleMessagingEnabledState';
import { isMicrosoftCalendarEnabledState } from '@/client-config/states/isMicrosoftCalendarEnabledState';
import { isMicrosoftMessagingEnabledState } from '@/client-config/states/isMicrosoftMessagingEnabledState';
import { useTriggerApisOAuth } from '@/settings/accounts/hooks/useTriggerApiOAuth'; import { useTriggerApisOAuth } from '@/settings/accounts/hooks/useTriggerApiOAuth';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro'; import { useLingui } from '@lingui/react/macro';
import { useRecoilValue } from 'recoil';
import { import {
Button, Button,
Card, Card,
@ -33,23 +38,40 @@ export const SettingsAccountsListEmptyStateCard = ({
const { t } = useLingui(); const { t } = useLingui();
const isGoogleMessagingEnabled = useRecoilValue(
isGoogleMessagingEnabledState,
);
const isMicrosoftMessagingEnabled = useRecoilValue(
isMicrosoftMessagingEnabledState,
);
const isGoogleCalendarEnabled = useRecoilValue(isGoogleCalendarEnabledState);
const isMicrosoftCalendarEnabled = useRecoilValue(
isMicrosoftCalendarEnabledState,
);
return ( return (
<Card> <Card>
<StyledHeader>{label || t`No connected account`}</StyledHeader> <StyledHeader>{label || t`No connected account`}</StyledHeader>
<StyledBody> <StyledBody>
<Button {(isGoogleMessagingEnabled || isGoogleCalendarEnabled) && (
Icon={IconGoogle} <Button
title={t`Connect with Google`} Icon={IconGoogle}
variant="secondary" title={t`Connect with Google`}
onClick={() => triggerApisOAuth('google')} variant="secondary"
/> onClick={() => triggerApisOAuth('google')}
/>
)}
<Button {(isMicrosoftMessagingEnabled || isMicrosoftCalendarEnabled) && (
Icon={IconMicrosoft} <Button
title={t`Connect with Microsoft`} Icon={IconMicrosoft}
variant="secondary" title={t`Connect with Microsoft`}
onClick={() => triggerApisOAuth('microsoft')} variant="secondary"
/> onClick={() => triggerApisOAuth('microsoft')}
/>
)}
</StyledBody> </StyledBody>
</Card> </Card>
); );

View File

@ -53,4 +53,8 @@ export const mockedClientConfig: ClientConfig = {
api: { mutationMaximumAffectedRecords: 100 }, api: { mutationMaximumAffectedRecords: 100 },
canManageFeatureFlags: true, canManageFeatureFlags: true,
publicFeatureFlags: [], publicFeatureFlags: [],
isMicrosoftMessagingEnabled: true,
isMicrosoftCalendarEnabled: true,
isGoogleMessagingEnabled: true,
isGoogleCalendarEnabled: true,
}; };

View File

@ -21,6 +21,8 @@ FRONT_PORT=3001
# FILE_TOKEN_EXPIRES_IN=1d # FILE_TOKEN_EXPIRES_IN=1d
# MESSAGING_PROVIDER_GMAIL_ENABLED=false # MESSAGING_PROVIDER_GMAIL_ENABLED=false
# CALENDAR_PROVIDER_GOOGLE_ENABLED=false # CALENDAR_PROVIDER_GOOGLE_ENABLED=false
# MESSAGING_PROVIDER_MICROSOFT_ENABLED=false
# CALENDAR_PROVIDER_MICROSOFT_ENABLED=false
# IS_BILLING_ENABLED=false # IS_BILLING_ENABLED=false
# BILLING_PLAN_REQUIRED_LINK=https://twenty.com/stripe-redirection # BILLING_PLAN_REQUIRED_LINK=https://twenty.com/stripe-redirection
# AUTH_PASSWORD_ENABLED=false # AUTH_PASSWORD_ENABLED=false

View File

@ -15,6 +15,12 @@ CACHE_STORAGE_TYPE=redis
AUTH_GOOGLE_ENABLED=false AUTH_GOOGLE_ENABLED=false
MESSAGING_PROVIDER_GMAIL_ENABLED=false MESSAGING_PROVIDER_GMAIL_ENABLED=false
CALENDAR_PROVIDER_GOOGLE_ENABLED=false CALENDAR_PROVIDER_GOOGLE_ENABLED=false
MESSAGING_PROVIDER_MICROSOFT_ENABLED=false
CALENDAR_PROVIDER_MICROSOFT_ENABLED=false
AUTH_GOOGLE_CALLBACK_URL=http://localhost:3000/auth/google/redirect AUTH_GOOGLE_CALLBACK_URL=http://localhost:3000/auth/google/redirect
AUTH_GOOGLE_APIS_CALLBACK_URL=http://localhost:3000/auth/google-apis/get-access-token AUTH_GOOGLE_APIS_CALLBACK_URL=http://localhost:3000/auth/google-apis/get-access-token
MESSAGING_PROVIDER_GMAIL_CALLBACK_URL=http://localhost:3000/auth/google-gmail/get-access-token MESSAGING_PROVIDER_GMAIL_CALLBACK_URL=http://localhost:3000/auth/google-gmail/get-access-token
AUTH_MICROSOFT_CALLBACK_URL=http://localhost:3000/auth/microsoft/redirect
AUTH_MICROSOFT_APIS_CALLBACK_URL=http://localhost:3000/auth/microsoft-apis/get-access-token

View File

@ -26,6 +26,16 @@ export class MicrosoftAPIsOauthExchangeCodeForTokenGuard extends AuthGuard(
const request = context.switchToHttp().getRequest(); const request = context.switchToHttp().getRequest();
const state = JSON.parse(request.query.state); const state = JSON.parse(request.query.state);
if (
!this.environmentService.get('MESSAGING_PROVIDER_MICROSOFT_ENABLED') &&
!this.environmentService.get('CALENDAR_PROVIDER_MICROSOFT_ENABLED')
) {
throw new AuthException(
'Microsoft apis auth is not enabled',
AuthExceptionCode.MICROSOFT_API_AUTH_DISABLED,
);
}
new MicrosoftAPIsOauthExchangeCodeForTokenStrategy( new MicrosoftAPIsOauthExchangeCodeForTokenStrategy(
this.environmentService, this.environmentService,
); );

View File

@ -4,6 +4,10 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import {
AuthException,
AuthExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception';
import { MicrosoftAPIsOauthRequestCodeStrategy } from 'src/engine/core-modules/auth/strategies/microsoft-apis-oauth-request-code.auth.strategy'; import { MicrosoftAPIsOauthRequestCodeStrategy } from 'src/engine/core-modules/auth/strategies/microsoft-apis-oauth-request-code.auth.strategy';
import { TransientTokenService } from 'src/engine/core-modules/auth/token/services/transient-token.service'; import { TransientTokenService } from 'src/engine/core-modules/auth/token/services/transient-token.service';
import { setRequestExtraParams } from 'src/engine/core-modules/auth/utils/google-apis-set-request-extra-params.util'; import { setRequestExtraParams } from 'src/engine/core-modules/auth/utils/google-apis-set-request-extra-params.util';
@ -33,6 +37,16 @@ export class MicrosoftAPIsOauthRequestCodeGuard extends AuthGuard(
let workspace: Workspace | null = null; let workspace: Workspace | null = null;
try { try {
if (
!this.environmentService.get('MESSAGING_PROVIDER_MICROSOFT_ENABLED') &&
!this.environmentService.get('CALENDAR_PROVIDER_MICROSOFT_ENABLED')
) {
throw new AuthException(
'Microsoft apis auth is not enabled',
AuthExceptionCode.MICROSOFT_API_AUTH_DISABLED,
);
}
const request = context.switchToHttp().getRequest(); const request = context.switchToHttp().getRequest();
const { workspaceId } = const { workspaceId } =

View File

@ -7,6 +7,7 @@ import { v4 } from 'uuid';
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
import { getMicrosoftApisOauthScopes } from 'src/engine/core-modules/auth/utils/get-microsoft-apis-oauth-scopes'; import { getMicrosoftApisOauthScopes } from 'src/engine/core-modules/auth/utils/get-microsoft-apis-oauth-scopes';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator'; import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
@ -48,6 +49,7 @@ export class MicrosoftAPIsService {
private readonly workspaceEventEmitter: WorkspaceEventEmitter, private readonly workspaceEventEmitter: WorkspaceEventEmitter,
@InjectRepository(ObjectMetadataEntity, 'metadata') @InjectRepository(ObjectMetadataEntity, 'metadata')
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>, private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
private readonly environmentService: EnvironmentService,
) {} ) {}
async refreshMicrosoftRefreshToken(input: { async refreshMicrosoftRefreshToken(input: {
@ -167,37 +169,42 @@ export class MicrosoftAPIsService {
workspaceId, workspaceId,
}); });
const newCalendarChannel = await calendarChannelRepository.save( if (
{ this.environmentService.get('CALENDAR_PROVIDER_MICROSOFT_ENABLED')
id: v4(), ) {
connectedAccountId: newOrExistingConnectedAccountId, const newCalendarChannel = await calendarChannelRepository.save(
handle,
visibility:
calendarVisibility || CalendarChannelVisibility.SHARE_EVERYTHING,
},
{},
manager,
);
const calendarChannelMetadata =
await this.objectMetadataRepository.findOneOrFail({
where: { nameSingular: 'calendarChannel', workspaceId },
});
this.workspaceEventEmitter.emitDatabaseBatchEvent({
objectMetadataNameSingular: 'calendarChannel',
action: DatabaseEventAction.CREATED,
events: [
{ {
recordId: newCalendarChannel.id, id: v4(),
objectMetadata: calendarChannelMetadata, connectedAccountId: newOrExistingConnectedAccountId,
properties: { handle,
after: newCalendarChannel, visibility:
}, calendarVisibility ||
CalendarChannelVisibility.SHARE_EVERYTHING,
}, },
], {},
workspaceId, manager,
}); );
const calendarChannelMetadata =
await this.objectMetadataRepository.findOneOrFail({
where: { nameSingular: 'calendarChannel', workspaceId },
});
this.workspaceEventEmitter.emitDatabaseBatchEvent({
objectMetadataNameSingular: 'calendarChannel',
action: DatabaseEventAction.CREATED,
events: [
{
recordId: newCalendarChannel.id,
objectMetadata: calendarChannelMetadata,
properties: {
after: newCalendarChannel,
},
},
],
workspaceId,
});
}
} else { } else {
const updatedConnectedAccount = await connectedAccountRepository.update( const updatedConnectedAccount = await connectedAccountRepository.update(
{ {
@ -291,36 +298,40 @@ export class MicrosoftAPIsService {
} }
}); });
const messageChannels = await messageChannelRepository.find({ if (this.environmentService.get('MESSAGING_PROVIDER_MICROSOFT_ENABLED')) {
where: { const messageChannels = await messageChannelRepository.find({
connectedAccountId: newOrExistingConnectedAccountId, where: {
}, connectedAccountId: newOrExistingConnectedAccountId,
});
for (const messageChannel of messageChannels) {
await this.messageQueueService.add<MessagingMessageListFetchJobData>(
MessagingMessageListFetchJob.name,
{
workspaceId,
messageChannelId: messageChannel.id,
}, },
); });
for (const messageChannel of messageChannels) {
await this.messageQueueService.add<MessagingMessageListFetchJobData>(
MessagingMessageListFetchJob.name,
{
workspaceId,
messageChannelId: messageChannel.id,
},
);
}
} }
const calendarChannels = await calendarChannelRepository.find({ if (this.environmentService.get('CALENDAR_PROVIDER_MICROSOFT_ENABLED')) {
where: { const calendarChannels = await calendarChannelRepository.find({
connectedAccountId: newOrExistingConnectedAccountId, where: {
}, connectedAccountId: newOrExistingConnectedAccountId,
});
for (const calendarChannel of calendarChannels) {
await this.calendarQueueService.add<CalendarEventListFetchJobData>(
CalendarEventListFetchJob.name,
{
calendarChannelId: calendarChannel.id,
workspaceId,
}, },
); });
for (const calendarChannel of calendarChannels) {
await this.calendarQueueService.add<CalendarEventListFetchJobData>(
CalendarEventListFetchJob.name,
{
calendarChannelId: calendarChannel.id,
workspaceId,
},
);
}
} }
} }
} }

View File

@ -127,4 +127,16 @@ export class ClientConfig {
@Field(() => [PublicFeatureFlag]) @Field(() => [PublicFeatureFlag])
publicFeatureFlags: PublicFeatureFlag[]; publicFeatureFlags: PublicFeatureFlag[];
@Field(() => Boolean)
isMicrosoftMessagingEnabled: boolean;
@Field(() => Boolean)
isMicrosoftCalendarEnabled: boolean;
@Field(() => Boolean)
isGoogleMessagingEnabled: boolean;
@Field(() => Boolean)
isGoogleCalendarEnabled: boolean;
} }

View File

@ -77,6 +77,18 @@ export class ClientConfigResolver {
this.environmentService.get('DEBUG_MODE') || this.environmentService.get('DEBUG_MODE') ||
this.environmentService.get('IS_BILLING_ENABLED'), this.environmentService.get('IS_BILLING_ENABLED'),
publicFeatureFlags: PUBLIC_FEATURE_FLAGS, publicFeatureFlags: PUBLIC_FEATURE_FLAGS,
isMicrosoftMessagingEnabled: this.environmentService.get(
'MESSAGING_PROVIDER_MICROSOFT_ENABLED',
),
isMicrosoftCalendarEnabled: this.environmentService.get(
'CALENDAR_PROVIDER_MICROSOFT_ENABLED',
),
isGoogleMessagingEnabled: this.environmentService.get(
'MESSAGING_PROVIDER_GMAIL_ENABLED',
),
isGoogleCalendarEnabled: this.environmentService.get(
'CALENDAR_PROVIDER_GOOGLE_ENABLED',
),
}; };
return Promise.resolve(clientConfig); return Promise.resolve(clientConfig);

View File

@ -199,6 +199,22 @@ export class EnvironmentVariables {
@ValidateIf((env) => env.AUTH_MICROSOFT_ENABLED) @ValidateIf((env) => env.AUTH_MICROSOFT_ENABLED)
AUTH_MICROSOFT_APIS_CALLBACK_URL: string; AUTH_MICROSOFT_APIS_CALLBACK_URL: string;
@EnvironmentVariablesMetadata({
group: EnvironmentVariablesGroup.Authentication,
subGroup: EnvironmentVariablesSubGroup.MicrosoftAuth,
description: 'Is Microsoft messaging provider enabled',
})
@CastToBoolean()
MESSAGING_PROVIDER_MICROSOFT_ENABLED = false;
@EnvironmentVariablesMetadata({
group: EnvironmentVariablesGroup.Authentication,
subGroup: EnvironmentVariablesSubGroup.MicrosoftAuth,
description: 'Is Microsoft Calendar provider enabled',
})
@CastToBoolean()
CALENDAR_PROVIDER_MICROSOFT_ENABLED = false;
@EnvironmentVariablesMetadata({ @EnvironmentVariablesMetadata({
group: EnvironmentVariablesGroup.Authentication, group: EnvironmentVariablesGroup.Authentication,
subGroup: EnvironmentVariablesSubGroup.Tokens, subGroup: EnvironmentVariablesSubGroup.Tokens,

View File

@ -20,6 +20,8 @@ Then you can set the following environment variables:
- `MESSAGING_PROVIDER_GMAIL_ENABLED=true` - `MESSAGING_PROVIDER_GMAIL_ENABLED=true`
- `CALENDAR_PROVIDER_GOOGLE_ENABLED=true` - `CALENDAR_PROVIDER_GOOGLE_ENABLED=true`
- `MESSAGING_PROVIDER_MICROSOFT_ENABLED=false`
- `CALENDAR_PROVIDER_MICROSOFT_ENABLED=false`
- `AUTH_GOOGLE_CLIENT_ID=<client-id>` - `AUTH_GOOGLE_CLIENT_ID=<client-id>`
- `AUTH_GOOGLE_CLIENT_SECRET=<client-secret>` - `AUTH_GOOGLE_CLIENT_SECRET=<client-secret>`
- `AUTH_GOOGLE_CALLBACK_URL=https://<your-domain>/auth/google/redirect` if you want to use Google SSO - `AUTH_GOOGLE_CALLBACK_URL=https://<your-domain>/auth/google/redirect` if you want to use Google SSO
@ -77,6 +79,8 @@ You will need to create a project in [Microsoft Azure](https://portal.azure.com/
Then you can set the following environment variables: Then you can set the following environment variables:
- `MESSAGING_PROVIDER_MICROSOFT_ENABLED=true`
- `CALENDAR_PROVIDER_MICROSOFT_ENABLED=true`
- `AUTH_MICROSOFT_ENABLED=true` - `AUTH_MICROSOFT_ENABLED=true`
- `AUTH_MICROSOFT_CLIENT_ID=<client-id>` - `AUTH_MICROSOFT_CLIENT_ID=<client-id>`
- `AUTH_MICROSOFT_CLIENT_SECRET=<client-secret>` - `AUTH_MICROSOFT_CLIENT_SECRET=<client-secret>`