mirror of
https://github.com/docmost/docmost.git
synced 2025-11-13 00:52:40 +10:00
workspace - wip
This commit is contained in:
1
server/.gitignore
vendored
1
server/.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
.env
|
.env
|
||||||
|
package-lock.json
|
||||||
# compiled output
|
# compiled output
|
||||||
/dist
|
/dist
|
||||||
/node_modules
|
/node_modules
|
||||||
|
|||||||
9436
server/package-lock.json
generated
9436
server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,7 @@
|
|||||||
"@nestjs/platform-express": "^10.0.0",
|
"@nestjs/platform-express": "^10.0.0",
|
||||||
"@nestjs/platform-fastify": "^10.1.3",
|
"@nestjs/platform-fastify": "^10.1.3",
|
||||||
"@nestjs/typeorm": "^10.0.0",
|
"@nestjs/typeorm": "^10.0.0",
|
||||||
|
"@types/uuid": "^9.0.2",
|
||||||
"bcrypt": "^5.1.0",
|
"bcrypt": "^5.1.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.0",
|
||||||
@ -42,7 +43,8 @@
|
|||||||
"pg": "^8.11.2",
|
"pg": "^8.11.2",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"typeorm": "^0.3.17"
|
"typeorm": "^0.3.17",
|
||||||
|
"uuid": "^9.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^10.0.0",
|
"@nestjs/cli": "^10.0.0",
|
||||||
|
|||||||
@ -19,6 +19,7 @@ export class AuthController {
|
|||||||
return await this.authService.login(loginInput);
|
return await this.authService.login(loginInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
@Post('register')
|
@Post('register')
|
||||||
async register(@Body() createUserDto: CreateUserDto) {
|
async register(@Body() createUserDto: CreateUserDto) {
|
||||||
return await this.authService.register(createUserDto);
|
return await this.authService.register(createUserDto);
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { forwardRef, Module } from '@nestjs/common';
|
||||||
import { AuthController } from './auth.controller';
|
import { AuthController } from './auth.controller';
|
||||||
import { AuthService } from './services/auth.service';
|
import { AuthService } from './services/auth.service';
|
||||||
import { JwtModule } from '@nestjs/jwt';
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
import { EnvironmentService } from '../../environment/environment.service';
|
import { EnvironmentService } from '../../environment/environment.service';
|
||||||
import { TokenService } from './services/token.service';
|
import { TokenService } from './services/token.service';
|
||||||
import { UserService } from '../user/user.service';
|
import { UserModule } from '../user/user.module';
|
||||||
import { UserRepository } from '../user/repositories/user.repository';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -21,9 +20,10 @@ import { UserRepository } from '../user/repositories/user.repository';
|
|||||||
},
|
},
|
||||||
inject: [EnvironmentService],
|
inject: [EnvironmentService],
|
||||||
}),
|
}),
|
||||||
|
forwardRef(() => UserModule),
|
||||||
],
|
],
|
||||||
exports: [TokenService],
|
|
||||||
controllers: [AuthController],
|
controllers: [AuthController],
|
||||||
providers: [AuthService, TokenService, UserService, UserRepository],
|
providers: [AuthService, TokenService],
|
||||||
|
exports: [TokenService],
|
||||||
})
|
})
|
||||||
export class AuthModule {}
|
export class AuthModule {}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { UserModule } from './user/user.module';
|
import { UserModule } from './user/user.module';
|
||||||
import { AuthModule } from './auth/auth.module';
|
import { AuthModule } from './auth/auth.module';
|
||||||
|
import { WorkspaceModule } from './workspace/workspace.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [UserModule, AuthModule],
|
imports: [UserModule, AuthModule, WorkspaceModule],
|
||||||
})
|
})
|
||||||
export class CoreModule {}
|
export class CoreModule {}
|
||||||
|
|||||||
@ -3,10 +3,13 @@ import {
|
|||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
Entity,
|
Entity,
|
||||||
|
OneToMany,
|
||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import * as bcrypt from 'bcrypt';
|
import * as bcrypt from 'bcrypt';
|
||||||
|
import { Workspace } from '../../workspace/entities/workspace.entity';
|
||||||
|
import { WorkspaceUser } from '../../workspace/entities/workspace-user.entity';
|
||||||
|
|
||||||
@Entity('users')
|
@Entity('users')
|
||||||
export class User {
|
export class User {
|
||||||
@ -49,6 +52,16 @@ export class User {
|
|||||||
@UpdateDateColumn()
|
@UpdateDateColumn()
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
|
|
||||||
|
@OneToMany(() => Workspace, (workspace) => workspace.creator, {
|
||||||
|
createForeignKeyConstraints: false,
|
||||||
|
})
|
||||||
|
workspaces: Workspace[];
|
||||||
|
|
||||||
|
@OneToMany(() => WorkspaceUser, (workspaceUser) => workspaceUser.user, {
|
||||||
|
createForeignKeyConstraints: false,
|
||||||
|
})
|
||||||
|
workspaceUser: WorkspaceUser[];
|
||||||
|
|
||||||
toJSON() {
|
toJSON() {
|
||||||
delete this.password;
|
delete this.password;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@ -1,14 +1,20 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { forwardRef, Module } from '@nestjs/common';
|
||||||
import { UserService } from './user.service';
|
import { UserService } from './user.service';
|
||||||
import { UserController } from './user.controller';
|
import { UserController } from './user.controller';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { User } from './entities/user.entity';
|
import { User } from './entities/user.entity';
|
||||||
import { UserRepository } from './repositories/user.repository';
|
import { UserRepository } from './repositories/user.repository';
|
||||||
import { AuthModule } from '../auth/auth.module';
|
import { AuthModule } from '../auth/auth.module';
|
||||||
|
import { WorkspaceModule } from '../workspace/workspace.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([User]), AuthModule],
|
imports: [
|
||||||
|
TypeOrmModule.forFeature([User]),
|
||||||
|
forwardRef(() => AuthModule),
|
||||||
|
WorkspaceModule,
|
||||||
|
],
|
||||||
controllers: [UserController],
|
controllers: [UserController],
|
||||||
providers: [UserService, UserRepository],
|
providers: [UserService, UserRepository],
|
||||||
|
exports: [UserService, UserRepository],
|
||||||
})
|
})
|
||||||
export class UserModule {}
|
export class UserModule {}
|
||||||
|
|||||||
@ -5,10 +5,15 @@ import { User } from './entities/user.entity';
|
|||||||
import { UserRepository } from './repositories/user.repository';
|
import { UserRepository } from './repositories/user.repository';
|
||||||
import { plainToClass } from 'class-transformer';
|
import { plainToClass } from 'class-transformer';
|
||||||
import * as bcrypt from 'bcrypt';
|
import * as bcrypt from 'bcrypt';
|
||||||
|
import { WorkspaceService } from '../workspace/services/workspace.service';
|
||||||
|
import { CreateWorkspaceDto } from '../workspace/dto/create-workspace.dto';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
constructor(private userRepository: UserRepository) {}
|
constructor(
|
||||||
|
private userRepository: UserRepository,
|
||||||
|
private workspaceService: WorkspaceService,
|
||||||
|
) {}
|
||||||
async create(createUserDto: CreateUserDto): Promise<User> {
|
async create(createUserDto: CreateUserDto): Promise<User> {
|
||||||
const existingUser: User = await this.findByEmail(createUserDto.email);
|
const existingUser: User = await this.findByEmail(createUserDto.email);
|
||||||
|
|
||||||
@ -16,11 +21,20 @@ export class UserService {
|
|||||||
throw new BadRequestException('A user with this email already exists');
|
throw new BadRequestException('A user with this email already exists');
|
||||||
}
|
}
|
||||||
|
|
||||||
const user: User = plainToClass(User, createUserDto);
|
let user: User = plainToClass(User, createUserDto);
|
||||||
user.locale = 'en';
|
user.locale = 'en';
|
||||||
user.lastLoginAt = new Date();
|
user.lastLoginAt = new Date();
|
||||||
|
|
||||||
return this.userRepository.save(user);
|
user = await this.userRepository.save(user);
|
||||||
|
|
||||||
|
//TODO: create workspace if it is not a signup to an existing workspace
|
||||||
|
const workspaceDto: CreateWorkspaceDto = {
|
||||||
|
name: user.name, // will be better handled
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.workspaceService.create(workspaceDto, user.id);
|
||||||
|
|
||||||
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
findById(userId: string) {
|
findById(userId: string) {
|
||||||
|
|||||||
@ -0,0 +1,20 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { WorkspaceController } from './workspace.controller';
|
||||||
|
import { WorkspaceService } from '../services/workspace.service';
|
||||||
|
|
||||||
|
describe('WorkspaceController', () => {
|
||||||
|
let controller: WorkspaceController;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [WorkspaceController],
|
||||||
|
providers: [WorkspaceService],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
controller = module.get<WorkspaceController>(WorkspaceController);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(controller).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { Controller } from '@nestjs/common';
|
||||||
|
import { WorkspaceService } from '../services/workspace.service';
|
||||||
|
|
||||||
|
@Controller('workspace')
|
||||||
|
export class WorkspaceController {
|
||||||
|
constructor(private readonly workspaceService: WorkspaceService) {}
|
||||||
|
}
|
||||||
18
server/src/core/workspace/dto/create-workspace.dto.ts
Normal file
18
server/src/core/workspace/dto/create-workspace.dto.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { IsOptional, IsString, MaxLength, MinLength } from 'class-validator';
|
||||||
|
|
||||||
|
export class CreateWorkspaceDto {
|
||||||
|
@MinLength(4)
|
||||||
|
@MaxLength(64)
|
||||||
|
@IsString()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@MinLength(4)
|
||||||
|
@MaxLength(30)
|
||||||
|
@IsString()
|
||||||
|
hostname?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
4
server/src/core/workspace/dto/update-workspace.dto.ts
Normal file
4
server/src/core/workspace/dto/update-workspace.dto.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { PartialType } from '@nestjs/mapped-types';
|
||||||
|
import { CreateWorkspaceDto } from './create-workspace.dto';
|
||||||
|
|
||||||
|
export class UpdateWorkspaceDto extends PartialType(CreateWorkspaceDto) {}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
import {
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
ManyToOne,
|
||||||
|
JoinColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { Workspace } from './workspace.entity';
|
||||||
|
import { User } from '../../user/entities/user.entity';
|
||||||
|
|
||||||
|
@Entity('workspace_invitations')
|
||||||
|
export class WorkspaceInvitation {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => Workspace, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
createForeignKeyConstraints: false,
|
||||||
|
})
|
||||||
|
@JoinColumn({ name: 'workspaceId' })
|
||||||
|
workspace: Workspace;
|
||||||
|
|
||||||
|
@ManyToOne(() => User, {
|
||||||
|
onDelete: 'SET NULL',
|
||||||
|
createForeignKeyConstraints: false,
|
||||||
|
})
|
||||||
|
@JoinColumn({ name: 'invitedById' })
|
||||||
|
invitedBy: User;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 255 })
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 100, nullable: true })
|
||||||
|
role?: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 100, nullable: true })
|
||||||
|
status?: string;
|
||||||
|
|
||||||
|
@CreateDateColumn()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn()
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
48
server/src/core/workspace/entities/workspace-user.entity.ts
Normal file
48
server/src/core/workspace/entities/workspace-user.entity.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import {
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
ManyToOne,
|
||||||
|
JoinColumn,
|
||||||
|
Unique,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { Workspace } from './workspace.entity';
|
||||||
|
import { User } from '../../user/entities/user.entity';
|
||||||
|
|
||||||
|
@Entity('workspace_users')
|
||||||
|
@Unique(['workspaceId', 'userId'])
|
||||||
|
export class WorkspaceUser {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => User, (user) => user.workspaceUser, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
createForeignKeyConstraints: false,
|
||||||
|
})
|
||||||
|
@JoinColumn({ name: 'userId' })
|
||||||
|
user: User;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
userId: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => Workspace, (workspace) => workspace.workspaceUser, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
createForeignKeyConstraints: false,
|
||||||
|
})
|
||||||
|
@JoinColumn({ name: 'workspaceId' })
|
||||||
|
workspace: Workspace;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
workspaceId: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 100, nullable: true })
|
||||||
|
role?: string;
|
||||||
|
|
||||||
|
@CreateDateColumn()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn()
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
62
server/src/core/workspace/entities/workspace.entity.ts
Normal file
62
server/src/core/workspace/entities/workspace.entity.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import {
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
ManyToOne,
|
||||||
|
OneToMany,
|
||||||
|
JoinColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { User } from '../../user/entities/user.entity';
|
||||||
|
import { WorkspaceUser } from './workspace-user.entity';
|
||||||
|
|
||||||
|
@Entity('workspaces')
|
||||||
|
export class Workspace {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column({ type: 'text', nullable: true })
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
logo?: string;
|
||||||
|
|
||||||
|
@Column({ unique: true })
|
||||||
|
hostname: string;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
customDomain?: string;
|
||||||
|
|
||||||
|
@Column({ type: 'boolean', default: true })
|
||||||
|
enableInvite: boolean;
|
||||||
|
|
||||||
|
@Column({ type: 'text', unique: true, nullable: true })
|
||||||
|
inviteCode?: string;
|
||||||
|
|
||||||
|
@Column({ type: 'jsonb', nullable: true })
|
||||||
|
settings?: any;
|
||||||
|
|
||||||
|
@ManyToOne(() => User, (user) => user.workspaces, {
|
||||||
|
createForeignKeyConstraints: false,
|
||||||
|
})
|
||||||
|
@JoinColumn({ name: 'creatorId' })
|
||||||
|
creator: User;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
creatorId: string;
|
||||||
|
|
||||||
|
@CreateDateColumn()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn()
|
||||||
|
updatedAt: Date;
|
||||||
|
|
||||||
|
@OneToMany(() => WorkspaceUser, (workspaceUser) => workspaceUser.workspace, {
|
||||||
|
createForeignKeyConstraints: false,
|
||||||
|
})
|
||||||
|
workspaceUser: WorkspaceUser[];
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { DataSource, Repository } from 'typeorm';
|
||||||
|
import { WorkspaceUser } from '../entities/workspace-user.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WorkspaceUserRepository extends Repository<WorkspaceUser> {
|
||||||
|
constructor(private dataSource: DataSource) {
|
||||||
|
super(WorkspaceUser, dataSource.createEntityManager());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { DataSource, Repository } from 'typeorm';
|
||||||
|
import { Workspace } from '../entities/workspace.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WorkspaceRepository extends Repository<Workspace> {
|
||||||
|
constructor(private dataSource: DataSource) {
|
||||||
|
super(Workspace, dataSource.createEntityManager());
|
||||||
|
}
|
||||||
|
}
|
||||||
18
server/src/core/workspace/services/workspace.service.spec.ts
Normal file
18
server/src/core/workspace/services/workspace.service.spec.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { WorkspaceService } from './workspace.service';
|
||||||
|
|
||||||
|
describe('WorkspaceService', () => {
|
||||||
|
let service: WorkspaceService;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [WorkspaceService],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
service = module.get<WorkspaceService>(WorkspaceService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(service).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
49
server/src/core/workspace/services/workspace.service.ts
Normal file
49
server/src/core/workspace/services/workspace.service.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { CreateWorkspaceDto } from '../dto/create-workspace.dto';
|
||||||
|
import { WorkspaceRepository } from '../repositories/workspace.repository';
|
||||||
|
import { WorkspaceUserRepository } from '../repositories/workspace-user.repository';
|
||||||
|
import { WorkspaceUser } from '../entities/workspace-user.entity';
|
||||||
|
import { Workspace } from '../entities/workspace.entity';
|
||||||
|
import { plainToClass } from 'class-transformer';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
import { generateHostname } from '../workspace.util';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WorkspaceService {
|
||||||
|
constructor(
|
||||||
|
private workspaceRepository: WorkspaceRepository,
|
||||||
|
private workspaceUserRepository: WorkspaceUserRepository,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async create(
|
||||||
|
createWorkspaceDto: CreateWorkspaceDto,
|
||||||
|
userId: string,
|
||||||
|
): Promise<Workspace> {
|
||||||
|
let workspace: Workspace = plainToClass(Workspace, createWorkspaceDto);
|
||||||
|
|
||||||
|
workspace.inviteCode = uuid();
|
||||||
|
workspace.creatorId = userId;
|
||||||
|
|
||||||
|
if (!workspace.hostname?.trim()) {
|
||||||
|
workspace.hostname = generateHostname(createWorkspaceDto.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
workspace = await this.workspaceRepository.save(workspace);
|
||||||
|
await this.addUserToWorkspace(userId, workspace.id, 'owner');
|
||||||
|
|
||||||
|
return workspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
async addUserToWorkspace(
|
||||||
|
userId: string,
|
||||||
|
workspaceId: string,
|
||||||
|
role: string,
|
||||||
|
): Promise<WorkspaceUser> {
|
||||||
|
const workspaceUser = new WorkspaceUser();
|
||||||
|
workspaceUser.userId = userId;
|
||||||
|
workspaceUser.workspaceId = workspaceId;
|
||||||
|
workspaceUser.role = role;
|
||||||
|
|
||||||
|
return this.workspaceUserRepository.save(workspaceUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
19
server/src/core/workspace/workspace.module.ts
Normal file
19
server/src/core/workspace/workspace.module.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { WorkspaceService } from './services/workspace.service';
|
||||||
|
import { WorkspaceController } from './controllers/workspace.controller';
|
||||||
|
import { WorkspaceRepository } from './repositories/workspace.repository';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { Workspace } from './entities/workspace.entity';
|
||||||
|
import { WorkspaceUser } from './entities/workspace-user.entity';
|
||||||
|
import { WorkspaceInvitation } from './entities/workspace-invitation.entity';
|
||||||
|
import { WorkspaceUserRepository } from './repositories/workspace-user.repository';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
TypeOrmModule.forFeature([Workspace, WorkspaceUser, WorkspaceInvitation]),
|
||||||
|
],
|
||||||
|
controllers: [WorkspaceController],
|
||||||
|
providers: [WorkspaceService, WorkspaceRepository, WorkspaceUserRepository],
|
||||||
|
exports: [WorkspaceService, WorkspaceRepository, WorkspaceUserRepository],
|
||||||
|
})
|
||||||
|
export class WorkspaceModule {}
|
||||||
5
server/src/core/workspace/workspace.util.ts
Normal file
5
server/src/core/workspace/workspace.util.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export function generateHostname(name: string): string {
|
||||||
|
let hostname = name.replace(/[^a-z0-9]/gi, '').toLowerCase();
|
||||||
|
hostname = hostname.substring(0, 30);
|
||||||
|
return hostname;
|
||||||
|
}
|
||||||
3
server/src/database/migrations/.gitignore
vendored
Normal file
3
server/src/database/migrations/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# don't include frequently changing migrations yet
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
@ -1,15 +0,0 @@
|
|||||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
|
||||||
|
|
||||||
export class CreateUserTable1691158956520 implements MigrationInterface {
|
|
||||||
name = 'CreateUserTable1691158956520';
|
|
||||||
|
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
||||||
await queryRunner.query(
|
|
||||||
`CREATE TABLE "users" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "email" character varying NOT NULL, "emailVerifiedAt" TIMESTAMP, "password" character varying NOT NULL, "avatar_url" character varying, "locale" character varying, "timezone" character varying, "settings" jsonb, "lastLoginAt" TIMESTAMP, "lastLoginIp" character varying, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3" UNIQUE ("email"), CONSTRAINT "PK_a3ffb1c0c8416b9fc6f907b7433" PRIMARY KEY ("id"))`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
||||||
await queryRunner.query(`DROP TABLE "users"`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -9,6 +9,6 @@ export const AppDataSource: DataSource = new DataSource({
|
|||||||
entities: ['src/**/*.entity.{ts,js}'],
|
entities: ['src/**/*.entity.{ts,js}'],
|
||||||
migrations: ['src/**/migrations/*.{ts,js}'],
|
migrations: ['src/**/migrations/*.{ts,js}'],
|
||||||
subscribers: [],
|
subscribers: [],
|
||||||
synchronize: process.env.NODE_ENV === 'development',
|
synchronize: false,
|
||||||
logging: process.env.NODE_ENV === 'development',
|
logging: process.env.NODE_ENV === 'development',
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user