chore: initial commit

This commit is contained in:
Divlo
2021-10-24 04:06:16 +02:00
commit 714cc643ba
260 changed files with 40783 additions and 0 deletions

View File

@ -0,0 +1,48 @@
/users/signup:
post:
tags:
- 'users'
summary: 'Signup the user'
description: 'Allows a new user to signup, if success he would need to confirm his email.'
requestBody:
content:
application/json:
schema:
type: 'object'
properties:
email:
type: 'string'
format: 'email'
name:
type: 'string'
minLength: 3
maxLength: 30
example: 'user'
password:
type: 'string'
format: 'password'
example: 'password'
language:
allOf:
- $ref: '#/definitions/Language'
theme:
allOf:
- $ref: '#/definitions/Theme'
required:
- 'email'
- 'name'
- 'password'
parameters:
- name: 'redirectURI'
description: 'The redirect URI to redirect the user when he successfuly confirm his email (could be a signin page), if not provided it will redirect the user to a simple page with a message to tell the user he can now signin.'
in: 'query'
required: false
responses:
allOf:
- $ref: '#/definitions/BadRequestError'
- '201':
description: 'User created and send an email to confirm the account'
content:
application/json:
schema:
$ref: '#/definitions/User'

View File

@ -0,0 +1,124 @@
import request from 'supertest'
import { formatErrors } from '../../../../__test__/utils/formatErrors'
import application from '../../../../application'
import User from '../../../../models/User'
import { commonErrorsMessages } from '../../../../tools/configurations/constants'
import { errorsMessages } from '../post'
describe('POST /users/signup', () => {
it('succeeds and create a new user', async () => {
let users = await User.findAll()
expect(users.length).toEqual(0)
await request(application)
.post('/users/signup')
.send({
name: 'John',
email: 'contact@test.com',
password: 'test'
})
.expect(201)
users = await User.findAll()
expect(users.length).toEqual(1)
})
it('fails with invalid email', async () => {
let users = await User.findAll()
expect(users.length).toEqual(0)
const response = await request(application)
.post('/users/signup')
.send({
name: 'Divlo',
email: 'incorrect@email',
password: 'test'
})
.expect(400)
expect(response.body.errors.length).toEqual(1)
expect(response.body.errors[0].message).toBe(
errorsMessages.email.mustBeValid
)
users = await User.findAll()
expect(users.length).toEqual(0)
})
it('fails with invalid name', async () => {
let users = await User.findAll()
expect(users.length).toEqual(0)
const response = await request(application)
.post('/users/signup')
.send({
name: 'jo',
email: 'contact@email.com',
password: 'test'
})
.expect(400)
expect(response.body.errors.length).toEqual(1)
expect(response.body.errors[0].message).toBe(
commonErrorsMessages.charactersLength('name', { max: 30, min: 3 })
)
users = await User.findAll()
expect(users.length).toEqual(0)
})
it('fails with invalid name and invalid email', async () => {
let users = await User.findAll()
expect(users.length).toEqual(0)
const response = await request(application)
.post('/users/signup')
.send({
name: 'jo',
email: 'contact@email',
password: 'test'
})
.expect(400)
const errors = formatErrors(response.body.errors)
expect(errors.length).toEqual(2)
expect(errors).toEqual(
expect.arrayContaining([
commonErrorsMessages.charactersLength('name', { max: 30, min: 3 }),
errorsMessages.email.mustBeValid
])
)
users = await User.findAll()
expect(users.length).toEqual(0)
})
it('fails with name and email already used', async () => {
const name = 'John'
const email = 'contact@test.com'
await request(application)
.post('/users/signup')
.send({
name,
email,
password: 'test'
})
.expect(201)
const response = await request(application)
.post('/users/signup')
.send({
name,
email,
password: 'test'
})
.expect(400)
const errors = formatErrors(response.body.errors)
expect(errors.length).toEqual(2)
expect(errors).toEqual(
expect.arrayContaining(['Name already used', 'Email already used'])
)
})
})

View File

@ -0,0 +1,102 @@
import bcrypt from 'bcryptjs'
import { Request, Response, Router } from 'express'
import { body, query } from 'express-validator'
import { v4 as uuidv4 } from 'uuid'
import { validateRequest } from '../../../tools/middlewares/validateRequest'
import User from '../../../models/User'
import UserSetting, {
Language,
languages,
Theme,
themes
} from '../../../models/UserSetting'
import { commonErrorsMessages } from '../../../tools/configurations/constants'
import { sendEmail } from '../../../tools/email/sendEmail'
import { alreadyUsedValidation } from '../../../tools/validations/alreadyUsedValidation'
import { onlyPossibleValuesValidation } from '../../../tools/validations/onlyPossibleValuesValidation'
export const errorsMessages = {
email: {
mustBeValid: 'Email must be valid'
}
}
export const signupRouter = Router()
signupRouter.post(
'/users/signup',
[
body('email')
.trim()
.notEmpty()
.isEmail()
.withMessage(errorsMessages.email.mustBeValid)
.custom(async (email: string) => {
return await alreadyUsedValidation(User, 'email', email)
}),
body('name')
.trim()
.notEmpty()
.isLength({ max: 30, min: 3 })
.withMessage(
commonErrorsMessages.charactersLength('name', { max: 30, min: 3 })
)
.custom(async (name: string) => {
return await alreadyUsedValidation(User, 'name', name)
}),
body('password').trim().notEmpty().isString(),
body('theme')
.optional({ nullable: true })
.trim()
.isString()
.custom(async (theme: Theme) => {
return await onlyPossibleValuesValidation([...themes], 'theme', theme)
}),
body('language')
.optional({ nullable: true })
.trim()
.isString()
.custom(async (language: Language) => {
return await onlyPossibleValuesValidation(
languages,
'language',
language
)
}),
query('redirectURI').optional({ nullable: true }).trim()
],
validateRequest,
async (req: Request, res: Response) => {
const { name, email, password, theme, language } = req.body as {
name: string
email: string
password: string
theme?: Theme
language?: Language
}
const { redirectURI } = req.query as { redirectURI?: string }
const hashedPassword = await bcrypt.hash(password, 12)
const tempToken = uuidv4()
const user = await User.create({
email,
name,
password: hashedPassword,
tempToken
})
const userSettings = await UserSetting.create({
userId: user.id,
theme: theme ?? 'dark',
language: language ?? 'en'
})
const redirectQuery = redirectURI != null ? `&redirectURI=${redirectURI}` : ''
await sendEmail({
type: 'confirm-email',
email,
url: `${process.env.API_BASE_URL}/users/confirmEmail?tempToken=${tempToken}${redirectQuery}`,
language: userSettings.language,
theme: userSettings.theme
})
return res.status(201).json({ user })
}
)