feat(): enable custom domain usage (#9911)

# Content
- Introduce the `workspaceUrls` property. It contains two
sub-properties: `customUrl, subdomainUrl`. These endpoints are used to
access the workspace. Even if the `workspaceUrls` is invalid for
multiple reasons, the `subdomainUrl` remains valid.
- Introduce `ResolveField` workspaceEndpoints to avoid unnecessary URL
computation on the frontend part.
- Add a `forceSubdomainUrl` to avoid custom URL using a query parameter
This commit is contained in:
Antoine Moreaux
2025-02-07 14:34:26 +01:00
committed by GitHub
parent 3cc66fe712
commit 68183b7c85
87 changed files with 645 additions and 373 deletions

View File

@ -13,6 +13,7 @@ import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/featu
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { User } from 'src/engine/core-modules/user/user.entity';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
const UserFindOneMock = jest.fn();
const WorkspaceFindOneMock = jest.fn();
@ -95,6 +96,15 @@ describe('AdminPanelService', () => {
generateLoginToken: LoginTokenServiceGenerateLoginTokenMock,
},
},
{
provide: DomainManagerService,
useValue: {
getworkspaceUrls: jest.fn().mockReturnValue({
customUrl: undefined,
subdomainUrl: 'https://twenty.twenty.com',
}),
},
},
{
provide: EnvironmentService,
useValue: {
@ -230,7 +240,10 @@ describe('AdminPanelService', () => {
expect.objectContaining({
workspace: {
id: 'workspace-id',
subdomain: 'example-subdomain',
workspaceUrls: {
customUrl: undefined,
subdomainUrl: 'https://twenty.twenty.com',
},
},
loginToken: expect.objectContaining({
token: 'mock-login-token',

View File

@ -7,11 +7,13 @@ import { AuthModule } from 'src/engine/core-modules/auth/auth.module';
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { User } from 'src/engine/core-modules/user/user.entity';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { DomainManagerModule } from 'src/engine/core-modules/domain-manager/domain-manager.module';
@Module({
imports: [
TypeOrmModule.forFeature([User, Workspace, FeatureFlag], 'core'),
AuthModule,
DomainManagerModule,
],
providers: [AdminPanelResolver, AdminPanelService],
exports: [AdminPanelService],

View File

@ -28,12 +28,14 @@ import { User } from 'src/engine/core-modules/user/user.entity';
import { userValidator } from 'src/engine/core-modules/user/user.validate';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
@Injectable()
export class AdminPanelService {
constructor(
private readonly loginTokenService: LoginTokenService,
private readonly environmentService: EnvironmentService,
private readonly domainManagerService: DomainManagerService,
@InjectRepository(User, 'core')
private readonly userRepository: Repository<User>,
@InjectRepository(Workspace, 'core')
@ -72,7 +74,9 @@ export class AdminPanelService {
return {
workspace: {
id: user.workspaces[0].workspace.id,
subdomain: user.workspaces[0].workspace.subdomain,
workspaceUrls: this.domainManagerService.getworkspaceUrls(
user.workspaces[0].workspace,
),
},
loginToken,
};

View File

@ -1,13 +1,13 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { AuthToken } from 'src/engine/core-modules/auth/dto/token.entity';
import { WorkspaceSubdomainAndId } from 'src/engine/core-modules/workspace/dtos/workspace-subdomain-id.dto';
import { workspaceUrlsAndId } from 'src/engine/core-modules/workspace/dtos/workspace-subdomain-id.dto';
@ObjectType()
export class ImpersonateOutput {
@Field(() => AuthToken)
loginToken: AuthToken;
@Field(() => WorkspaceSubdomainAndId)
workspace: WorkspaceSubdomainAndId;
@Field(() => workspaceUrlsAndId)
workspace: workspaceUrlsAndId;
}