Leverage workspace activationStatus to decide if a workspace is activated or not (#6497)
An ACTIVE workspace is a workspace that has a complete workspaceSchema and is authorized to be browsed by users. In this PR, I'm: - introducing a new activationStatus: PENDING_CREATION (existing ACTIVE / INACTIVE) - removing workspaceService.isWorkspaceActivated (based on workspaceSchema existence which is not robust and checking activationStatus.ACTIVE instead) - removing dynamic activationStatus field on worksapce resolver (we can use the postgres column directly now that data has been migrated) - on user sign up creating the workspace in PENDING_CREATION, and on workspace activation setting it to ACTIVE - only re-activating a workspace if the current activationStatus is INACTIVE through billing webhooks (a PENDING_CREATION should stay PENDING and ACTIVE should stay ACTIVE)
This commit is contained in:
@ -1,30 +1,33 @@
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
import {
|
||||
BadRequestException,
|
||||
ForbiddenException,
|
||||
Injectable,
|
||||
} from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
|
||||
import FileType from 'file-type';
|
||||
import { Repository } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
import FileType from 'file-type';
|
||||
|
||||
import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface';
|
||||
|
||||
import { assert } from 'src/utils/assert';
|
||||
import {
|
||||
PASSWORD_REGEX,
|
||||
hashPassword,
|
||||
compareHash,
|
||||
hashPassword,
|
||||
} from 'src/engine/core-modules/auth/auth.util';
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
|
||||
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
|
||||
import { getImageBufferFromUrl } from 'src/utils/image';
|
||||
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { WorkspaceService } from 'src/engine/core-modules/workspace/services/workspace.service';
|
||||
import {
|
||||
Workspace,
|
||||
WorkspaceActivationStatus,
|
||||
} from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { getImageBufferFromUrl } from 'src/utils/image';
|
||||
|
||||
export type SignInUpServiceInput = {
|
||||
email: string;
|
||||
@ -144,11 +147,8 @@ export class SignInUpService {
|
||||
ForbiddenException,
|
||||
);
|
||||
|
||||
const isWorkspaceActivated =
|
||||
await this.workspaceService.isWorkspaceActivated(workspace.id);
|
||||
|
||||
assert(
|
||||
isWorkspaceActivated,
|
||||
workspace.activationStatus === WorkspaceActivationStatus.ACTIVE,
|
||||
'Workspace is not ready to welcome new members',
|
||||
ForbiddenException,
|
||||
);
|
||||
@ -203,6 +203,7 @@ export class SignInUpService {
|
||||
displayName: '',
|
||||
domainName: '',
|
||||
inviteHash: v4(),
|
||||
activationStatus: WorkspaceActivationStatus.PENDING_CREATION,
|
||||
});
|
||||
|
||||
const workspace = await this.workspaceRepository.save(workspaceToCreate);
|
||||
|
||||
@ -284,7 +284,7 @@ export class BillingWorkspaceService {
|
||||
| Stripe.CustomerSubscriptionCreatedEvent.Data
|
||||
| Stripe.CustomerSubscriptionDeletedEvent.Data,
|
||||
) {
|
||||
const workspace = this.workspaceRepository.find({
|
||||
const workspace = await this.workspaceRepository.findOne({
|
||||
where: { id: workspaceId },
|
||||
});
|
||||
|
||||
@ -341,9 +341,10 @@ export class BillingWorkspaceService {
|
||||
}
|
||||
|
||||
if (
|
||||
data.object.status === SubscriptionStatus.Active ||
|
||||
data.object.status === SubscriptionStatus.Trialing ||
|
||||
data.object.status === SubscriptionStatus.PastDue
|
||||
(data.object.status === SubscriptionStatus.Active ||
|
||||
data.object.status === SubscriptionStatus.Trialing ||
|
||||
data.object.status === SubscriptionStatus.PastDue) &&
|
||||
workspace.activationStatus == WorkspaceActivationStatus.INACTIVE
|
||||
) {
|
||||
await this.workspaceRepository.update(workspaceId, {
|
||||
activationStatus: WorkspaceActivationStatus.ACTIVE,
|
||||
|
||||
@ -45,23 +45,20 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
if (!data.displayName || !data.displayName.length) {
|
||||
throw new BadRequestException("'displayName' not provided");
|
||||
}
|
||||
await this.workspaceRepository.update(user.defaultWorkspace.id, {
|
||||
displayName: data.displayName,
|
||||
activationStatus: WorkspaceActivationStatus.ACTIVE,
|
||||
});
|
||||
|
||||
await this.workspaceManagerService.init(user.defaultWorkspace.id);
|
||||
await this.userWorkspaceService.createWorkspaceMember(
|
||||
user.defaultWorkspace.id,
|
||||
user,
|
||||
);
|
||||
await this.workspaceRepository.update(user.defaultWorkspace.id, {
|
||||
displayName: data.displayName,
|
||||
activationStatus: WorkspaceActivationStatus.ACTIVE,
|
||||
});
|
||||
|
||||
return user.defaultWorkspace;
|
||||
}
|
||||
|
||||
async isWorkspaceActivated(id: string): Promise<boolean> {
|
||||
return await this.workspaceManagerService.doesDataSourceExist(id);
|
||||
}
|
||||
|
||||
async softDeleteWorkspace(id: string) {
|
||||
const workspace = await this.workspaceRepository.findOneBy({ id });
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-works
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
|
||||
export enum WorkspaceActivationStatus {
|
||||
PENDING_CREATION = 'PENDING_CREATION',
|
||||
ACTIVE = 'ACTIVE',
|
||||
INACTIVE = 'INACTIVE',
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ import { WorkspaceCacheVersionService } from 'src/engine/metadata-modules/worksp
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { streamToBuffer } from 'src/utils/stream-to-buffer';
|
||||
|
||||
import { Workspace, WorkspaceActivationStatus } from './workspace.entity';
|
||||
import { Workspace } from './workspace.entity';
|
||||
|
||||
import { WorkspaceService } from './services/workspace.service';
|
||||
|
||||
@ -100,21 +100,6 @@ export class WorkspaceResolver {
|
||||
return this.workspaceService.deleteWorkspace(id);
|
||||
}
|
||||
|
||||
@ResolveField(() => WorkspaceActivationStatus)
|
||||
async activationStatus(
|
||||
@Parent() workspace: Workspace,
|
||||
): Promise<WorkspaceActivationStatus> {
|
||||
if (workspace.activationStatus) {
|
||||
return workspace.activationStatus;
|
||||
}
|
||||
|
||||
if (await this.workspaceService.isWorkspaceActivated(workspace.id)) {
|
||||
return WorkspaceActivationStatus.ACTIVE;
|
||||
}
|
||||
|
||||
return WorkspaceActivationStatus.INACTIVE;
|
||||
}
|
||||
|
||||
@ResolveField(() => String, { nullable: true })
|
||||
async currentCacheVersion(
|
||||
@Parent() workspace: Workspace,
|
||||
|
||||
Reference in New Issue
Block a user