Create core.workspace at signup
This commit is contained in:
@@ -546,7 +546,7 @@ export type User = {
|
||||
canImpersonate: Scalars['Boolean'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
defaultAvatarUrl?: Maybe<Scalars['String']>;
|
||||
defaultWorkspace?: Maybe<Workspace>;
|
||||
defaultWorkspace: Workspace;
|
||||
deletedAt?: Maybe<Scalars['DateTime']>;
|
||||
disabled?: Maybe<Scalars['Boolean']>;
|
||||
email: Scalars['String'];
|
||||
@@ -790,7 +790,7 @@ export type ImpersonateMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace?: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } | null }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
|
||||
export type RenewTokenMutationVariables = Exact<{
|
||||
refreshToken: Scalars['String'];
|
||||
@@ -821,7 +821,7 @@ export type VerifyMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace?: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } | null }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
|
||||
export type CheckUserExistsQueryVariables = Exact<{
|
||||
email: Scalars['String'];
|
||||
@@ -858,7 +858,7 @@ export type UploadImageMutationVariables = Exact<{
|
||||
|
||||
export type UploadImageMutation = { __typename?: 'Mutation', uploadImage: string };
|
||||
|
||||
export type UserQueryFragmentFragment = { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace?: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } | null };
|
||||
export type UserQueryFragmentFragment = { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } };
|
||||
|
||||
export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
@@ -877,7 +877,7 @@ export type CreateWorkspaceMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateWorkspaceMutation = { __typename?: 'Mutation', createWorkspace: { __typename?: 'User', id: string, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace?: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } | null } };
|
||||
export type CreateWorkspaceMutation = { __typename?: 'Mutation', createWorkspace: { __typename?: 'User', id: string, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } } };
|
||||
|
||||
export type DeleteCurrentWorkspaceMutationVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
@@ -141,13 +141,12 @@ export const useSignInUp = () => {
|
||||
|
||||
if (
|
||||
billing?.isBillingEnabled &&
|
||||
(!currentWorkspace ||
|
||||
currentWorkspace.subscriptionStatus !== 'active')
|
||||
currentWorkspace.subscriptionStatus !== 'active'
|
||||
) {
|
||||
navigate(AppPath.PlanRequired);
|
||||
return;
|
||||
}
|
||||
if (currentWorkspace?.displayName) {
|
||||
if (currentWorkspace.displayName) {
|
||||
navigate(AppPath.Index);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -27,14 +27,6 @@ export const getOnboardingStatus = ({
|
||||
if (!isLoggedIn) {
|
||||
return OnboardingStatus.OngoingUserCreation;
|
||||
}
|
||||
// if the user has not been fetched yet, we can't know the onboarding status
|
||||
if (currentWorkspace && !currentWorkspaceMember) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!currentWorkspace && isBillingEnabled) {
|
||||
return OnboardingStatus.Incomplete;
|
||||
}
|
||||
|
||||
if (
|
||||
isBillingEnabled &&
|
||||
@@ -50,9 +42,12 @@ export const getOnboardingStatus = ({
|
||||
if (!currentWorkspace?.displayName) {
|
||||
return OnboardingStatus.OngoingWorkspaceCreation;
|
||||
}
|
||||
if (!currentWorkspaceMember) {
|
||||
return undefined;
|
||||
}
|
||||
if (
|
||||
!currentWorkspaceMember?.name.firstName ||
|
||||
!currentWorkspaceMember?.name.lastName
|
||||
!currentWorkspaceMember.name.firstName ||
|
||||
!currentWorkspaceMember.name.lastName
|
||||
) {
|
||||
return OnboardingStatus.OngoingProfileCreation;
|
||||
}
|
||||
|
||||
@@ -157,13 +157,13 @@ export const PasswordReset = () => {
|
||||
|
||||
if (
|
||||
billing?.isBillingEnabled &&
|
||||
(!currentWorkspace || currentWorkspace.subscriptionStatus !== 'active')
|
||||
currentWorkspace.subscriptionStatus !== 'active'
|
||||
) {
|
||||
navigate(AppPath.PlanRequired);
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentWorkspace?.displayName) {
|
||||
if (currentWorkspace.displayName) {
|
||||
navigate(AppPath.Index);
|
||||
return;
|
||||
}
|
||||
|
||||
+1
-9
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
|
||||
import { Request } from 'express';
|
||||
|
||||
@@ -46,10 +42,6 @@ export class ApiRestQueryBuilderFactory {
|
||||
}> {
|
||||
const workspace = await this.tokenService.validateToken(request);
|
||||
|
||||
if (!workspace) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
const objectMetadataItems =
|
||||
await this.objectMetadataService.findManyWithinWorkspace(workspace.id);
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ export class AuthService {
|
||||
}
|
||||
|
||||
const passwordHash = password ? await hashPassword(password) : undefined;
|
||||
let workspace: Workspace | null = null;
|
||||
let workspace: Workspace | null;
|
||||
|
||||
if (workspaceInviteHash) {
|
||||
workspace = await this.workspaceRepository.findOneBy({
|
||||
@@ -126,6 +126,15 @@ export class AuthService {
|
||||
'Sign up is disabled',
|
||||
ForbiddenException,
|
||||
);
|
||||
|
||||
const workspaceToCreate = this.workspaceRepository.create({
|
||||
displayName: '',
|
||||
domainName: '',
|
||||
inviteHash: v4(),
|
||||
subscriptionStatus: 'incomplete',
|
||||
});
|
||||
|
||||
workspace = await this.workspaceRepository.save(workspaceToCreate);
|
||||
}
|
||||
|
||||
let imagePath: string | undefined = undefined;
|
||||
@@ -155,7 +164,7 @@ export class AuthService {
|
||||
defaultAvatarUrl: imagePath,
|
||||
canImpersonate: false,
|
||||
passwordHash,
|
||||
defaultWorkspace: workspace || undefined,
|
||||
defaultWorkspace: workspace,
|
||||
});
|
||||
|
||||
return await this.userRepository.save(userToCreate);
|
||||
@@ -166,12 +175,24 @@ export class AuthService {
|
||||
where: {
|
||||
email,
|
||||
},
|
||||
relations: ['defaultWorkspace'],
|
||||
});
|
||||
|
||||
assert(user, "This user doesn't exist", NotFoundException);
|
||||
|
||||
assert(
|
||||
user.defaultWorkspace,
|
||||
'User has no default workspace',
|
||||
NotFoundException,
|
||||
);
|
||||
|
||||
// passwordHash is hidden for security reasons
|
||||
user.passwordHash = '';
|
||||
const workspaceMember = await this.userService.loadWorkspaceMember(user);
|
||||
|
||||
if (workspaceMember) {
|
||||
user.workspaceMember = workspaceMember;
|
||||
}
|
||||
|
||||
const accessToken = await this.tokenService.generateAccessToken(user.id);
|
||||
const refreshToken = await this.tokenService.generateRefreshToken(user.id);
|
||||
|
||||
@@ -68,9 +68,13 @@ export class TokenService {
|
||||
throw new NotFoundException('User is not found');
|
||||
}
|
||||
|
||||
if (!user.defaultWorkspace) {
|
||||
throw new NotFoundException('User does not have a default workspace');
|
||||
}
|
||||
|
||||
const jwtPayload: JwtPayload = {
|
||||
sub: user.id,
|
||||
workspaceId: user.defaultWorkspace?.id || undefined,
|
||||
workspaceId: user.defaultWorkspace.id,
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -188,7 +192,7 @@ export class TokenService {
|
||||
return !!token;
|
||||
}
|
||||
|
||||
async validateToken(request: Request): Promise<Workspace | null | undefined> {
|
||||
async validateToken(request: Request): Promise<Workspace> {
|
||||
const token = ExtractJwt.fromAuthHeaderAsBearerToken()(request);
|
||||
|
||||
if (!token) {
|
||||
|
||||
@@ -16,12 +16,8 @@ import { User } from 'src/core/user/user.entity';
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
||||
|
||||
export type JwtPayload = {
|
||||
sub: string;
|
||||
workspaceId?: string;
|
||||
jti?: string;
|
||||
};
|
||||
export type PassportUser = { user?: User; workspace?: Workspace | null };
|
||||
export type JwtPayload = { sub: string; workspaceId: string; jti?: string };
|
||||
export type PassportUser = { user?: User; workspace: Workspace };
|
||||
|
||||
@Injectable()
|
||||
export class JwtAuthStrategy extends PassportStrategy(Strategy, 'jwt') {
|
||||
@@ -46,7 +42,11 @@ export class JwtAuthStrategy extends PassportStrategy(Strategy, 'jwt') {
|
||||
id: payload.workspaceId ?? payload.sub,
|
||||
});
|
||||
|
||||
if (workspace && payload.jti) {
|
||||
if (!workspace) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
if (payload.jti) {
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
workspace.id,
|
||||
@@ -68,7 +68,7 @@ export class JwtAuthStrategy extends PassportStrategy(Strategy, 'jwt') {
|
||||
|
||||
let user;
|
||||
|
||||
if (payload.workspaceId || !workspace) {
|
||||
if (payload.workspaceId) {
|
||||
user = await this.userRepository.findOne({
|
||||
where: { id: payload.sub },
|
||||
relations: ['defaultWorkspace'],
|
||||
|
||||
@@ -32,10 +32,6 @@ export class OpenApiService {
|
||||
try {
|
||||
const workspace = await this.tokenService.validateToken(request);
|
||||
|
||||
if (!workspace) {
|
||||
return schema;
|
||||
}
|
||||
|
||||
objectMetadataItems =
|
||||
await this.objectMetadataService.findManyWithinWorkspace(workspace.id);
|
||||
} catch (err) {
|
||||
|
||||
@@ -2,7 +2,6 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
|
||||
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
@@ -88,10 +87,6 @@ export class UserService extends TypeOrmQueryService<User> {
|
||||
);
|
||||
}
|
||||
|
||||
async updateUser(userId: string, data: QueryDeepPartialEntity<User>) {
|
||||
await this.userRepository.update(userId, data);
|
||||
}
|
||||
|
||||
async getUser(userId: string): Promise<User> {
|
||||
return await this.userRepository.findOneOrFail({
|
||||
where: { id: userId },
|
||||
|
||||
@@ -66,7 +66,7 @@ export class User {
|
||||
@Column({ nullable: true })
|
||||
deletedAt: Date;
|
||||
|
||||
@Field(() => Workspace, { nullable: true })
|
||||
@Field(() => Workspace, { nullable: false })
|
||||
@ManyToOne(() => Workspace, (workspace) => workspace.users, {
|
||||
onDelete: 'SET NULL',
|
||||
})
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
|
||||
import assert from 'assert';
|
||||
|
||||
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { WorkspaceManagerService } from 'src/workspace/workspace-manager/workspace-manager.service';
|
||||
import { Workspace } from 'src/core/workspace/workspace.entity';
|
||||
@@ -23,25 +23,16 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
}
|
||||
|
||||
async createWorkspace(user: User, data: CreateWorkspaceInput) {
|
||||
const workspaceToCreate = this.workspaceRepository.create({
|
||||
displayName: '',
|
||||
domainName: '',
|
||||
inviteHash: v4(),
|
||||
subscriptionStatus: 'active',
|
||||
...data,
|
||||
if (!data.displayName || !data.displayName.length) {
|
||||
throw new BadRequestException("'displayName' not provided");
|
||||
}
|
||||
await this.workspaceRepository.update(user.defaultWorkspace.id, {
|
||||
displayName: data.displayName,
|
||||
});
|
||||
const workspace = await this.workspaceRepository.save(workspaceToCreate);
|
||||
await this.workspaceManagerService.init(user.defaultWorkspace.id);
|
||||
await this.userService.createWorkspaceMember(user);
|
||||
|
||||
await this.userService.updateUser(user.id, {
|
||||
defaultWorkspace: workspace,
|
||||
});
|
||||
|
||||
await this.workspaceManagerService.init(workspace.id);
|
||||
const updatedUser = await this.userService.getUser(user.id);
|
||||
|
||||
await this.userService.createWorkspaceMember(updatedUser);
|
||||
|
||||
return updatedUser;
|
||||
return await this.userService.getUser(user.id);
|
||||
}
|
||||
|
||||
async deleteWorkspace(id: string) {
|
||||
|
||||
@@ -62,10 +62,6 @@ export class GraphQLConfigService
|
||||
|
||||
const workspace = await this.tokenService.validateToken(context.req);
|
||||
|
||||
if (!workspace) {
|
||||
return new GraphQLSchema({});
|
||||
}
|
||||
|
||||
return await this.createSchema(context, workspace);
|
||||
} catch (error) {
|
||||
if (error instanceof UnauthorizedException) {
|
||||
|
||||
Reference in New Issue
Block a user