mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-22 19:41:53 +03:00
WIP
This commit is contained in:
parent
2ceb1c87b3
commit
f807021208
@ -30,6 +30,7 @@
|
||||
"cache-manager": "^5.4.0",
|
||||
"cache-manager-redis-yet": "^4.1.2",
|
||||
"class-validator": "patch:class-validator@0.14.0#./patches/class-validator+0.14.0.patch",
|
||||
"cloudflare": "^3.5.0",
|
||||
"connect-redis": "^7.1.1",
|
||||
"express-session": "^1.18.1",
|
||||
"graphql-middleware": "^6.1.35",
|
||||
|
@ -246,6 +246,14 @@ export class EnvironmentVariables {
|
||||
@IsBoolean()
|
||||
IS_MULTIWORKSPACE_ENABLED = false;
|
||||
|
||||
@IsString()
|
||||
@ValidateIf((env) => env.CLOUDFLARE_ZONE_ID)
|
||||
CLOUDFLARE_USER_SERVICE_KEY: string;
|
||||
|
||||
@IsString()
|
||||
@ValidateIf((env) => env.CLOUDFLARE_USER_SERVICE_KEY)
|
||||
CLOUDFLARE_ZONE_ID: string;
|
||||
|
||||
// Custom Code Engine
|
||||
@IsEnum(ServerlessDriverType)
|
||||
@IsOptional()
|
||||
|
@ -18,6 +18,14 @@ export class UpdateWorkspaceInput {
|
||||
@ForbiddenWords(['demo'])
|
||||
subdomain?: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Matches(
|
||||
/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$/,
|
||||
)
|
||||
domain?: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
|
@ -3,6 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import assert from 'assert';
|
||||
|
||||
import Cloudflare from 'cloudflare';
|
||||
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
@ -46,6 +47,48 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
super(workspaceRepository);
|
||||
}
|
||||
|
||||
private async validateSubdomainUpdate(newSubdomain: string) {
|
||||
const subdomainAvailable = await this.isSubdomainAvailable(newSubdomain);
|
||||
|
||||
if (
|
||||
!subdomainAvailable ||
|
||||
this.environmentService.get('DEFAULT_SUBDOMAIN') === newSubdomain
|
||||
) {
|
||||
throw new WorkspaceException(
|
||||
'Subdomain already taken',
|
||||
WorkspaceExceptionCode.SUBDOMAIN_ALREADY_TAKEN,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async validateDomain(newDomain: string, workspaceId: string) {
|
||||
const existingWorkspace = await this.isDomainAvailable(newDomain);
|
||||
|
||||
if (existingWorkspace) {
|
||||
throw new WorkspaceException(
|
||||
'Domain already taken',
|
||||
WorkspaceExceptionCode.DOMAIN_ALREADY_TAKEN,
|
||||
);
|
||||
}
|
||||
|
||||
const client = new Cloudflare({
|
||||
userServiceKey: this.environmentService.get(
|
||||
'CLOUDFLARE_USER_SERVICE_KEY',
|
||||
),
|
||||
});
|
||||
|
||||
const customHostname = await client.customHostnames.create({
|
||||
zone_id: this.environmentService.get('CLOUDFLARE_ZONE_ID'),
|
||||
hostname: newDomain,
|
||||
ssl: {},
|
||||
custom_metadata: {
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('>>>>>>>>>>>>>>', customHostname);
|
||||
}
|
||||
|
||||
async updateWorkspaceById(payload: Partial<Workspace> & { id: string }) {
|
||||
const workspace = await this.workspaceRepository.findOneBy({
|
||||
id: payload.id,
|
||||
@ -60,19 +103,11 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
);
|
||||
|
||||
if (payload.subdomain && workspace.subdomain !== payload.subdomain) {
|
||||
const subdomainAvailable = await this.isSubdomainAvailable(
|
||||
payload.subdomain,
|
||||
);
|
||||
await this.validateSubdomainUpdate(payload.subdomain);
|
||||
}
|
||||
|
||||
if (
|
||||
!subdomainAvailable ||
|
||||
this.environmentService.get('DEFAULT_SUBDOMAIN') === payload.subdomain
|
||||
) {
|
||||
throw new WorkspaceException(
|
||||
'Subdomain already taken',
|
||||
WorkspaceExceptionCode.SUBDOMAIN_ALREADY_TAKEN,
|
||||
);
|
||||
}
|
||||
if (payload.domain && workspace.domain !== payload.domain) {
|
||||
await this.validateDomain(payload.domain, workspace.id);
|
||||
}
|
||||
|
||||
return this.workspaceRepository.save({
|
||||
@ -214,4 +249,12 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
|
||||
return !existingWorkspace;
|
||||
}
|
||||
|
||||
async isDomainAvailable(domain: string) {
|
||||
const existingWorkspace = await this.workspaceRepository.findOne({
|
||||
where: { domain },
|
||||
});
|
||||
|
||||
return !existingWorkspace;
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ export class Workspace {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
// @deprecated. Use domain field
|
||||
@Field({ nullable: true })
|
||||
@Column({ nullable: true })
|
||||
domainName?: string;
|
||||
@ -147,6 +148,10 @@ export class Workspace {
|
||||
@Column()
|
||||
subdomain: string;
|
||||
|
||||
@Field()
|
||||
@Column({ unique: true })
|
||||
domain: string;
|
||||
|
||||
@Field()
|
||||
@Column({ default: true })
|
||||
isGoogleAuthEnabled: boolean;
|
||||
|
@ -9,5 +9,6 @@ export class WorkspaceException extends CustomException {
|
||||
export enum WorkspaceExceptionCode {
|
||||
SUBDOMAIN_NOT_FOUND = 'SUBDOMAIN_NOT_FOUND',
|
||||
SUBDOMAIN_ALREADY_TAKEN = 'SUBDOMAIN_ALREADY_TAKEN',
|
||||
DOMAIN_ALREADY_TAKEN = 'DOMAIN_ALREADY_TAKEN',
|
||||
WORKSPACE_NOT_FOUND = 'WORKSPACE_NOT_FOUND',
|
||||
}
|
||||
|
35
yarn.lock
35
yarn.lock
@ -16596,6 +16596,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/qs@npm:^6.9.7":
|
||||
version: 6.9.17
|
||||
resolution: "@types/qs@npm:6.9.17"
|
||||
checksum: 10c0/a183fa0b3464267f8f421e2d66d960815080e8aab12b9aadab60479ba84183b1cdba8f4eff3c06f76675a8e42fe6a3b1313ea76c74f2885c3e25d32499c17d1b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/range-parser@npm:*":
|
||||
version: 1.2.7
|
||||
resolution: "@types/range-parser@npm:1.2.7"
|
||||
@ -22020,6 +22027,24 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cloudflare@npm:^3.5.0":
|
||||
version: 3.5.0
|
||||
resolution: "cloudflare@npm:3.5.0"
|
||||
dependencies:
|
||||
"@types/node": "npm:^18.11.18"
|
||||
"@types/node-fetch": "npm:^2.6.4"
|
||||
"@types/qs": "npm:^6.9.7"
|
||||
abort-controller: "npm:^3.0.0"
|
||||
agentkeepalive: "npm:^4.2.1"
|
||||
form-data-encoder: "npm:1.7.2"
|
||||
formdata-node: "npm:^4.3.2"
|
||||
node-fetch: "npm:^2.6.7"
|
||||
qs: "npm:^6.10.3"
|
||||
web-streams-polyfill: "npm:^3.2.1"
|
||||
checksum: 10c0/bb48ff68a4f5b7e945ceec570e7e17251ad167d8d2dadf8099fd74df46582356382b8cd3f62a9da74cd3a208f8f5181a40c561bc5873592d6a4930458c0e7b86
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"clsx@npm:^1.1.1, clsx@npm:^1.2.1":
|
||||
version: 1.2.1
|
||||
resolution: "clsx@npm:1.2.1"
|
||||
@ -38872,6 +38897,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"qs@npm:^6.10.3":
|
||||
version: 6.13.1
|
||||
resolution: "qs@npm:6.13.1"
|
||||
dependencies:
|
||||
side-channel: "npm:^1.0.6"
|
||||
checksum: 10c0/5ef527c0d62ffca5501322f0832d800ddc78eeb00da3b906f1b260ca0492721f8cdc13ee4b8fd8ac314a6ec37b948798c7b603ccc167e954088df392092f160c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"qs@npm:~6.5.2":
|
||||
version: 6.5.3
|
||||
resolution: "qs@npm:6.5.3"
|
||||
@ -43990,6 +44024,7 @@ __metadata:
|
||||
cache-manager: "npm:^5.4.0"
|
||||
cache-manager-redis-yet: "npm:^4.1.2"
|
||||
class-validator: "patch:class-validator@0.14.0#./patches/class-validator+0.14.0.patch"
|
||||
cloudflare: "npm:^3.5.0"
|
||||
connect-redis: "npm:^7.1.1"
|
||||
express-session: "npm:^1.18.1"
|
||||
graphql-middleware: "npm:^6.1.35"
|
||||
|
Loading…
Reference in New Issue
Block a user