Refactoring

* Refactor workspace membership system
* Create setup endpoint
* Use Passport.js
* Several updates and fixes
This commit is contained in:
Philipinho
2024-03-16 22:58:12 +00:00
parent b42fe48e9b
commit a821e37028
87 changed files with 2703 additions and 2307 deletions

View File

@ -1,38 +1,57 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { LoginDto } from '../dto/login.dto';
import { User } from '../../user/entities/user.entity';
import { CreateUserDto } from '../../user/dto/create-user.dto';
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';
@Injectable()
export class AuthService {
constructor(
private userService: UserService,
private signupService: SignupService,
private tokenService: TokenService,
private userRepository: UserRepository,
) {}
async login(loginDto: LoginDto) {
const user: User = await this.userService.findByEmail(loginDto.email);
const invalidCredentialsMessage = 'email or password does not match';
async login(loginDto: LoginDto, workspaceId: string) {
const user = await this.userRepository.findOneByEmail(
loginDto.email,
workspaceId,
);
if (
!user ||
!(await this.userService.compareHash(loginDto.password, user.password))
!(await comparePasswordHash(loginDto.password, user.password))
) {
throw new UnauthorizedException(invalidCredentialsMessage);
throw new UnauthorizedException('email or password does not match');
}
user.lastLoginAt = new Date();
await this.userRepository.save(user);
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 tokens: TokensDto = await this.tokenService.generateTokens(user);
return { tokens };
}
async register(createUserDto: CreateUserDto) {
const user: User = await this.userService.create(createUserDto);
async setup(createAdminUserDto: CreateAdminUserDto) {
const user: User = await this.signupService.firstSetup(createAdminUserDto);
const tokens: TokensDto = await this.tokenService.generateTokens(user);

View File

@ -0,0 +1,121 @@
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/space.service';
import { CreateAdminUserDto } from '../dto/create-admin-user.dto';
@Injectable()
export class SignupService {
constructor(
private userRepository: UserRepository,
private workspaceRepository: WorkspaceRepository,
private workspaceService: WorkspaceService,
private spaceService: SpaceService,
private dataSource: DataSource,
) {}
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,
): Promise<User> {
const userCheck = await this.userRepository.findOneByEmail(
createUserDto.email,
workspaceId,
);
if (userCheck) {
throw new BadRequestException('You have an account on this workspace');
}
return await transactionWrapper(
async (manager: EntityManager) => {
// create user
const user = await this.createUser(createUserDto, manager);
// add user to workspace
await this.workspaceService.addUserToWorkspace(
user,
workspaceId,
undefined,
manager,
);
return user;
},
this.dataSource,
manager,
);
}
async createWorkspace(
user: User,
workspaceName,
manager?: EntityManager,
): Promise<Workspace> {
return await transactionWrapper(
async (manager: EntityManager) => {
// for cloud
const workspaceData: CreateWorkspaceDto = {
name: workspaceName,
// hostname: '', // generate
};
return await this.workspaceService.create(user, workspaceData, manager);
},
this.dataSource,
manager,
);
}
async firstSetup(
createAdminUserDto: CreateAdminUserDto,
manager?: EntityManager,
): Promise<User> {
return await transactionWrapper(
async (manager: EntityManager) => {
// create user
const user = await this.createUser(createAdminUserDto, manager);
await this.createWorkspace(
user,
createAdminUserDto.workspaceName,
manager,
);
return user;
},
this.dataSource,
manager,
);
}
}

View File

@ -2,10 +2,8 @@ import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { EnvironmentService } from '../../../environment/environment.service';
import { User } from '../../user/entities/user.entity';
import { FastifyRequest } from 'fastify';
import { TokensDto } from '../dto/tokens.dto';
export type JwtPayload = { sub: string; email: string };
import { JwtPayload, JwtRefreshPayload, JwtType } from '../dto/jwt-payload';
@Injectable()
export class TokenService {
@ -13,31 +11,37 @@ export class TokenService {
private jwtService: JwtService,
private environmentService: EnvironmentService,
) {}
async generateJwt(user: User): Promise<string> {
async generateAccessToken(user: User): Promise<string> {
const payload: JwtPayload = {
sub: user.id,
email: user.email,
workspaceId: user.workspaceId,
type: JwtType.ACCESS,
};
return await this.jwtService.signAsync(payload);
return this.jwtService.sign(payload);
}
async generateRefreshToken(userId: string, workspaceId): Promise<string> {
const payload: JwtRefreshPayload = {
sub: userId,
workspaceId,
type: JwtType.REFRESH,
};
const expiresIn = '30d'; // todo: fix
return this.jwtService.sign(payload, { expiresIn });
}
async generateTokens(user: User): Promise<TokensDto> {
return {
accessToken: await this.generateJwt(user),
refreshToken: null,
accessToken: await this.generateAccessToken(user),
refreshToken: await this.generateRefreshToken(user.id, user.workspaceId),
};
}
async verifyJwt(token: string) {
return await this.jwtService.verifyAsync(token, {
return this.jwtService.verifyAsync(token, {
secret: this.environmentService.getJwtSecret(),
});
}
async extractTokenFromHeader(
request: FastifyRequest,
): Promise<string | undefined> {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
}