fix: Add reserved subdomains constant and update validation on generateSubdomain (#15217)

## Description

Fixes #15160 

- Moved the reserved subdomains to a separate shared constant file:
`packages/twenty-server/src/engine/core-modules/workspace/constants/reserved-subdomains.constant.ts`
- Updated the validation while generating subdomain to check if the
extracted subdomain (from email or display name) is reserved
- When a reserved subdomain is detected, the server will automatically
fall back to a random subdomain

---------

Co-authored-by: Naineel Soyantar <naineelsoyantar@gmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Naineel Soyantar
2025-10-21 20:23:48 +05:30
committed by GitHub
parent 139b6dd29b
commit 26e432d8e2
97 changed files with 1369 additions and 754 deletions
@@ -6,7 +6,7 @@ import axios from 'axios';
import { AdminPanelService } from 'src/engine/core-modules/admin-panel/admin-panel.service';
import { AuditService } from 'src/engine/core-modules/audit/services/audit.service';
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
import { WorkspaceDomainsService } from 'src/engine/core-modules/domain/workspace-domains/services/workspace-domains.service';
import { FileService } from 'src/engine/core-modules/file/services/file.service';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { User } from 'src/engine/core-modules/user/user.entity';
@@ -59,7 +59,7 @@ describe('AdminPanelService', () => {
},
},
{
provide: DomainManagerService,
provide: WorkspaceDomainsService,
useValue: {
getWorkspaceUrls: jest.fn().mockReturnValue({
customUrl: undefined,
@@ -8,7 +8,7 @@ import { AdminPanelResolver } from 'src/engine/core-modules/admin-panel/admin-pa
import { AdminPanelService } from 'src/engine/core-modules/admin-panel/admin-panel.service';
import { AuditModule } from 'src/engine/core-modules/audit/audit.module';
import { AuthModule } from 'src/engine/core-modules/auth/auth.module';
import { DomainManagerModule } from 'src/engine/core-modules/domain-manager/domain-manager.module';
import { WorkspaceDomainsModule } from 'src/engine/core-modules/domain/workspace-domains/workspace-domains.module';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { FileModule } from 'src/engine/core-modules/file/file.module';
import { HealthModule } from 'src/engine/core-modules/health/health.module';
@@ -22,8 +22,8 @@ import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permi
imports: [
TypeOrmModule.forFeature([User]),
AuthModule,
DomainManagerModule,
FileModule,
WorkspaceDomainsModule,
HealthModule,
RedisClientModule,
TerminusModule,
@@ -12,13 +12,11 @@ import { type ConfigVariablesGroupDataDTO } from 'src/engine/core-modules/admin-
import { type ConfigVariablesOutput } from 'src/engine/core-modules/admin-panel/dtos/config-variables.output';
import { type UserLookup } from 'src/engine/core-modules/admin-panel/dtos/user-lookup.entity';
import { type VersionInfoDTO } from 'src/engine/core-modules/admin-panel/dtos/version-info.dto';
import { AuditService } from 'src/engine/core-modules/audit/services/audit.service';
import {
AuthException,
AuthExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception';
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
import { WorkspaceDomainsService } from 'src/engine/core-modules/domain/workspace-domains/services/workspace-domains.service';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { type FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { FileService } from 'src/engine/core-modules/file/services/file.service';
@@ -32,10 +30,8 @@ import { userValidator } from 'src/engine/core-modules/user/user.validate';
@Injectable()
export class AdminPanelService {
constructor(
private readonly loginTokenService: LoginTokenService,
private readonly twentyConfigService: TwentyConfigService,
private readonly domainManagerService: DomainManagerService,
private readonly auditService: AuditService,
private readonly workspaceDomainsService: WorkspaceDomainsService,
private readonly fileService: FileService,
@InjectRepository(User)
private readonly userRepository: Repository<User>,
@@ -90,7 +86,7 @@ export class AdminPanelService {
})
: userWorkspace.workspace.logo,
allowImpersonation: userWorkspace.workspace.allowImpersonation,
workspaceUrls: this.domainManagerService.getWorkspaceUrls({
workspaceUrls: this.workspaceDomainsService.getWorkspaceUrls({
subdomain: userWorkspace.workspace.subdomain,
customDomain: userWorkspace.workspace.customDomain,
isCustomDomainEnabled: userWorkspace.workspace.isCustomDomainEnabled,