mirror of
https://github.com/docmost/docmost.git
synced 2025-11-20 11:01:08 +10:00
feat: spaces - WIP
This commit is contained in:
12
apps/server/src/core/space/dto/create-space.dto.ts
Normal file
12
apps/server/src/core/space/dto/create-space.dto.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { IsOptional, IsString, MaxLength, MinLength } from 'class-validator';
|
||||
|
||||
export class CreateSpaceDto {
|
||||
@MinLength(4)
|
||||
@MaxLength(64)
|
||||
@IsString()
|
||||
name: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string;
|
||||
}
|
||||
6
apps/server/src/core/space/dto/delete-space.dto.ts
Normal file
6
apps/server/src/core/space/dto/delete-space.dto.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { IsString } from 'class-validator';
|
||||
|
||||
export class DeleteSpaceDto {
|
||||
@IsString()
|
||||
spaceId: string;
|
||||
}
|
||||
4
apps/server/src/core/space/dto/update-space.dto.ts
Normal file
4
apps/server/src/core/space/dto/update-space.dto.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateSpaceDto } from './create-space.dto';
|
||||
|
||||
export class UpdateSpaceDto extends PartialType(CreateSpaceDto) {}
|
||||
46
apps/server/src/core/space/entities/space-user.entity.ts
Normal file
46
apps/server/src/core/space/entities/space-user.entity.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
Unique,
|
||||
} from 'typeorm';
|
||||
import { User } from '../../user/entities/user.entity';
|
||||
import { Space } from './space.entity';
|
||||
|
||||
@Entity('space_users')
|
||||
@Unique(['spaceId', 'userId'])
|
||||
export class SpaceUser {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
userId: string;
|
||||
|
||||
@ManyToOne(() => User, (user) => user.spaceUsers, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'userId' })
|
||||
user: User;
|
||||
|
||||
@Column()
|
||||
spaceId: string;
|
||||
|
||||
@ManyToOne(() => Space, (space) => space.spaceUsers, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'spaceId' })
|
||||
space: Space;
|
||||
|
||||
@Column({ length: 100, nullable: true })
|
||||
role: string;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
}
|
||||
54
apps/server/src/core/space/entities/space.entity.ts
Normal file
54
apps/server/src/core/space/entities/space.entity.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { User } from '../../user/entities/user.entity';
|
||||
import { Workspace } from '../../workspace/entities/workspace.entity';
|
||||
import { SpaceUser } from './space-user.entity';
|
||||
|
||||
@Entity('spaces')
|
||||
export class Space {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ length: 255, nullable: true })
|
||||
name: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
description: string;
|
||||
|
||||
@Column({ length: 255, nullable: true })
|
||||
icon: string;
|
||||
|
||||
@Column({ length: 255, nullable: true, unique: true })
|
||||
hostname: string;
|
||||
|
||||
@Column()
|
||||
creatorId: string;
|
||||
|
||||
@ManyToOne(() => User, (user) => user.spaces)
|
||||
@JoinColumn({ name: 'creatorId' })
|
||||
creator: User;
|
||||
|
||||
@Column()
|
||||
workspaceId: string;
|
||||
|
||||
@ManyToOne(() => Workspace, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'workspaceId' })
|
||||
workspace: Workspace;
|
||||
|
||||
@OneToMany(() => SpaceUser, (workspaceUser) => workspaceUser.space)
|
||||
spaceUsers: SpaceUser[];
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
import { SpaceUser } from '../entities/space-user.entity';
|
||||
|
||||
@Injectable()
|
||||
export class SpaceUserRepository extends Repository<SpaceUser> {
|
||||
constructor(private dataSource: DataSource) {
|
||||
super(SpaceUser, dataSource.createEntityManager());
|
||||
}
|
||||
}
|
||||
14
apps/server/src/core/space/repositories/space.repository.ts
Normal file
14
apps/server/src/core/space/repositories/space.repository.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
import { Space } from '../entities/space.entity';
|
||||
|
||||
@Injectable()
|
||||
export class SpaceRepository extends Repository<Space> {
|
||||
constructor(private dataSource: DataSource) {
|
||||
super(Space, dataSource.createEntityManager());
|
||||
}
|
||||
|
||||
async findById(spaceId: string) {
|
||||
return this.findOneBy({ id: spaceId });
|
||||
}
|
||||
}
|
||||
20
apps/server/src/core/space/space.controller.spec.ts
Normal file
20
apps/server/src/core/space/space.controller.spec.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { SpaceController } from './space.controller';
|
||||
import { SpaceService } from './space.service';
|
||||
|
||||
describe('SpaceController', () => {
|
||||
let controller: SpaceController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [SpaceController],
|
||||
providers: [SpaceService],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<SpaceController>(SpaceController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
22
apps/server/src/core/space/space.controller.ts
Normal file
22
apps/server/src/core/space/space.controller.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import {
|
||||
Controller,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
Post,
|
||||
Req,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { FastifyRequest } from 'fastify';
|
||||
import { JwtGuard } from '../auth/guards/jwt.guard';
|
||||
import { SpaceService } from './space.service';
|
||||
|
||||
@UseGuards(JwtGuard)
|
||||
@Controller('spaces')
|
||||
export class SpaceController {
|
||||
constructor(private readonly spaceService: SpaceService) {}
|
||||
|
||||
// get all spaces user is a member of
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Post('/')
|
||||
async getUserSpaces(@Req() req: FastifyRequest) {}
|
||||
}
|
||||
17
apps/server/src/core/space/space.module.ts
Normal file
17
apps/server/src/core/space/space.module.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { SpaceService } from './space.service';
|
||||
import { SpaceController } from './space.controller';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Space } from './entities/space.entity';
|
||||
import { AuthModule } from '../auth/auth.module';
|
||||
import { SpaceUser } from './entities/space-user.entity';
|
||||
import { SpaceRepository } from './repositories/space.repository';
|
||||
import { SpaceUserRepository } from './repositories/space-user.repository';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Space, SpaceUser]), AuthModule],
|
||||
controllers: [SpaceController],
|
||||
providers: [SpaceService, SpaceRepository, SpaceUserRepository],
|
||||
exports: [SpaceService, SpaceRepository, SpaceUserRepository],
|
||||
})
|
||||
export class SpaceModule {}
|
||||
18
apps/server/src/core/space/space.service.spec.ts
Normal file
18
apps/server/src/core/space/space.service.spec.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { SpaceService } from './space.service';
|
||||
|
||||
describe('SpaceService', () => {
|
||||
let service: SpaceService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [SpaceService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<SpaceService>(SpaceService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
69
apps/server/src/core/space/space.service.ts
Normal file
69
apps/server/src/core/space/space.service.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { CreateSpaceDto } from './dto/create-space.dto';
|
||||
import { Space } from './entities/space.entity';
|
||||
import { plainToInstance } from 'class-transformer';
|
||||
import { SpaceRepository } from './repositories/space.repository';
|
||||
import { SpaceUserRepository } from './repositories/space-user.repository';
|
||||
import { SpaceUser } from './entities/space-user.entity';
|
||||
|
||||
@Injectable()
|
||||
export class SpaceService {
|
||||
constructor(
|
||||
private spaceRepository: SpaceRepository,
|
||||
private spaceUserRepository: SpaceUserRepository,
|
||||
) {}
|
||||
|
||||
async create(userId: string, workspaceId, createSpaceDto?: CreateSpaceDto) {
|
||||
let space: Space;
|
||||
|
||||
if (createSpaceDto) {
|
||||
space = plainToInstance(Space, createSpaceDto);
|
||||
} else {
|
||||
space = new Space();
|
||||
}
|
||||
|
||||
space.creatorId = userId;
|
||||
space.workspaceId = workspaceId;
|
||||
|
||||
space.name = createSpaceDto?.name ?? 'untitled space';
|
||||
space.description = createSpaceDto?.description ?? null;
|
||||
|
||||
space = await this.spaceRepository.save(space);
|
||||
return space;
|
||||
}
|
||||
|
||||
async addUserToSpace(
|
||||
userId: string,
|
||||
spaceId: string,
|
||||
role: string,
|
||||
): Promise<SpaceUser> {
|
||||
const existingSpaceUser = await this.spaceUserRepository.findOne({
|
||||
where: { userId: userId, spaceId: spaceId },
|
||||
});
|
||||
|
||||
if (existingSpaceUser) {
|
||||
throw new BadRequestException('User already added to this space');
|
||||
}
|
||||
|
||||
const spaceUser = new SpaceUser();
|
||||
spaceUser.userId = userId;
|
||||
spaceUser.spaceId = spaceId;
|
||||
spaceUser.role = role;
|
||||
|
||||
return this.spaceUserRepository.save(spaceUser);
|
||||
}
|
||||
|
||||
async getUserSpacesInWorkspace(userId: string, workspaceId: string) {
|
||||
const spaces = await this.spaceUserRepository.find({
|
||||
relations: ['space'],
|
||||
where: {
|
||||
userId: userId,
|
||||
space: {
|
||||
workspaceId: workspaceId,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return spaces.map((userSpace: SpaceUser) => userSpace.space);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user