diff --git a/packages/twenty-server/src/engine/core-modules/domain-manager/domain-manager.exception.ts b/packages/twenty-server/src/engine/core-modules/domain-manager/domain-manager.exception.ts index e3af6b3907bd..0425a824af9c 100644 --- a/packages/twenty-server/src/engine/core-modules/domain-manager/domain-manager.exception.ts +++ b/packages/twenty-server/src/engine/core-modules/domain-manager/domain-manager.exception.ts @@ -8,5 +8,5 @@ export class DomainManagerException extends CustomException { export enum DomainManagerExceptionCode { CLOUDFLARE_CLIENT_NOT_INITIALIZED = 'CLOUDFLARE_CLIENT_NOT_INITIALIZED', - DOMAIN_ALREADY_REGISTERED = 'DOMAIN_ALREADY_REGISTERED', + HOSTNAME_ALREADY_REGISTERED = 'HOSTNAME_ALREADY_REGISTERED', } diff --git a/packages/twenty-server/src/engine/core-modules/domain-manager/service/domain-manager.service.ts b/packages/twenty-server/src/engine/core-modules/domain-manager/service/domain-manager.service.ts index 707b42973175..ad9a32c847ad 100644 --- a/packages/twenty-server/src/engine/core-modules/domain-manager/service/domain-manager.service.ts +++ b/packages/twenty-server/src/engine/core-modules/domain-manager/service/domain-manager.service.ts @@ -227,16 +227,16 @@ export class DomainManagerService { ) { return 'hostname' in params ? params.hostname - : await this.getCustomDomainDetails(params.hostnameName); + : await this.getCustomHostnameDetails(params.hostnameName); } - async registerCustomDomain(hostname: string) { + async registerCustomHostname(hostname: string) { domainManagerValidator.isExist(this.cloudflareClient); - if (await this.getCustomDomainDetails(hostname)) { + if (await this.getCustomHostnameDetails(hostname)) { throw new DomainManagerException( - 'Domain already registered', - DomainManagerExceptionCode.DOMAIN_ALREADY_REGISTERED, + 'Hostname already registered', + DomainManagerExceptionCode.HOSTNAME_ALREADY_REGISTERED, ); } @@ -259,7 +259,7 @@ export class DomainManagerService { }); } - async getCustomDomainDetails( + async getCustomHostnameDetails( hostname: string, ): Promise { domainManagerValidator.isExist(this.cloudflareClient); @@ -278,10 +278,10 @@ export class DomainManagerService { } // should never append. error 5xx - throw new Error('More than one custom domain found'); + throw new Error('More than one custom hostname found in cloudflare'); } - async updateCustomDomain( + async updateCustomHostname( fromHostname: { hostnameName: string } | { hostname: CustomHostname }, toHostname: string, ) { @@ -290,13 +290,29 @@ export class DomainManagerService { const fromCustomHostname = await this.getCustomHostname(fromHostname); if (fromCustomHostname) { - await this.deleteCustomDomain(fromCustomHostname); + await this.deleteCustomHostname(fromCustomHostname); } - return this.registerCustomDomain(toHostname); + return this.registerCustomHostname(toHostname); } - async deleteCustomDomain(customHostname: CustomHostname) { + async deleteCustomHostnameByHostnameSilently(hostname: string) { + domainManagerValidator.isExist(this.cloudflareClient); + + try { + const customHostname = await this.getCustomHostnameDetails(hostname); + + if (customHostname) { + await this.cloudflareClient.customHostnames.delete(customHostname.id, { + zone_id: this.environmentService.get('CLOUDFLARE_ZONE_ID'), + }); + } + } catch (err) { + return; + } + } + + async deleteCustomHostname(customHostname: CustomHostname) { domainManagerValidator.isExist(this.cloudflareClient); return this.cloudflareClient.customHostnames.delete(customHostname.id, { diff --git a/packages/twenty-server/src/engine/core-modules/workspace/services/workspace.service.ts b/packages/twenty-server/src/engine/core-modules/workspace/services/workspace.service.ts index 8c408d103044..a000f3dee5d4 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/services/workspace.service.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/services/workspace.service.ts @@ -63,24 +63,35 @@ export class WorkspaceService extends TypeOrmQueryService { } } - private async updateDomain(workspace: Workspace, hostname: string) { + private async setCustomDomain(workspace: Workspace, hostname: string) { + const existingWorkspace = await this.workspaceRepository.findOne({ + where: { hostname }, + }); + + if (existingWorkspace && existingWorkspace.id !== workspace.id) { + throw new WorkspaceException( + 'Domain already taken', + WorkspaceExceptionCode.DOMAIN_ALREADY_TAKEN, + ); + } + if ( hostname && - workspace.domain !== hostname && - isDefined(workspace.domain) + workspace.hostname !== hostname && + isDefined(workspace.hostname) ) { - await this.domainManagerService.updateCustomDomain( - { hostnameName: workspace.domain }, + await this.domainManagerService.updateCustomHostname( + { hostnameName: workspace.hostname }, hostname, ); } if ( hostname && - workspace.domain !== hostname && - !isDefined(workspace.domain) + workspace.hostname !== hostname && + !isDefined(workspace.hostname) ) { - await this.domainManagerService.registerCustomDomain(hostname); + await this.domainManagerService.registerCustomHostname(hostname); } } @@ -95,14 +106,25 @@ export class WorkspaceService extends TypeOrmQueryService { await this.validateSubdomainUpdate(payload.subdomain); } - if (payload.domain && workspace.domain !== payload.domain) { - await this.updateDomain(workspace, payload.domain); + let customDomainRegistered = false; + + if (payload.hostname && workspace.hostname !== payload.hostname) { + await this.setCustomDomain(workspace, payload.hostname); + customDomainRegistered = true; } - return this.workspaceRepository.save({ - ...workspace, - ...payload, - }); + try { + return this.workspaceRepository.save({ + ...workspace, + ...payload, + }); + } catch (e) { + if (payload.hostname && customDomainRegistered) { + await this.domainManagerService.deleteCustomHostnameByHostnameSilently( + payload.hostname, + ); + } + } } async activateWorkspace(user: User, data: ActivateWorkspaceInput) { @@ -238,12 +260,4 @@ export class WorkspaceService extends TypeOrmQueryService { return !existingWorkspace; } - - async isDomainAvailable(domain: string) { - const existingWorkspace = await this.workspaceRepository.findOne({ - where: { domain }, - }); - - return !existingWorkspace; - } } diff --git a/packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts b/packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts index 65eea850dce7..b2a98f93381f 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts @@ -51,10 +51,10 @@ export class Workspace { @PrimaryGeneratedColumn('uuid') id: string; - // @deprecated. Use domain field - @Field({ nullable: true }) - @Column({ nullable: true }) - domainName?: string; + // @deprecated. Use hostname field + // @Field({ nullable: true }) + // @Column({ nullable: true }) + // domainName?: string; @Field({ nullable: true }) @Column({ nullable: true }) @@ -150,7 +150,7 @@ export class Workspace { @Field() @Column({ unique: true }) - domain: string; + hostname: string; @Field() @Column({ default: true })