[Breaking: DEPLOY SERVER BEFORE FRONT] fix: allow custom domain without Cloudflare API key (#17160)

## Summary

Fixes #17101

When self-hosting TwentyCRM, users can now set workspace custom domains
without requiring CLOUDFLARE_API_KEY to be configured. This enables
manual DNS configuration for those not using Cloudflare.

## Changes

- Added isCloudflareConfigured method to DnsManagerService
- Modified all Cloudflare-dependent methods to gracefully handle missing
configuration
- Added getManualDnsRecords helper that provides DNS configuration
instructions when Cloudflare is not available
- isHostnameWorking returns true in manual mode, allowing the domain to
be saved and enabled
- Added comprehensive tests for non-Cloudflare scenarios

## Behavior

When CLOUDFLARE_API_KEY is set: Works exactly as before with automatic
Cloudflare provisioning

When CLOUDFLARE_API_KEY is NOT set:
- Custom domain can be saved to the database
- User receives manual DNS configuration instructions
- Domain is marked as working, user is responsible for external DNS and
TLS configuration

## Testing

Added tests covering non-Cloudflare scenarios. Full test suite requires
Docker which was not run locally.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Introduces a Cloudflare integration feature flag and wires it through
server and front to gate the custom domain UI.
> 
> - Server: adds `isCloudflareIntegrationEnabled` to `ClientConfig`,
computed from `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ZONE_ID` in
`client-config.service`; updates GraphQL schema and unit tests.
> - Frontend: adds `isCloudflareIntegrationEnabledState`, extends
`ClientConfig` type, sets the flag in `useClientConfig`, and
conditionally renders `SettingsCustomDomain` in `SettingsDomain` when
enabled.
> - Updates mocked client config to include the new flag.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
0c76dde03b1ae940a9dae65e8673c3ba4cfec9dc. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Signed-off-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
Vedant Madane
2026-01-19 17:17:09 +05:30
committed by GitHub
parent dc93cf4c59
commit 4ab95235ce
9 changed files with 39 additions and 2 deletions
@@ -11,6 +11,7 @@ import { isAnalyticsEnabledState } from '@/client-config/states/isAnalyticsEnabl
import { isAttachmentPreviewEnabledState } from '@/client-config/states/isAttachmentPreviewEnabledState';
import { isConfigVariablesInDbEnabledState } from '@/client-config/states/isConfigVariablesInDbEnabledState';
import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState';
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';
@@ -109,6 +110,9 @@ export const useClientConfig = (): UseClientConfigResult => {
const setIsEmailingDomainsEnabled = useSetRecoilState(
isEmailingDomainsEnabledState,
);
const setIsCloudflareIntegrationEnabled = useSetRecoilState(
isCloudflareIntegrationEnabledState,
);
const setAppVersion = useSetRecoilState(appVersionState);
@@ -184,6 +188,9 @@ export const useClientConfig = (): UseClientConfigResult => {
setCalendarBookingPageId(clientConfig?.calendarBookingPageId ?? null);
setIsImapSmtpCaldavEnabled(clientConfig?.isImapSmtpCaldavEnabled);
setIsEmailingDomainsEnabled(clientConfig?.isEmailingDomainsEnabled);
setIsCloudflareIntegrationEnabled(
clientConfig?.isCloudflareIntegrationEnabled,
);
} catch (err) {
const error =
err instanceof Error ? err : new Error('Failed to fetch client config');
@@ -217,6 +224,7 @@ export const useClientConfig = (): UseClientConfigResult => {
setIsImapSmtpCaldavEnabled,
setIsMultiWorkspaceEnabled,
setIsEmailingDomainsEnabled,
setIsCloudflareIntegrationEnabled,
setLabPublicFeatureFlags,
setMicrosoftCalendarEnabled,
setMicrosoftMessagingEnabled,