feat(workspace): Add subdomain availability check (#8906)
Implemented a feature to check the availability of subdomains when updating workspace settings. This includes a new mutation, `isSubdomainAvailable`, to validate subdomain availability through GraphQL. The frontend now verifies if a subdomain is available to prevent duplicates during updates. --------- Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
import { IsBoolean, IsOptional, IsString } from 'class-validator';
|
||||
import { IsBoolean, IsOptional, IsString, Matches } from 'class-validator';
|
||||
|
||||
@InputType()
|
||||
export class UpdateWorkspaceInput {
|
||||
@ -12,6 +12,7 @@ export class UpdateWorkspaceInput {
|
||||
@Field({ nullable: true })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Matches(/^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$/)
|
||||
subdomain?: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
|
||||
@ -18,6 +18,12 @@ import {
|
||||
} from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service';
|
||||
import { DEFAULT_FEATURE_FLAGS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/default-feature-flags';
|
||||
import {
|
||||
WorkspaceException,
|
||||
WorkspaceExceptionCode,
|
||||
} from 'src/engine/core-modules/workspace/workspace.exception';
|
||||
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
|
||||
import { ConflictError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
|
||||
@Injectable()
|
||||
// eslint-disable-next-line @nx/workspace-inject-workspace-repository
|
||||
@ -37,6 +43,35 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
super(workspaceRepository);
|
||||
}
|
||||
|
||||
async updateWorkspaceById(payload: Partial<Workspace> & { id: string }) {
|
||||
const workspace = await this.workspaceRepository.findOneBy({
|
||||
id: payload.id,
|
||||
});
|
||||
|
||||
workspaceValidator.assertIsExist(
|
||||
workspace,
|
||||
new WorkspaceException(
|
||||
'Workspace not found',
|
||||
WorkspaceExceptionCode.WORKSPACE_NOT_FOUND,
|
||||
),
|
||||
);
|
||||
|
||||
if (payload.subdomain && workspace.subdomain !== payload.subdomain) {
|
||||
const subdomainAvailable = await this.isSubdomainAvailable(
|
||||
payload.subdomain,
|
||||
);
|
||||
|
||||
if (!subdomainAvailable) {
|
||||
throw new ConflictError('Subdomain already taken');
|
||||
}
|
||||
}
|
||||
|
||||
return this.workspaceRepository.save({
|
||||
...workspace,
|
||||
...payload,
|
||||
});
|
||||
}
|
||||
|
||||
async activateWorkspace(user: User, data: ActivateWorkspaceInput) {
|
||||
if (!data.displayName || !data.displayName.length) {
|
||||
throw new BadRequestException("'displayName' not provided");
|
||||
@ -159,4 +194,12 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async isSubdomainAvailable(subdomain: string) {
|
||||
const existingWorkspace = await this.workspaceRepository.findOne({
|
||||
where: { subdomain: subdomain },
|
||||
});
|
||||
|
||||
return !existingWorkspace;
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +91,10 @@ export class WorkspaceResolver {
|
||||
@Args('data') data: UpdateWorkspaceInput,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
) {
|
||||
return this.workspaceService.updateOne(workspace.id, data);
|
||||
return this.workspaceService.updateWorkspaceById({
|
||||
...data,
|
||||
id: workspace.id,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => String)
|
||||
|
||||
Reference in New Issue
Block a user