c6f11d8adb
## Summary - Migrates `LogicFunctionModule`, `CodeInterpreterModule`, and `CaptchaModule` from the `forRootAsync` + injection token pattern to the `DriverFactoryBase` lazy-loading pattern (matching `EmailModule` and `FileStorageModule`) - Fixes #18724 where `LOGIC_FUNCTION_TYPE` was not respected in worker processes because the driver was created at module boot time before the DB config cache was loaded - Removes `isEnvOnly` from `LOGIC_FUNCTION_TYPE`, `CODE_INTERPRETER_TYPE`, `CAPTCHA_DRIVER`, `IS_MULTIWORKSPACE_ENABLED`, and `FRONTEND_URL` — these can now be safely configured via the database at runtime ## How it works Each migrated module now uses a `DriverFactory` (extending `DriverFactoryBase`) instead of a module-level async factory + Symbol injection token: 1. **Lazy creation**: `getCurrentDriver()` creates the driver on first call, after `DatabaseConfigDriver.onModuleInit()` has loaded the DB cache 2. **Auto-recreation**: If config changes in the DB, the next `getCurrentDriver()` call detects the key mismatch and creates a new driver instance 3. **Unified config**: Both server and worker read from the same database — driver config only needs to be set once ### Files deleted (old pattern) - `logic-function-module.factory.ts`, `logic-function-drivers.module.ts`, `logic-function-driver.constants.ts` - `code-interpreter-module.factory.ts` - `captcha.module-factory.ts`, `captcha-driver.constants.ts` ### Files created (new pattern) - `logic-function-driver.factory.ts` - `code-interpreter-driver.factory.ts` - `captcha-driver.factory.ts` Net: **-150 lines** ## Test plan - [x] `npx nx typecheck twenty-server` passes - [x] `npx nx lint:diff-with-main twenty-server` passes - [ ] Integration tests pass (`npx nx run twenty-server:test:integration:with-db-reset`) - [ ] Verify logic functions execute in workflow runs (the original bug) - [ ] Verify code interpreter works in workflow code steps - [ ] Verify captcha validation works on sign-up (when captcha is configured) Made with [Cursor](https://cursor.com)
248 lines
9.7 KiB
TypeScript
248 lines
9.7 KiB
TypeScript
import { aiModelsState } from '@/client-config/states/aiModelsState';
|
|
import { apiConfigState } from '@/client-config/states/apiConfigState';
|
|
import { appVersionState } from '@/client-config/states/appVersionState';
|
|
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
|
import { billingState } from '@/client-config/states/billingState';
|
|
import { calendarBookingPageIdState } from '@/client-config/states/calendarBookingPageIdState';
|
|
import { canManageFeatureFlagsState } from '@/client-config/states/canManageFeatureFlagsState';
|
|
import { captchaState } from '@/client-config/states/captchaState';
|
|
import { isAnalyticsEnabledState } from '@/client-config/states/isAnalyticsEnabledState';
|
|
import { isAttachmentPreviewEnabledState } from '@/client-config/states/isAttachmentPreviewEnabledState';
|
|
import { isConfigVariablesInDbEnabledState } from '@/client-config/states/isConfigVariablesInDbEnabledState';
|
|
import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState';
|
|
import { isClickHouseConfiguredState } from '@/client-config/states/isClickHouseConfiguredState';
|
|
import { isCloudflareIntegrationEnabledState } from '@/client-config/states/isCloudflareIntegrationEnabledState';
|
|
import { isEmailingDomainsEnabledState } from '@/client-config/states/isEmailingDomainsEnabledState';
|
|
import { isEmailVerificationRequiredState } from '@/client-config/states/isEmailVerificationRequiredState';
|
|
import { isGoogleCalendarEnabledState } from '@/client-config/states/isGoogleCalendarEnabledState';
|
|
import { isGoogleMessagingEnabledState } from '@/client-config/states/isGoogleMessagingEnabledState';
|
|
import { isImapSmtpCaldavEnabledState } from '@/client-config/states/isImapSmtpCaldavEnabledState';
|
|
import { isMicrosoftCalendarEnabledState } from '@/client-config/states/isMicrosoftCalendarEnabledState';
|
|
import { isMicrosoftMessagingEnabledState } from '@/client-config/states/isMicrosoftMessagingEnabledState';
|
|
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
|
import { labPublicFeatureFlagsState } from '@/client-config/states/labPublicFeatureFlagsState';
|
|
import { sentryConfigState } from '@/client-config/states/sentryConfigState';
|
|
import { supportChatState } from '@/client-config/states/supportChatState';
|
|
import { type ClientConfig } from '@/client-config/types/ClientConfig';
|
|
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
|
import { useCallback } from 'react';
|
|
import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState';
|
|
import { getClientConfig } from '@/client-config/utils/getClientConfig';
|
|
import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons';
|
|
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
|
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
|
|
|
type UseClientConfigResult = {
|
|
data: { clientConfig: ClientConfig } | undefined;
|
|
loading: boolean;
|
|
error: Error | undefined;
|
|
fetchClientConfig: () => Promise<void>;
|
|
refetch: () => Promise<void>;
|
|
};
|
|
|
|
export const useClientConfig = (): UseClientConfigResult => {
|
|
const setIsAnalyticsEnabled = useSetAtomState(isAnalyticsEnabledState);
|
|
const setDomainConfiguration = useSetAtomState(domainConfigurationState);
|
|
const setAuthProviders = useSetAtomState(authProvidersState);
|
|
const setAiModels = useSetAtomState(aiModelsState);
|
|
|
|
const setIsDeveloperDefaultSignInPrefilled = useSetAtomState(
|
|
isDeveloperDefaultSignInPrefilledState,
|
|
);
|
|
const setIsMultiWorkspaceEnabled = useSetAtomState(
|
|
isMultiWorkspaceEnabledState,
|
|
);
|
|
const setIsEmailVerificationRequired = useSetAtomState(
|
|
isEmailVerificationRequiredState,
|
|
);
|
|
|
|
const setBilling = useSetAtomState(billingState);
|
|
const setSupportChat = useSetAtomState(supportChatState);
|
|
|
|
const setSentryConfig = useSetAtomState(sentryConfigState);
|
|
const [clientConfigApiStatus, setClientConfigApiStatus] = useAtomState(
|
|
clientConfigApiStatusState,
|
|
);
|
|
|
|
const setCaptcha = useSetAtomState(captchaState);
|
|
|
|
const setApiConfig = useSetAtomState(apiConfigState);
|
|
|
|
const setCanManageFeatureFlags = useSetAtomState(canManageFeatureFlagsState);
|
|
|
|
const setLabPublicFeatureFlags = useSetAtomState(labPublicFeatureFlagsState);
|
|
|
|
const setIsMicrosoftMessagingEnabled = useSetAtomState(
|
|
isMicrosoftMessagingEnabledState,
|
|
);
|
|
|
|
const setIsMicrosoftCalendarEnabled = useSetAtomState(
|
|
isMicrosoftCalendarEnabledState,
|
|
);
|
|
|
|
const setIsGoogleMessagingEnabled = useSetAtomState(
|
|
isGoogleMessagingEnabledState,
|
|
);
|
|
|
|
const setIsGoogleCalendarEnabled = useSetAtomState(
|
|
isGoogleCalendarEnabledState,
|
|
);
|
|
|
|
const setIsAttachmentPreviewEnabled = useSetAtomState(
|
|
isAttachmentPreviewEnabledState,
|
|
);
|
|
|
|
const setIsConfigVariablesInDbEnabled = useSetAtomState(
|
|
isConfigVariablesInDbEnabledState,
|
|
);
|
|
|
|
const setCalendarBookingPageId = useSetAtomState(calendarBookingPageIdState);
|
|
|
|
const setIsImapSmtpCaldavEnabled = useSetAtomState(
|
|
isImapSmtpCaldavEnabledState,
|
|
);
|
|
const setIsEmailingDomainsEnabled = useSetAtomState(
|
|
isEmailingDomainsEnabledState,
|
|
);
|
|
|
|
const setAllowRequestsToTwentyIcons = useSetAtomState(
|
|
allowRequestsToTwentyIconsState,
|
|
);
|
|
|
|
const setIsCloudflareIntegrationEnabled = useSetAtomState(
|
|
isCloudflareIntegrationEnabledState,
|
|
);
|
|
|
|
const setIsClickHouseConfigured = useSetAtomState(
|
|
isClickHouseConfiguredState,
|
|
);
|
|
|
|
const setAppVersion = useSetAtomState(appVersionState);
|
|
|
|
const fetchClientConfig = useCallback(async () => {
|
|
setClientConfigApiStatus((prev) => ({
|
|
...prev,
|
|
isLoading: true,
|
|
}));
|
|
|
|
try {
|
|
const clientConfig = await getClientConfig();
|
|
setClientConfigApiStatus((prev) => ({
|
|
...prev,
|
|
isLoading: false,
|
|
isLoadedOnce: true,
|
|
isErrored: false,
|
|
error: undefined,
|
|
data: { clientConfig },
|
|
}));
|
|
setClientConfigApiStatus((currentStatus) => ({
|
|
...currentStatus,
|
|
isErrored: false,
|
|
error: undefined,
|
|
}));
|
|
setAppVersion(clientConfig.appVersion);
|
|
setAuthProviders({
|
|
google: clientConfig.authProviders.google,
|
|
microsoft: clientConfig.authProviders.microsoft,
|
|
password: clientConfig.authProviders.password,
|
|
magicLink: false,
|
|
sso: clientConfig.authProviders.sso,
|
|
});
|
|
setAiModels(clientConfig.aiModels ?? []);
|
|
setIsAnalyticsEnabled(clientConfig.analyticsEnabled);
|
|
setIsDeveloperDefaultSignInPrefilled(clientConfig.signInPrefilled);
|
|
setIsMultiWorkspaceEnabled(clientConfig.isMultiWorkspaceEnabled);
|
|
setIsEmailVerificationRequired(clientConfig.isEmailVerificationRequired);
|
|
setBilling(clientConfig.billing);
|
|
setSupportChat(clientConfig.support);
|
|
|
|
setSentryConfig({
|
|
dsn: clientConfig?.sentry?.dsn,
|
|
release: clientConfig?.sentry?.release,
|
|
environment: clientConfig?.sentry?.environment,
|
|
});
|
|
|
|
setCaptcha({
|
|
provider: clientConfig?.captcha?.provider,
|
|
siteKey: clientConfig?.captcha?.siteKey,
|
|
});
|
|
|
|
setApiConfig(clientConfig?.api);
|
|
setDomainConfiguration({
|
|
defaultSubdomain: clientConfig?.defaultSubdomain,
|
|
frontDomain: clientConfig?.frontDomain,
|
|
});
|
|
setCanManageFeatureFlags(clientConfig?.canManageFeatureFlags);
|
|
setLabPublicFeatureFlags(clientConfig?.publicFeatureFlags);
|
|
setIsMicrosoftMessagingEnabled(clientConfig?.isMicrosoftMessagingEnabled);
|
|
setIsMicrosoftCalendarEnabled(clientConfig?.isMicrosoftCalendarEnabled);
|
|
setIsGoogleMessagingEnabled(clientConfig?.isGoogleMessagingEnabled);
|
|
setIsGoogleCalendarEnabled(clientConfig?.isGoogleCalendarEnabled);
|
|
setIsAttachmentPreviewEnabled(clientConfig?.isAttachmentPreviewEnabled);
|
|
setIsConfigVariablesInDbEnabled(
|
|
clientConfig?.isConfigVariablesInDbEnabled,
|
|
);
|
|
setClientConfigApiStatus((currentStatus) => ({
|
|
...currentStatus,
|
|
isSaved: true,
|
|
}));
|
|
|
|
setCalendarBookingPageId(clientConfig?.calendarBookingPageId ?? null);
|
|
setIsImapSmtpCaldavEnabled(clientConfig?.isImapSmtpCaldavEnabled);
|
|
setIsEmailingDomainsEnabled(clientConfig?.isEmailingDomainsEnabled);
|
|
setAllowRequestsToTwentyIcons(clientConfig?.allowRequestsToTwentyIcons);
|
|
setIsCloudflareIntegrationEnabled(
|
|
clientConfig?.isCloudflareIntegrationEnabled,
|
|
);
|
|
setIsClickHouseConfigured(clientConfig?.isClickHouseConfigured ?? false);
|
|
} catch (err) {
|
|
const error =
|
|
err instanceof Error ? err : new Error('Failed to fetch client config');
|
|
setClientConfigApiStatus((prev) => ({
|
|
...prev,
|
|
isLoading: false,
|
|
isLoadedOnce: true,
|
|
isErrored: true,
|
|
error,
|
|
}));
|
|
}
|
|
}, [
|
|
setAiModels,
|
|
setApiConfig,
|
|
setAppVersion,
|
|
setAuthProviders,
|
|
setBilling,
|
|
setCalendarBookingPageId,
|
|
setCanManageFeatureFlags,
|
|
setCaptcha,
|
|
setClientConfigApiStatus,
|
|
setDomainConfiguration,
|
|
setIsGoogleCalendarEnabled,
|
|
setIsGoogleMessagingEnabled,
|
|
setIsAnalyticsEnabled,
|
|
setIsAttachmentPreviewEnabled,
|
|
setIsConfigVariablesInDbEnabled,
|
|
setIsDeveloperDefaultSignInPrefilled,
|
|
setIsEmailVerificationRequired,
|
|
setIsImapSmtpCaldavEnabled,
|
|
setIsMultiWorkspaceEnabled,
|
|
setIsEmailingDomainsEnabled,
|
|
setIsClickHouseConfigured,
|
|
setIsCloudflareIntegrationEnabled,
|
|
setLabPublicFeatureFlags,
|
|
setIsMicrosoftCalendarEnabled,
|
|
setIsMicrosoftMessagingEnabled,
|
|
setSentryConfig,
|
|
setSupportChat,
|
|
setAllowRequestsToTwentyIcons,
|
|
]);
|
|
|
|
return {
|
|
data: clientConfigApiStatus.data,
|
|
loading: clientConfigApiStatus.isLoading || false,
|
|
error: clientConfigApiStatus.error,
|
|
fetchClientConfig,
|
|
refetch: fetchClientConfig,
|
|
};
|
|
};
|