Skip to content

Commit

Permalink
Merge pull request #30 from BinaryStudioAcademy/task/OV-3-add-sign-up…
Browse files Browse the repository at this point in the history
…-flow

OV-3: add sign up flow
  • Loading branch information
nikita-remeslov authored Aug 28, 2024
2 parents 90a4422 + b8c0ee2 commit 46bf15d
Show file tree
Hide file tree
Showing 13 changed files with 70 additions and 9 deletions.
9 changes: 9 additions & 0 deletions backend/src/bundles/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ class AuthController extends BaseController {
* format: email
* password:
* type: string
* fullName:
* type: string
* responses:
* 201:
* description: Successful operation
Expand All @@ -131,6 +133,13 @@ class AuthController extends BaseController {
* message:
* type: object
* $ref: '#/components/schemas/User'
* 400:
* description: Failed operation
* content:
* application/json:
* schema:
* type: object
* $ref: '#/components/schemas/Error'
*/

private async signUp(
Expand Down
10 changes: 9 additions & 1 deletion backend/src/bundles/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,17 @@ class AuthService {
return user.toObject();
}

public signUp(
public async signUp(
userRequestDto: UserSignUpRequestDto,
): Promise<UserSignUpResponseDto> {
const { email } = userRequestDto;
const emailExists = await this.userService.findByEmail(email);
if (emailExists) {
throw new HttpError({
message: UserValidationMessage.EMAIL_ALREADY_EXISTS,
status: HttpCode.BAD_REQUEST,
});
}
return this.userService.create(userRequestDto);
}
}
Expand Down
15 changes: 15 additions & 0 deletions backend/src/bundles/users/user.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,58 +5,69 @@ class UserEntity implements Entity {

private 'email': string;

private 'fullName': string;

private 'passwordHash': string;

private 'passwordSalt': string;

private constructor({
id,
email,
fullName,
passwordHash,
passwordSalt,
}: {
id: number | null;
email: string;
fullName: string;
passwordHash: string;
passwordSalt: string;
}) {
this.id = id;
this.email = email;
this.fullName = fullName;
this.passwordHash = passwordHash;
this.passwordSalt = passwordSalt;
}

public static initialize({
id,
email,
fullName,
passwordHash,
passwordSalt,
}: {
id: number;
email: string;
fullName: string;
passwordHash: string;
passwordSalt: string;
}): UserEntity {
return new UserEntity({
id,
email,
fullName,
passwordHash,
passwordSalt,
});
}

public static initializeNew({
email,
fullName,
passwordHash,
passwordSalt,
}: {
email: string;
fullName: string;
passwordHash: string;
passwordSalt: string;
}): UserEntity {
return new UserEntity({
id: null,
email,
fullName,
passwordHash,
passwordSalt,
});
Expand All @@ -65,20 +76,24 @@ class UserEntity implements Entity {
public toObject(): {
id: number;
email: string;
fullName: string;
} {
return {
id: this.id as number,
email: this.email,
fullName: this.fullName,
};
}

public toNewObject(): {
email: string;
fullName: string;
passwordHash: string;
passwordSalt: string;
} {
return {
email: this.email,
fullName: this.fullName,
passwordHash: this.passwordHash,
passwordSalt: this.passwordSalt,
};
Expand Down
2 changes: 2 additions & 0 deletions backend/src/bundles/users/user.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
class UserModel extends AbstractModel {
public 'email': string;

public 'fullName': string;

public 'passwordHash': string;

public 'passwordSalt': string;
Expand Down
4 changes: 3 additions & 1 deletion backend/src/bundles/users/user.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ class UserRepository implements Repository {
}

public async create(entity: UserEntity): Promise<UserEntity> {
const { email, passwordSalt, passwordHash } = entity.toNewObject();
const { email, fullName, passwordSalt, passwordHash } =
entity.toNewObject();

const item = await this.userModel
.query()
.insert({
email,
fullName,
passwordSalt,
passwordHash,
})
Expand Down
5 changes: 3 additions & 2 deletions backend/src/bundles/users/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ class UserService implements Service {
const user = await this.userRepository.create(
UserEntity.initializeNew({
email: payload.email,
passwordSalt: salt, // TODO
passwordHash: hash, // TODO
fullName: payload.fullName,
passwordSalt: salt,
passwordHash: hash,
}),
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { type Knex } from 'knex';

const TABLE_NAME = 'users';

async function up(knex: Knex): Promise<void> {
await knex.schema.table(TABLE_NAME, (table) => {
table.string('full_name').notNullable();
});
}

async function down(knex: Knex): Promise<void> {
await knex.schema.table(TABLE_NAME, (table) => {
table.dropColumn('full_name');
});
}

export { down, up };
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type UserSignUpRequestDto } from '~/bundles/users/users.js';

const DEFAULT_SIGN_UP_PAYLOAD: UserSignUpRequestDto = {
name: '',
fullName: '',
email: '',
password: '',
confirmPassword: '',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const SignUpForm: React.FC<Properties> = ({ onSubmit }) => {
type="text"
label="Full Name"
placeholder="Name"
name="name"
name="fullName"
required
/>
<Input
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ const UserValidationMessage = {
EMAIL_WRONG: 'Email is wrong',
FIELD_REQUIRE: 'Please fill out this field',
EMAIL_INVALID: 'Please enter a valid email',
EMAIL_ALREADY_EXISTS: 'Email already exists',
PASSWORD_LENGTH: 'Password must have from 6 to 12 characters',
PASS_DONT_MATCH: 'Passwords must be identical',
INVALID_DATA: 'Incorrect email or password. Please try again.',
WRONG_CREDENTIALS: 'Email or password are incorrect',
USER_IS_NOT_AVAILABLE:
'User with this email already exists. Log in if it is you',
FULL_NAME_INVALID: 'Name must have at least two words',
} as const;

export { UserValidationMessage };
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
type UserSignUpRequestDto = {
name: string;
fullName: string;
email: string;
password: string;
confirmPassword: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
type UserSignUpResponseDto = {
id: number;
fullName: string;
email: string;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { z } from 'zod';
import { UserValidationMessage, UserValidationRule } from '../enums/enums.js';

type UserSignUpRequestValidationDto = {
name: z.ZodString;
fullName: z.ZodString;
email: z.ZodString;
password: z.ZodString;
confirmPassword: z.ZodString;
};

const userSignUp = z
.object<UserSignUpRequestValidationDto>({
name: z
fullName: z
.string({ required_error: UserValidationMessage.FIELD_REQUIRE })
.trim(),
email: z
Expand Down Expand Up @@ -46,6 +46,10 @@ const userSignUp = z
}),
})
.required()
.refine((data) => data.fullName.split(/\s+/).length >= 2, {
message: UserValidationMessage.FULL_NAME_INVALID,
path: ['name'],
})
.refine((data) => data.password === data.confirmPassword, {
message: UserValidationMessage.PASS_DONT_MATCH,
path: ['confirmPassword'],
Expand Down

0 comments on commit 46bf15d

Please sign in to comment.