Refactoring

* replace TypeORM with Kysely query builder
* refactor migrations
* other changes and fixes
This commit is contained in:
Philipinho
2024-03-29 01:46:11 +00:00
parent cacb5606b1
commit c18c9ae02b
122 changed files with 2619 additions and 3541 deletions

View File

@ -1,8 +0,0 @@
import * as bcrypt from 'bcrypt';
export async function comparePasswordHash(
plainPassword: string,
passwordHash: string,
): Promise<boolean> {
return bcrypt.compare(plainPassword, passwordHash);
}

View File

@ -1,11 +1,12 @@
import { CanActivate, ForbiddenException, Injectable } from '@nestjs/common';
import { WorkspaceRepository } from '../../workspace/repositories/workspace.repository';
import { WorkspaceRepo } from '@docmost/db/repos/workspace/workspace.repo';
@Injectable()
export class SetupGuard implements CanActivate {
constructor(private workspaceRepository: WorkspaceRepository) {}
constructor(private workspaceRepo: WorkspaceRepo) {}
async canActivate(): Promise<boolean> {
const workspaceCount = await this.workspaceRepository.count();
const workspaceCount = await this.workspaceRepo.count();
if (workspaceCount > 0) {
throw new ForbiddenException('Workspace setup already completed.');
}

View File

@ -1,14 +1,13 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { LoginDto } from '../dto/login.dto';
import { User } from '../../user/entities/user.entity';
import { CreateUserDto } from '../dto/create-user.dto';
import { UserService } from '../../user/user.service';
import { TokenService } from './token.service';
import { TokensDto } from '../dto/tokens.dto';
import { UserRepository } from '../../user/repositories/user.repository';
import { comparePasswordHash } from '../auth.utils';
import { SignupService } from './signup.service';
import { CreateAdminUserDto } from '../dto/create-admin-user.dto';
import { UserRepo } from '@docmost/db/repos/user/user.repo';
import { comparePasswordHash } from '../../../helpers/utils';
@Injectable()
export class AuthService {
@ -16,14 +15,11 @@ export class AuthService {
private userService: UserService,
private signupService: SignupService,
private tokenService: TokenService,
private userRepository: UserRepository,
private userRepo: UserRepo,
) {}
async login(loginDto: LoginDto, workspaceId: string) {
const user = await this.userRepository.findOneByEmail(
loginDto.email,
workspaceId,
);
const user = await this.userRepo.findByEmail(loginDto.email, workspaceId);
if (
!user ||
@ -33,17 +29,14 @@ export class AuthService {
}
user.lastLoginAt = new Date();
await this.userRepository.save(user);
await this.userRepo.updateLastLogin(user.id, workspaceId);
const tokens: TokensDto = await this.tokenService.generateTokens(user);
return { tokens };
}
async register(createUserDto: CreateUserDto, workspaceId: string) {
const user: User = await this.signupService.signup(
createUserDto,
workspaceId,
);
const user = await this.signupService.signup(createUserDto, workspaceId);
const tokens: TokensDto = await this.tokenService.generateTokens(user);
@ -51,8 +44,7 @@ export class AuthService {
}
async setup(createAdminUserDto: CreateAdminUserDto) {
const user: User =
await this.signupService.initialSetup(createAdminUserDto);
const user = await this.signupService.initialSetup(createAdminUserDto);
const tokens: TokensDto = await this.tokenService.generateTokens(user);

View File

@ -1,140 +1,95 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { CreateUserDto } from '../dto/create-user.dto';
import { DataSource, EntityManager } from 'typeorm';
import { User } from '../../user/entities/user.entity';
import { transactionWrapper } from '../../../helpers/db.helper';
import { UserRepository } from '../../user/repositories/user.repository';
import { WorkspaceRepository } from '../../workspace/repositories/workspace.repository';
import { WorkspaceService } from '../../workspace/services/workspace.service';
import { CreateWorkspaceDto } from '../../workspace/dto/create-workspace.dto';
import { Workspace } from '../../workspace/entities/workspace.entity';
import { SpaceService } from '../../space/services/space.service';
import { CreateAdminUserDto } from '../dto/create-admin-user.dto';
import { GroupUserService } from '../../group/services/group-user.service';
import { UserRepo } from '@docmost/db/repos/user/user.repo';
import { KyselyDB, KyselyTransaction } from '@docmost/db/types/kysely.types';
import { executeTx } from '@docmost/db/utils';
import { InjectKysely } from 'nestjs-kysely';
import { User } from '@docmost/db/types/entity.types';
@Injectable()
export class SignupService {
constructor(
private userRepository: UserRepository,
private workspaceRepository: WorkspaceRepository,
private userRepo: UserRepo,
private workspaceService: WorkspaceService,
private spaceService: SpaceService,
private groupUserService: GroupUserService,
private dataSource: DataSource,
@InjectKysely() private readonly db: KyselyDB,
) {}
prepareUser(createUserDto: CreateUserDto): User {
const user = new User();
user.name = createUserDto.name || createUserDto.email.split('@')[0];
user.email = createUserDto.email.toLowerCase();
user.password = createUserDto.password;
user.locale = 'en';
user.lastLoginAt = new Date();
return user;
}
async createUser(
createUserDto: CreateUserDto,
manager?: EntityManager,
): Promise<User> {
return await transactionWrapper(
async (transactionManager: EntityManager) => {
let user = this.prepareUser(createUserDto);
user = await transactionManager.save(user);
return user;
},
this.dataSource,
manager,
);
}
async signup(
createUserDto: CreateUserDto,
workspaceId: string,
manager?: EntityManager,
trx?: KyselyTransaction,
): Promise<User> {
const userCheck = await this.userRepository.findOneByEmail(
const userCheck = await this.userRepo.findByEmail(
createUserDto.email,
workspaceId,
);
if (userCheck) {
throw new BadRequestException(
'You already have an account on this workspace',
);
}
return await transactionWrapper(
async (manager: EntityManager) => {
return await executeTx(
this.db,
async (trx) => {
// create user
const user = await this.createUser(createUserDto, manager);
const user = await this.userRepo.insertUser(createUserDto, trx);
// add user to workspace
await this.workspaceService.addUserToWorkspace(
user,
user.id,
workspaceId,
undefined,
manager,
trx,
);
// add user to default group
await this.groupUserService.addUserToDefaultGroup(
user.id,
workspaceId,
manager,
trx,
);
return user;
},
this.dataSource,
manager,
trx,
);
}
async createWorkspace(
user: User,
workspaceName,
manager?: EntityManager,
): Promise<Workspace> {
return await transactionWrapper(
async (manager: EntityManager) => {
// for cloud
async createWorkspace(user, workspaceName, trx?: KyselyTransaction) {
return await executeTx(
this.db,
async (trx) => {
const workspaceData: CreateWorkspaceDto = {
name: workspaceName,
// hostname: '', // generate
};
return await this.workspaceService.create(user, workspaceData, manager);
return await this.workspaceService.create(user, workspaceData, trx);
},
this.dataSource,
manager,
trx,
);
}
async initialSetup(
createAdminUserDto: CreateAdminUserDto,
manager?: EntityManager,
): Promise<User> {
return await transactionWrapper(
async (manager: EntityManager) => {
trx?: KyselyTransaction,
) {
return await executeTx(
this.db,
async (trx) => {
// create user
const user = await this.createUser(createAdminUserDto, manager);
await this.createWorkspace(
user,
createAdminUserDto.workspaceName,
manager,
);
const user = await this.userRepo.insertUser(createAdminUserDto, trx);
await this.createWorkspace(user, createAdminUserDto.workspaceName, trx);
return user;
},
this.dataSource,
manager,
trx,
);
}
}
// create user -
// create workspace -
// create default group
// create space
// add group to space instead of user
// add new users to default group

View File

@ -1,9 +1,9 @@
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { EnvironmentService } from '../../../integrations/environment/environment.service';
import { User } from '../../user/entities/user.entity';
import { TokensDto } from '../dto/tokens.dto';
import { JwtPayload, JwtRefreshPayload, JwtType } from '../dto/jwt-payload';
import { User } from '@docmost/db/types/entity.types';
@Injectable()
export class TokenService {
@ -32,7 +32,7 @@ export class TokenService {
return this.jwtService.sign(payload, { expiresIn });
}
async generateTokens(user: User): Promise<TokensDto> {
async generateTokens(user): Promise<TokensDto> {
return {
accessToken: await this.generateAccessToken(user),
refreshToken: await this.generateRefreshToken(user.id, user.workspaceId),

View File

@ -7,18 +7,14 @@ import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { EnvironmentService } from '../../../integrations/environment/environment.service';
import { JwtPayload, JwtType } from '../dto/jwt-payload';
import { AuthService } from '../services/auth.service';
import { UserRepository } from '../../user/repositories/user.repository';
import { UserService } from '../../user/user.service';
import { WorkspaceRepository } from '../../workspace/repositories/workspace.repository';
import { WorkspaceRepo } from '@docmost/db/repos/workspace/workspace.repo';
import { UserRepo } from '@docmost/db/repos/user/user.repo';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
constructor(
private authService: AuthService,
private userService: UserService,
private userRepository: UserRepository,
private workspaceRepository: WorkspaceRepository,
private userRepo: UserRepo,
private workspaceRepo: WorkspaceRepo,
private readonly environmentService: EnvironmentService,
) {
super({
@ -29,7 +25,11 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
});
}
async validate(req, payload: JwtPayload) {
async validate(req: any, payload: JwtPayload) {
if (!payload.workspaceId || payload.type !== JwtType.ACCESS) {
throw new UnauthorizedException();
}
// CLOUD ENV
if (this.environmentService.isCloud()) {
if (req.raw.workspaceId && req.raw.workspaceId !== payload.workspaceId) {
@ -37,23 +37,12 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
}
}
if (!payload.workspaceId || payload.type !== JwtType.ACCESS) {
throw new UnauthorizedException();
}
const workspace = await this.workspaceRepository.findById(
payload.workspaceId,
);
const workspace = await this.workspaceRepo.findById(payload.workspaceId);
if (!workspace) {
throw new UnauthorizedException();
}
const user = await this.userRepository.findOne({
where: {
id: payload.sub,
workspaceId: payload.workspaceId,
},
});
const user = await this.userRepo.findById(payload.sub, payload.workspaceId);
if (!user) {
throw new UnauthorizedException();