Skip to content

Commit

Permalink
handle register new user
Browse files Browse the repository at this point in the history
  • Loading branch information
leephan2k1 committed Jul 20, 2024
1 parent 3731d2d commit 3a249db
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 4 deletions.
3 changes: 2 additions & 1 deletion src/common/dtos/user-dtos/user.dto.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { AutoMap } from '@automapper/classes';
import { Field, ObjectType } from '@nestjs/graphql';
import { IsEmail } from 'class-validator';
import { BaseDto } from '../base-dtos/base.dto';

@ObjectType()
export class UserDto {
export class UserDto extends BaseDto {
@AutoMap()
@Field({ nullable: true })
userName?: string;
Expand Down
14 changes: 13 additions & 1 deletion src/contracts/services/auth-service.interface.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
export interface IAuthService {}
import { AuthUserResponse } from '~/graphql/types/dtos/authentication/auth-user-response.dto';
import { CredentialsTakenError } from '~/graphql/types/dtos/authentication/credentials-taken-error.dto';
import { RegisterUserInput } from '~/graphql/types/dtos/authentication/register-user.input';
import { User } from '~/models/user.model';
import { Either } from '~/utils/tools/either';

export interface IAuthService {
registerUser(
user: RegisterUserInput,
): Promise<Either<CredentialsTakenError, User>>;

signTokens(user: User): Promise<AuthUserResponse>;
}

export const IAuthService = Symbol('IAuthService');
4 changes: 4 additions & 0 deletions src/contracts/services/user-service.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { User } from '~/models/user.model';

export interface IUserService {
getUserByConditions(user: Partial<User>): Promise<User | null>;

existsByCredentials(user: Pick<User, 'email' | 'userName'>): Promise<boolean>;

createUser(user: Partial<User>): Promise<Omit<User, 'password'>>;
}

export const IUserService = Symbol('IUserService');
41 changes: 41 additions & 0 deletions src/graphql/resolvers/auth.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Inject } from '@nestjs/common';
import { Args, Mutation, Resolver } from '@nestjs/graphql';
import { ValidateInput } from '~/common/decorators/validate-input.decorator';
import { IAuthService } from '~/contracts/services/auth-service.interface';
import { RegisterUserResultUnion } from '../types/dtos/authentication/register-user-response.dto';
import { AuthActions } from '../types/enums/actions.enum';
import { RegisterUserInput } from '../types/dtos/authentication/register-user.input';
import { InjectMapper } from '@automapper/nestjs';
import { Mapper } from '@automapper/core';
import { UserDto } from '~/common/dtos/user-dtos/user.dto';
import { User } from '~/models/user.model';

@Resolver()
export class AuthResolver {
constructor(
@Inject(IAuthService)
private readonly authService: IAuthService,

@InjectMapper()
private readonly mapper: Mapper,
) {}

@ValidateInput()
@Mutation(() => [RegisterUserResultUnion], {
name: AuthActions.SignUpInternal,
})
public async register(
@Args('registerUserInput') registerUserInput: RegisterUserInput,
) {
const result = await this.authService.registerUser(registerUserInput);

if (result.isError()) {
return [result.value];
}

const authUser = await this.authService.signTokens(result.value);

authUser.user = this.mapper.map(authUser.user, User, UserDto);
return [authUser];
}
}
1 change: 0 additions & 1 deletion src/models/user.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export class User extends BaseEntity {
@Column()
email: string;

@AutoMap()
@Column({ nullable: true })
@MinLength(6)
password?: string;
Expand Down
2 changes: 1 addition & 1 deletion src/modules/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Token } from 'graphql';
import { GoogleConfig, JwtConfig } from '~/configs';
import { SocialProvider } from '~/models/social-provider.model';
import { UserModule } from './user.module';
Expand All @@ -15,6 +14,7 @@ import { AuthService } from '~/services/auth.service';
import { ISocialProviderRepository } from '~/contracts/repositories/social-provider-repository.interface';
import { SocialProviderRepository } from '~/repositories/social-provider.repository';
import { AuthResolver } from '~/graphql/resolvers/auth.resolver';
import { Token } from '~/models/token.model';

const tokenRepositoryProvider: Provider = {
provide: ITokenRepository,
Expand Down
36 changes: 36 additions & 0 deletions src/services/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import { ISocialProviderRepository } from '~/contracts/repositories/social-provi
import { ITokenRepository } from '~/contracts/repositories/token-repository.interface';
import { IAuthService } from '~/contracts/services/auth-service.interface';
import { IUserService } from '~/contracts/services/user-service.interface';
import { AuthUserResponse } from '~/graphql/types/dtos/authentication/auth-user-response.dto';
import { CredentialsTakenError } from '~/graphql/types/dtos/authentication/credentials-taken-error.dto';
import { RegisterUserInput } from '~/graphql/types/dtos/authentication/register-user.input';
import { User } from '~/models/user.model';
import { either, Either } from '~/utils/tools/either';

@Injectable()
export class AuthService implements IAuthService {
Expand All @@ -24,4 +29,35 @@ export class AuthService implements IAuthService {

private readonly jwtService: JwtService,
) {}

public async registerUser(
user: RegisterUserInput,
): Promise<Either<CredentialsTakenError, User>> {
if (await this.userService.existsByCredentials(user)) {
return either.error(
new CredentialsTakenError({
providedEmail: user.email,
}),
);
}

const newUser = await this.userService.createUser(user);
return either.of(newUser);
}

public async signTokens(user: User): Promise<AuthUserResponse> {
const payload = { email: user.email, sub: user.id };
const refreshToken = await this.jwtService.signAsync(payload, {
secret: this.jwtConf.refreshSecret,
expiresIn: this.jwtConf.refreshExpiresIn,
});

await this.tokenRepository.save({ user, token: refreshToken });

return new AuthUserResponse({
user,
access_token: this.jwtService.sign(payload),
refresh_token: refreshToken,
});
}
}
19 changes: 19 additions & 0 deletions src/services/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,23 @@ export class UserService implements IUserService {

return result;
}

public async existsByCredentials(
user: Pick<User, 'email' | 'userName'>,
): Promise<boolean> {
const result = await this.usersRepository.findByCondition({
where: [{ email: user.email }, { userName: user.userName }],
});
return !!result;
}

public async createUser(
user: Partial<User>,
): Promise<Omit<User, 'password'>> {
const newUser = this.usersRepository.create(user);
await this.usersRepository.save(newUser);
const { password, ...newUserWithoutPassword } = newUser;

return newUserWithoutPassword as Omit<User, 'password'>;
}
}

0 comments on commit 3a249db

Please sign in to comment.