mirror of
https://github.com/docmost/docmost.git
synced 2025-11-20 16:11:09 +10:00
Implement Space membership by group
* Add all users to default group * Fixes and updates
This commit is contained in:
@ -10,3 +10,7 @@ export class CreateGroupDto {
|
||||
@IsString()
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export enum DefaultGroup {
|
||||
EVERYONE = 'users',
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ export class GroupUser {
|
||||
@Column()
|
||||
userId: string;
|
||||
|
||||
@ManyToOne(() => User, (user) => user.groups, {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'userId' })
|
||||
|
||||
@ -11,8 +11,11 @@ import {
|
||||
import { GroupUser } from './group-user.entity';
|
||||
import { Workspace } from '../../workspace/entities/workspace.entity';
|
||||
import { User } from '../../user/entities/user.entity';
|
||||
import { Unique } from 'typeorm';
|
||||
import { SpaceGroup } from '../../space/entities/space-group.entity';
|
||||
|
||||
@Entity('groups')
|
||||
@Unique(['name', 'workspaceId'])
|
||||
export class Group {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
@ -23,6 +26,9 @@ export class Group {
|
||||
@Column({ type: 'text', nullable: true })
|
||||
description: string;
|
||||
|
||||
@Column({ type: 'boolean', default: false })
|
||||
isDefault: boolean;
|
||||
|
||||
@Column()
|
||||
workspaceId: string;
|
||||
|
||||
@ -32,7 +38,7 @@ export class Group {
|
||||
@JoinColumn({ name: 'workspaceId' })
|
||||
workspace: Workspace;
|
||||
|
||||
@Column()
|
||||
@Column({ nullable: true })
|
||||
creatorId: string;
|
||||
|
||||
@ManyToOne(() => User)
|
||||
@ -48,5 +54,8 @@ export class Group {
|
||||
@OneToMany(() => GroupUser, (groupUser) => groupUser.group)
|
||||
groupUsers: GroupUser[];
|
||||
|
||||
@OneToMany(() => SpaceGroup, (spaceGroup) => spaceGroup.group)
|
||||
spaces: SpaceGroup[];
|
||||
|
||||
userCount?: number;
|
||||
}
|
||||
|
||||
@ -119,11 +119,12 @@ export class GroupController {
|
||||
removeGroupMember(
|
||||
@Body() removeGroupUserDto: RemoveGroupUserDto,
|
||||
//@AuthUser() user: User,
|
||||
//@CurrentWorkspace() workspace: Workspace,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
) {
|
||||
return this.groupUserService.removeUserFromGroup(
|
||||
removeGroupUserDto.userId,
|
||||
removeGroupUserDto.groupId,
|
||||
workspace.id,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -17,5 +17,6 @@ import { GroupUserService } from './services/group-user.service';
|
||||
GroupRepository,
|
||||
GroupUserRepository,
|
||||
],
|
||||
exports: [GroupService, GroupUserService],
|
||||
})
|
||||
export class GroupModule {}
|
||||
|
||||
@ -27,7 +27,7 @@ export class GroupUserService {
|
||||
workspaceId: string,
|
||||
paginationOptions: PaginationOptions,
|
||||
): Promise<PaginatedResult<User>> {
|
||||
await this.groupService.validateGroup(groupId, workspaceId);
|
||||
await this.groupService.findAndValidateGroup(groupId, workspaceId);
|
||||
|
||||
const [groupUsers, count] = await this.groupUserRepository.findAndCount({
|
||||
relations: ['user'],
|
||||
@ -49,16 +49,36 @@ export class GroupUserService {
|
||||
return new PaginatedResult(users, paginationMeta);
|
||||
}
|
||||
|
||||
async addUserToDefaultGroup(
|
||||
userId: string,
|
||||
workspaceId: string,
|
||||
manager?: EntityManager,
|
||||
): Promise<void> {
|
||||
return await transactionWrapper(
|
||||
async (manager) => {
|
||||
const defaultGroup = await this.groupService.getDefaultGroup(
|
||||
workspaceId,
|
||||
manager,
|
||||
);
|
||||
await this.addUserToGroup(
|
||||
userId,
|
||||
defaultGroup.id,
|
||||
workspaceId,
|
||||
manager,
|
||||
);
|
||||
},
|
||||
this.dataSource,
|
||||
manager,
|
||||
);
|
||||
}
|
||||
|
||||
async addUserToGroup(
|
||||
userId: string,
|
||||
groupId: string,
|
||||
workspaceId: string,
|
||||
manager?: EntityManager,
|
||||
): Promise<any> {
|
||||
let addedUser;
|
||||
|
||||
/*
|
||||
await transactionWrapper(
|
||||
): Promise<GroupUser> {
|
||||
return await transactionWrapper(
|
||||
async (manager) => {
|
||||
const group = await manager.findOneBy(Group, {
|
||||
id: groupId,
|
||||
@ -69,21 +89,18 @@ export class GroupUserService {
|
||||
throw new NotFoundException('Group not found');
|
||||
}
|
||||
|
||||
const userExists = await manager.exists(User, {
|
||||
const find = await manager.findOne(User, {
|
||||
where: { id: userId },
|
||||
});
|
||||
if (!userExists) {
|
||||
throw new NotFoundException('User not found');
|
||||
}
|
||||
|
||||
// only workspace users can be added to workspace groups
|
||||
const workspaceUser = await manager.findOneBy(WorkspaceUser, {
|
||||
userId: userId,
|
||||
workspaceId: workspaceId,
|
||||
console.log(find);
|
||||
|
||||
const userExists = await manager.exists(User, {
|
||||
where: { id: userId, workspaceId },
|
||||
});
|
||||
|
||||
if (!workspaceUser) {
|
||||
throw new NotFoundException('User is not a member of this workspace');
|
||||
if (!userExists) {
|
||||
throw new NotFoundException('User not found');
|
||||
}
|
||||
|
||||
const existingGroupUser = await manager.findOneBy(GroupUser, {
|
||||
@ -101,16 +118,29 @@ export class GroupUserService {
|
||||
groupUser.userId = userId;
|
||||
groupUser.groupId = groupId;
|
||||
|
||||
addedUser = await manager.save(groupUser);
|
||||
return manager.save(groupUser);
|
||||
},
|
||||
this.dataSource,
|
||||
manager,
|
||||
);
|
||||
*/
|
||||
return addedUser;
|
||||
}
|
||||
|
||||
async removeUserFromGroup(userId: string, groupId: string): Promise<void> {
|
||||
async removeUserFromGroup(
|
||||
userId: string,
|
||||
groupId: string,
|
||||
workspaceId: string,
|
||||
): Promise<void> {
|
||||
const group = await this.groupService.findAndValidateGroup(
|
||||
groupId,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (group.isDefault) {
|
||||
throw new BadRequestException(
|
||||
'You cannot remove users from a default group',
|
||||
);
|
||||
}
|
||||
|
||||
const groupUser = await this.getGroupUser(userId, groupId);
|
||||
|
||||
if (!groupUser) {
|
||||
@ -129,12 +159,4 @@ export class GroupUserService {
|
||||
groupId,
|
||||
});
|
||||
}
|
||||
|
||||
async getGroupUserCount(groupId: string): Promise<number> {
|
||||
return await this.groupUserRepository.count({
|
||||
where: {
|
||||
groupId: groupId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { CreateGroupDto } from '../dto/create-group.dto';
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { CreateGroupDto, DefaultGroup } from '../dto/create-group.dto';
|
||||
import { GroupRepository } from '../respositories/group.repository';
|
||||
import { Group } from '../entities/group.entity';
|
||||
import { plainToInstance } from 'class-transformer';
|
||||
@ -8,10 +12,15 @@ import { PaginationMetaDto } from '../../../helpers/pagination/pagination-meta-d
|
||||
import { PaginatedResult } from '../../../helpers/pagination/paginated-result';
|
||||
import { PaginationOptions } from '../../../helpers/pagination/pagination-options';
|
||||
import { UpdateGroupDto } from '../dto/update-group.dto';
|
||||
import { DataSource, EntityManager } from 'typeorm';
|
||||
import { transactionWrapper } from '../../../helpers/db.helper';
|
||||
|
||||
@Injectable()
|
||||
export class GroupService {
|
||||
constructor(private groupRepository: GroupRepository) {}
|
||||
constructor(
|
||||
private groupRepository: GroupRepository,
|
||||
private dataSource: DataSource,
|
||||
) {}
|
||||
|
||||
async createGroup(
|
||||
authUser: User,
|
||||
@ -22,9 +31,52 @@ export class GroupService {
|
||||
group.creatorId = authUser.id;
|
||||
group.workspaceId = workspaceId;
|
||||
|
||||
const groupExists = await this.findGroupByName(
|
||||
createGroupDto.name,
|
||||
workspaceId,
|
||||
);
|
||||
if (groupExists) {
|
||||
throw new BadRequestException('Group name already exists');
|
||||
}
|
||||
|
||||
return await this.groupRepository.save(group);
|
||||
}
|
||||
|
||||
async createDefaultGroup(
|
||||
workspaceId: string,
|
||||
userId?: string,
|
||||
manager?: EntityManager,
|
||||
): Promise<Group> {
|
||||
return await transactionWrapper(
|
||||
async (manager: EntityManager) => {
|
||||
const group = new Group();
|
||||
group.name = DefaultGroup.EVERYONE;
|
||||
group.isDefault = true;
|
||||
group.creatorId = userId ?? null;
|
||||
group.workspaceId = workspaceId;
|
||||
return await manager.save(group);
|
||||
},
|
||||
this.dataSource,
|
||||
manager,
|
||||
);
|
||||
}
|
||||
|
||||
async getDefaultGroup(
|
||||
workspaceId: string,
|
||||
manager: EntityManager,
|
||||
): Promise<Group> {
|
||||
return await transactionWrapper(
|
||||
async (manager: EntityManager) => {
|
||||
return await manager.findOneBy(Group, {
|
||||
isDefault: true,
|
||||
workspaceId,
|
||||
});
|
||||
},
|
||||
this.dataSource,
|
||||
manager,
|
||||
);
|
||||
}
|
||||
|
||||
async updateGroup(
|
||||
workspaceId: string,
|
||||
updateGroupDto: UpdateGroupDto,
|
||||
@ -38,6 +90,18 @@ export class GroupService {
|
||||
throw new NotFoundException('Group not found');
|
||||
}
|
||||
|
||||
if (group.isDefault) {
|
||||
throw new BadRequestException('You cannot update a default group');
|
||||
}
|
||||
|
||||
const groupExists = await this.findGroupByName(
|
||||
updateGroupDto.name,
|
||||
workspaceId,
|
||||
);
|
||||
if (groupExists) {
|
||||
throw new BadRequestException('Group name already exists');
|
||||
}
|
||||
|
||||
if (updateGroupDto.name) {
|
||||
group.name = updateGroupDto.name;
|
||||
}
|
||||
@ -90,19 +154,38 @@ export class GroupService {
|
||||
}
|
||||
|
||||
async deleteGroup(groupId: string, workspaceId: string): Promise<void> {
|
||||
await this.validateGroup(groupId, workspaceId);
|
||||
const group = await this.findAndValidateGroup(groupId, workspaceId);
|
||||
if (group.isDefault) {
|
||||
throw new BadRequestException('You cannot delete a default group');
|
||||
}
|
||||
await this.groupRepository.delete(groupId);
|
||||
}
|
||||
|
||||
async validateGroup(groupId: string, workspaceId: string): Promise<void> {
|
||||
const groupExists = await this.groupRepository.exists({
|
||||
async findAndValidateGroup(
|
||||
groupId: string,
|
||||
workspaceId: string,
|
||||
): Promise<Group> {
|
||||
const group = await this.groupRepository.findOne({
|
||||
where: {
|
||||
id: groupId,
|
||||
workspaceId: workspaceId,
|
||||
},
|
||||
});
|
||||
if (!groupExists) {
|
||||
if (!group) {
|
||||
throw new NotFoundException('Group not found');
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
async findGroupByName(
|
||||
groupName: string,
|
||||
workspaceId: string,
|
||||
): Promise<Group> {
|
||||
return this.groupRepository
|
||||
.createQueryBuilder('group')
|
||||
.where('LOWER(group.name) = LOWER(:groupName)', { groupName })
|
||||
.andWhere('group.workspaceId = :workspaceId', { workspaceId })
|
||||
.getOne();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user