4689 multi workspace i should be able to accept an invite if im already logged in (#5454)

- split signInUp to separate Invitation from signInUp
- update redirection logic
- add a resolver for userWorkspace
- add a mutation to add a user to a workspace
- authorize /invite/hash while loggedIn
- add a button to join a workspace

### Base functionnality

https://github.com/twentyhq/twenty/assets/29927851/a1075a4e-a2af-4184-aa3e-e163711277a1

### Error handling

https://github.com/twentyhq/twenty/assets/29927851/1bdd78ce-933a-4860-a87a-3f1f7bda389e
This commit is contained in:
martmull
2024-05-20 12:11:38 +02:00
committed by GitHub
parent 1ceeb68da8
commit 88f5eb669e
17 changed files with 340 additions and 101 deletions

View File

@ -27,7 +27,6 @@ import { EmailPasswordResetLink } from 'src/engine/core-modules/auth/dto/email-p
import { InvalidatePassword } from 'src/engine/core-modules/auth/dto/invalidate-password.entity';
import { EmailPasswordResetLinkInput } from 'src/engine/core-modules/auth/dto/email-password-reset-link.input';
import { GenerateJwtInput } from 'src/engine/core-modules/auth/dto/generate-jwt.input';
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
import { AuthorizeApp } from 'src/engine/core-modules/auth/dto/authorize-app.entity';
import { AuthorizeAppInput } from 'src/engine/core-modules/auth/dto/authorize-app.input';
import { ExchangeAuthCodeInput } from 'src/engine/core-modules/auth/dto/exchange-auth-code.input';
@ -56,7 +55,6 @@ export class AuthResolver {
private authService: AuthService,
private tokenService: TokenService,
private userService: UserService,
private userWorkspaceService: UserWorkspaceService,
) {}
@UseGuards(CaptchaGuard)

View File

@ -150,26 +150,10 @@ export class SignInUpService {
);
if (existingUser) {
const userWorkspaceExists =
await this.userWorkspaceService.checkUserWorkspaceExists(
existingUser.id,
workspace.id,
);
if (!userWorkspaceExists) {
await this.userWorkspaceService.create(existingUser.id, workspace.id);
await this.userWorkspaceService.createWorkspaceMember(
workspace.id,
existingUser,
);
}
const updatedUser = await this.userRepository.save({
id: existingUser.id,
defaultWorkspace: workspace,
updatedAt: new Date().toISOString(),
});
const updatedUser = await this.userWorkspaceService.addUserToWorkspace(
existingUser,
workspace,
);
return Object.assign(existingUser, updatedUser);
}

View File

@ -8,12 +8,13 @@ import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-works
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { User } from 'src/engine/core-modules/user/user.entity';
@Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [
NestjsQueryTypeOrmModule.forFeature([UserWorkspace], 'core'),
NestjsQueryTypeOrmModule.forFeature([User, UserWorkspace], 'core'),
TypeORMModule,
DataSourceModule,
WorkspaceDataSourceModule,

View File

@ -0,0 +1,41 @@
import { UseGuards } from '@nestjs/common';
import { Args, Mutation, Resolver } from '@nestjs/graphql';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { JwtAuthGuard } from 'src/engine/guards/jwt.auth.guard';
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator';
import { User } from 'src/engine/core-modules/user/user.entity';
import { WorkspaceInviteHashValidInput } from 'src/engine/core-modules/auth/dto/workspace-invite-hash.input';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
@UseGuards(JwtAuthGuard)
@Resolver(() => UserWorkspace)
export class UserWorkspaceResolver {
constructor(
@InjectRepository(Workspace, 'core')
private readonly workspaceRepository: Repository<Workspace>,
@InjectRepository(User, 'core')
private readonly userRepository: Repository<User>,
private readonly userWorkspaceService: UserWorkspaceService,
) {}
@Mutation(() => User)
async addUserToWorkspace(
@AuthUser() user: User,
@Args() workspaceInviteHashValidInput: WorkspaceInviteHashValidInput,
) {
const workspace = await this.workspaceRepository.findOneBy({
inviteHash: workspaceInviteHashValidInput.inviteHash,
});
if (!workspace) {
return;
}
return await this.userWorkspaceService.addUserToWorkspace(user, workspace);
}
}

View File

@ -12,11 +12,14 @@ import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/work
import { ObjectRecordCreateEvent } from 'src/engine/integrations/event-emitter/types/object-record-create.event';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
import { assert } from 'src/utils/assert';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
export class UserWorkspaceService extends TypeOrmQueryService<UserWorkspace> {
constructor(
@InjectRepository(UserWorkspace, 'core')
private readonly userWorkspaceRepository: Repository<UserWorkspace>,
@InjectRepository(User, 'core')
private readonly userRepository: Repository<User>,
private readonly dataSourceService: DataSourceService,
private readonly typeORMService: TypeORMService,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
@ -70,6 +73,25 @@ export class UserWorkspaceService extends TypeOrmQueryService<UserWorkspace> {
this.eventEmitter.emit('workspaceMember.created', payload);
}
async addUserToWorkspace(user: User, workspace: Workspace) {
const userWorkspaceExists = await this.checkUserWorkspaceExists(
user.id,
workspace.id,
);
if (!userWorkspaceExists) {
await this.create(user.id, workspace.id);
await this.createWorkspaceMember(workspace.id, user);
}
return await this.userRepository.save({
id: user.id,
defaultWorkspace: workspace,
updatedAt: new Date().toISOString(),
});
}
public async getWorkspaceMemberCount(
workspaceId: string,
): Promise<number | undefined> {

View File

@ -15,6 +15,7 @@ import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-s
import { WorkspaceWorkspaceMemberListener } from 'src/engine/core-modules/workspace/workspace-workspace-member.listener';
import { WorkspaceCacheVersionModule } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.module';
import { User } from 'src/engine/core-modules/user/user.entity';
import { UserWorkspaceResolver } from 'src/engine/core-modules/user-workspace/user-workspace.resolver';
import { workspaceAutoResolverOpts } from './workspace.auto-resolver-opts';
import { Workspace } from './workspace.entity';
@ -46,6 +47,7 @@ import { WorkspaceService } from './services/workspace.service';
providers: [
WorkspaceResolver,
WorkspaceService,
UserWorkspaceResolver,
WorkspaceWorkspaceMemberListener,
],
})