fix(cloudflare): cloudflare webhook (#14834)

This commit is contained in:
Antoine Moreaux
2025-10-06 16:39:19 +02:00
committed by GitHub
parent bc2070410c
commit 695b4a2139
14 changed files with 159 additions and 65 deletions
@@ -7,6 +7,7 @@ import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { PublicDomainModule } from 'src/engine/core-modules/public-domain/public-domain.module';
import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.module';
import { DnsCloudflareController } from 'src/engine/core-modules/cloudflare/controllers/dns-cloudflare.controller';
import { DnsCloudflareService } from 'src/engine/core-modules/cloudflare/services/dns-cloudflare.service';
@Module({
imports: [
@@ -14,6 +15,7 @@ import { DnsCloudflareController } from 'src/engine/core-modules/cloudflare/cont
WorkspaceModule,
PublicDomainModule,
],
providers: [DnsCloudflareService],
controllers: [DnsCloudflareController],
})
export class CloudflareModule {}
@@ -1,31 +1,22 @@
/* @license Enterprise */
import { Controller, Post, Req, UseFilters, UseGuards } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Request } from 'express';
import { Repository } from 'typeorm';
import { isDefined } from 'twenty-shared/utils';
import { AuthRestApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-rest-api-exception.filter';
import { PublicEndpointGuard } from 'src/engine/guards/public-endpoint.guard';
import { DnsManagerExceptionFilter } from 'src/engine/core-modules/dns-manager/exceptions/dns-manager-exception-filter';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { PublicDomain } from 'src/engine/core-modules/public-domain/public-domain.entity';
import { WorkspaceService } from 'src/engine/core-modules/workspace/services/workspace.service';
import { PublicDomainService } from 'src/engine/core-modules/public-domain/public-domain.service';
import { CloudflareSecretMatchGuard } from 'src/engine/core-modules/cloudflare/guards/cloudflare-secret.guard';
import { DnsCloudflareService } from 'src/engine/core-modules/cloudflare/services/dns-cloudflare.service';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
@Controller()
@UseFilters(AuthRestApiExceptionFilter, DnsManagerExceptionFilter)
export class DnsCloudflareController {
constructor(
@InjectRepository(Workspace)
private readonly workspaceRepository: Repository<Workspace>,
protected readonly workspaceService: WorkspaceService,
@InjectRepository(PublicDomain)
private readonly publicDomainRepository: Repository<PublicDomain>,
protected readonly publicDomainService: PublicDomainService,
protected readonly dnsCloudflareService: DnsCloudflareService,
private readonly twentyConfigService: TwentyConfigService,
) {}
@Post(['cloudflare/custom-hostname-webhooks', 'webhooks/cloudflare'])
@@ -33,28 +24,18 @@ export class DnsCloudflareController {
async customHostnameWebhooks(@Req() req: Request) {
const hostname = req.body?.data?.data?.hostname;
if (!hostname) {
const zoneIds = [
this.twentyConfigService.get('CLOUDFLARE_PUBLIC_DOMAIN_ZONE_ID'),
this.twentyConfigService.get('CLOUDFLARE_ZONE_ID'),
];
// since notification are not scoped to a zone, we need to check if the zone is in the list of zones
if (!hostname || !zoneIds.includes(req.body?.data?.metadata?.zone.id)) {
return;
}
try {
const workspace = await this.workspaceRepository.findOneBy({
customDomain: hostname,
});
if (isDefined(workspace)) {
await this.workspaceService.checkCustomDomainValidRecords(workspace);
}
const publicDomain = await this.publicDomainRepository.findOneBy({
domain: hostname,
});
if (isDefined(publicDomain)) {
await this.publicDomainService.checkPublicDomainValidRecords(
publicDomain,
);
}
await this.dnsCloudflareService.checkHostname(hostname);
} catch {
return;
}
@@ -0,0 +1,31 @@
import { Injectable } from '@nestjs/common';
import { isDefined } from 'twenty-shared/utils';
import { WorkspaceService } from 'src/engine/core-modules/workspace/services/workspace.service';
import { PublicDomainService } from 'src/engine/core-modules/public-domain/public-domain.service';
@Injectable()
// eslint-disable-next-line @nx/workspace-inject-workspace-repository
export class DnsCloudflareService {
constructor(
private readonly workspaceService: WorkspaceService,
private readonly publicDomainService: PublicDomainService,
) {}
async checkHostname(hostname: string) {
const workspace = await this.workspaceService.findByCustomDomain(hostname);
if (isDefined(workspace)) {
await this.workspaceService.checkCustomDomainValidRecords(workspace);
}
const publicDomain = await this.publicDomainService.findByDomain(hostname);
if (isDefined(publicDomain)) {
await this.publicDomainService.checkPublicDomainValidRecords(
publicDomain,
);
}
}
}