Skip to content
This repository has been archived by the owner on May 11, 2021. It is now read-only.

Commit

Permalink
feat(user): add sugn-up
Browse files Browse the repository at this point in the history
create user entity, db stuff and service for createing new users by http
  • Loading branch information
igorkamyshev committed Jan 28, 2019
1 parent 13818bf commit a9cd6e9
Show file tree
Hide file tree
Showing 14 changed files with 473 additions and 9 deletions.
6 changes: 5 additions & 1 deletion back/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@
"@nestjs/common": "^5.6.2",
"@nestjs/core": "^5.6.2",
"@nestjs/swagger": "^2.5.1",
"@nestjs/typeorm": "^5.2.2",
"@solid-soda/config": "^1.1.0",
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"mysql": "^2.16.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^6.3.3"
"rxjs": "^6.3.3",
"typeorm": "^0.2.12"
},
"devDependencies": {
"@types/cors": "^2.8.4",
Expand Down
3 changes: 2 additions & 1 deletion back/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'

import { ConfigModule } from './config/config.module'
import { DbModule } from './db/db.module'
import { MoneyModule } from './money/money.module'
import { UserModule } from './user/user.module'
import { UtilsModule } from './utils/utils.module'

@Module({
imports: [UserModule, MoneyModule, UtilsModule, ConfigModule],
imports: [UserModule, MoneyModule, UtilsModule, ConfigModule, DbModule],
})
export class AppModule implements NestModule {
public configure(consumer: MiddlewareConsumer) {
Expand Down
23 changes: 23 additions & 0 deletions back/src/db/DbOptionsFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Inject } from '@nestjs/common'
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm'

import { Configuration } from '@back/config/Configuration'

export class DbOptionsFactory implements TypeOrmOptionsFactory {
public constructor(
@Inject(Configuration) private readonly config: Configuration,
) {}

public createTypeOrmOptions(): TypeOrmModuleOptions {
return {
type: 'mysql',
host: this.config.getStringOrElse('DB_HOST', '127.0.0.1'),
port: this.config.getNumberOrElse('DB_PORT', 3306),
username: this.config.getStringOrElse('DB_USER', 'admin'),
password: this.config.getStringOrElse('DB_PASSWORD', 'admin'),
database: this.config.getStringOrElse('DB_NAME', 'oncohelp'),
entities: [__dirname + `/../**/*.{entity,vo}.{ts,js}`],
synchronize: !this.config.getBooleanOrElse('PRODUCTION_READY', true),
}
}
}
14 changes: 14 additions & 0 deletions back/src/db/EntitySaver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Injectable } from '@nestjs/common'
import { InjectEntityManager } from '@nestjs/typeorm'
import { EntityManager } from 'typeorm'

@Injectable()
export class EntitySaver {
public constructor(
@InjectEntityManager() private readonly em: EntityManager,
) {}

public async save<Entity>(...entities: Entity[]): Promise<void> {
await this.em.save(entities)
}
}
23 changes: 23 additions & 0 deletions back/src/db/db.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'
import { TypeOrmModule } from '@nestjs/typeorm'

import { ConfigModule } from '@back/config/config.module'

import { DbOptionsFactory } from './DbOptionsFactory'
import { EntitySaver } from './EntitySaver'

@Module({
imports: [
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useClass: DbOptionsFactory,
}),
],
providers: [EntitySaver],
exports: [EntitySaver],
})
export class DbModule implements NestModule {
public configure(consumer: MiddlewareConsumer) {
// pass
}
}
22 changes: 22 additions & 0 deletions back/src/user/application/Registrator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Injectable } from '@nestjs/common'

import { EntitySaver } from '@back/db/EntitySaver'

import { User } from '../domain/User.entity'
import { PasswordEncoder } from '../infrastructure/PasswordEncoder/PasswordEncoder'

@Injectable()
export class Registrator {
public constructor(
private readonly passwordEncoder: PasswordEncoder,
private readonly entitySaver: EntitySaver,
) {}

public async signUp(login: string, password: string): Promise<void> {
const user = new User(login)

await user.changePassword(password, this.passwordEncoder)

await this.entitySaver.save(user)
}
}
19 changes: 19 additions & 0 deletions back/src/user/domain/Profile.vo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export class Profile {
private name?: string

public constructor(name?: string) {
this.name = name
}

public changeName(newName: string) {
if (newName.length < 1) {
// TODO: throw error
}

this.name = newName
}

public removeName() {
this.name = undefined
}
}
36 changes: 36 additions & 0 deletions back/src/user/domain/User.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Column, Entity, PrimaryColumn } from 'typeorm'

import { PasswordEncoder } from '../infrastructure/PasswordEncoder/PasswordEncoder'
import { Profile } from './Profile.vo'

@Entity()
export class User {
@PrimaryColumn()
public readonly login: string

@Column(type => Profile)
public readonly profile: Profile

@Column()
private password: string | undefined

public constructor(login: string) {
this.login = login

this.profile = new Profile()
}

public async changePassword(
rawPassword: string,
encoder: PasswordEncoder,
): Promise<void> {
this.password = await encoder.encodePassword(rawPassword)
}

public async isPasswordValid(
rawPassword: string,
encoder: PasswordEncoder,
): Promise<boolean> {
return encoder.isPasswordValid(this.password, rawPassword)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as bcrypt from 'bcryptjs'
import { promisify } from 'util'

import { PasswordEncoder } from './PasswordEncoder'

export class BcryptPasswordEncoder implements PasswordEncoder {
private readonly getHash: (raw: string, saltRounds: number) => Promise<string>
private readonly compare: (raw: string, encoded: string) => Promise<boolean>

public constructor() {
this.getHash = promisify(bcrypt.hash)
this.compare = promisify(bcrypt.compare)
}

public encodePassword(raw: string) {
const SALT_ROUNDS = 12

return this.getHash(raw, SALT_ROUNDS)
}

public isPasswordValid(encoded: string, raw: string) {
return this.compare(raw, encoded)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export abstract class PasswordEncoder {
public abstract encodePassword(raw: string, salt?: string): Promise<string>
public abstract isPasswordValid(
encoded: string,
raw: string,
salt?: string,
): Promise<boolean>
}
7 changes: 7 additions & 0 deletions back/src/user/presentation/http/controller/AuthController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ApiUseTags,
} from '@nestjs/swagger'

import { Registrator } from '@back/user/application/Registrator'
import { PostNoCreate } from '@back/utils/presentation/http/PostNoCreate'

import { AuthRequest } from '../request/AuthRequest'
Expand All @@ -15,6 +16,8 @@ import { TokenResponse } from '../response/TokenResponse'
@Controller('user/auth')
@ApiUseTags('user')
export class AuthController {
public constructor(private readonly registrator: Registrator) {}

@PostNoCreate('sign-in')
@ApiOperation({ title: 'Sign-in by email and password' })
@ApiOkResponse({ description: 'Valid credentials', type: TokenResponse })
Expand All @@ -34,6 +37,10 @@ export class AuthController {
public async signUp(@Body() request: AuthRequest): Promise<TokenResponse> {
const { email, password } = request

await this.registrator.signUp(email, password)

// TODO: generate token by login service

return {
token: email + password,
}
Expand Down
14 changes: 14 additions & 0 deletions back/src/user/user.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'

import { DbModule } from '@back/db/db.module'

import { AuthController } from './presentation/http/controller/AuthController'
import { ProfileController } from './presentation/http/controller/ProfileController'

import { Registrator } from './application/Registrator'
import { BcryptPasswordEncoder } from './infrastructure/PasswordEncoder/BcryptPasswordEncoder'
import { PasswordEncoder } from './infrastructure/PasswordEncoder/PasswordEncoder'

@Module({
imports: [DbModule],
controllers: [AuthController, ProfileController],
providers: [
{
provide: PasswordEncoder,
useClass: BcryptPasswordEncoder,
},
Registrator,
],
})
export class UserModule implements NestModule {
public configure(consumer: MiddlewareConsumer) {
Expand Down
1 change: 1 addition & 0 deletions back/src/utils/utils.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'
import { TypeOrmModule } from '@nestjs/typeorm'

import { ParseDateRangePipe } from './presentation/http/pipes/dateRange/ParseDateRangePipe'

Expand Down
Loading

0 comments on commit a9cd6e9

Please sign in to comment.