mirror of
https://github.com/hknsh/project-knedita.git
synced 2024-11-28 09:31:16 +00:00
Created new routes. Fixed small bug in the tests.
This commit is contained in:
parent
bb9532c265
commit
5057f58a12
20 changed files with 172 additions and 75 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"json.schemaDownload.enable": true
|
||||
}
|
|
@ -10,6 +10,9 @@ import router from './routes'
|
|||
const app = express()
|
||||
|
||||
// TODO: test socket io, emit notifications when create one.
|
||||
// TODO: test update name, email and password routes.
|
||||
// TODO: start to create the client, or a barebone to test socket io
|
||||
// TODO: see how we create authentication with socket io.
|
||||
|
||||
app.use(express.json())
|
||||
app.use(express.urlencoded({ extended: true }))
|
||||
|
|
|
@ -29,6 +29,8 @@ usersRouter.put(
|
|||
uploadFile,
|
||||
user.uploadPicture
|
||||
)
|
||||
usersRouter.put('/update', authenticated, user.update)
|
||||
usersRouter.put('/update-email', authenticated, user.updateEmail)
|
||||
usersRouter.put('/update-name', authenticated, user.updateName)
|
||||
usersRouter.put('/update-password', authenticated, user.updatePassword)
|
||||
|
||||
export default usersRouter
|
||||
|
|
|
@ -7,7 +7,9 @@ import userLikeCommentController from './like-comment'
|
|||
import userLikePostController from './like-post'
|
||||
import userSearchController from './search-user'
|
||||
import userSignupController from './signup'
|
||||
import userUpdateController from './update'
|
||||
import userUpdateEmailController from './update-email'
|
||||
import userUpdateNameController from './update-name'
|
||||
import userUpdatePasswordController from './update-password'
|
||||
import userUploadPictureController from './upload-picture'
|
||||
|
||||
const user = {
|
||||
|
@ -20,7 +22,9 @@ const user = {
|
|||
likePost: userLikePostController,
|
||||
searchUser: userSearchController,
|
||||
signup: userSignupController,
|
||||
update: userUpdateController,
|
||||
updateEmail: userUpdateEmailController,
|
||||
updateName: userUpdateNameController,
|
||||
updatePassword: userUpdatePasswordController,
|
||||
uploadPicture: userUploadPictureController
|
||||
} as const
|
||||
|
||||
|
|
|
@ -2,16 +2,16 @@ import user from 'services/users'
|
|||
import type { Request, Response } from 'express'
|
||||
import handleResponse from 'helpers/handle-response'
|
||||
|
||||
async function userUpdateController (
|
||||
async function userUpdateEmailController (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
const { email, displayName, username } = req.body
|
||||
const { email } = req.body
|
||||
const id = req.user?.id ?? ''
|
||||
|
||||
const result = await user.update({ id, email, displayName, username })
|
||||
const result = await user.updateEmail({ id, email })
|
||||
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default userUpdateController
|
||||
export default userUpdateEmailController
|
17
src/controllers/users/update-name.ts
Normal file
17
src/controllers/users/update-name.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import user from 'services/users'
|
||||
import type { Request, Response } from 'express'
|
||||
import handleResponse from 'helpers/handle-response'
|
||||
|
||||
async function userUpdateNameController (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
const { displayName, username } = req.body
|
||||
const id = req.user?.id ?? ''
|
||||
|
||||
const result = await user.updateName({ id, displayName, username })
|
||||
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default userUpdateNameController
|
17
src/controllers/users/update-password.ts
Normal file
17
src/controllers/users/update-password.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import user from 'services/users'
|
||||
import type { Request, Response } from 'express'
|
||||
import handleResponse from 'helpers/handle-response'
|
||||
|
||||
async function userUpdatePasswordController (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<void> {
|
||||
const { currentPassword, newPassword } = req.body
|
||||
const id = req.user?.id ?? ''
|
||||
|
||||
const result = await user.updatePassword(id, currentPassword, newPassword)
|
||||
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default userUpdatePasswordController
|
|
@ -7,7 +7,9 @@ import userLikeCommentService from './like-comment'
|
|||
import userLikePostService from './like-post'
|
||||
import userSearchService from './search-user'
|
||||
import userSignupService from './signup'
|
||||
import userUpdateService from './update'
|
||||
import userUpdateEmailService from './update-email'
|
||||
import userUpdateNameService from './update-name'
|
||||
import userUpdatePasswordService from './update-password'
|
||||
import userUploadPictureService from './upload-picture'
|
||||
|
||||
const user = {
|
||||
|
@ -20,7 +22,9 @@ const user = {
|
|||
likePost: userLikePostService,
|
||||
searchUser: userSearchService,
|
||||
signup: userSignupService,
|
||||
update: userUpdateService,
|
||||
updateEmail: userUpdateEmailService,
|
||||
updateName: userUpdateNameService,
|
||||
updatePassword: userUpdatePasswordService,
|
||||
uploadPicture: userUploadPictureService
|
||||
} as const
|
||||
|
||||
|
|
|
@ -21,10 +21,6 @@ async function userSignupService ({
|
|||
)
|
||||
}
|
||||
|
||||
if (password.trim().length < 8) {
|
||||
return new Error('Password too short')
|
||||
}
|
||||
|
||||
if (!usernameRegex.test(username)) {
|
||||
return new Error(
|
||||
'Username not allowed. Only alphanumerics characters (uppercase and lowercase words), underscore, dots and it must be between 5 and 15 characters'
|
||||
|
|
42
src/services/users/update-email.ts
Normal file
42
src/services/users/update-email.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import type User from 'interfaces/user'
|
||||
import prisma from 'clients/prisma-client'
|
||||
|
||||
async function userUpdateEmailService ({
|
||||
id,
|
||||
email
|
||||
}: User): Promise<Record<string, unknown> | Error> {
|
||||
const user = await prisma.user.findFirst({ where: { id } })
|
||||
|
||||
if (user === null) {
|
||||
return new Error('User not found')
|
||||
}
|
||||
|
||||
if (user.id !== id) {
|
||||
return new Error('Forbidden')
|
||||
}
|
||||
|
||||
if (email !== undefined && email.trim() !== user.email) {
|
||||
const existingUser = await prisma.user.findFirst({ where: { email } })
|
||||
if (existingUser != null && existingUser.email !== user.email) {
|
||||
return new Error('Email already in use')
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.user.update({
|
||||
where: {
|
||||
id
|
||||
},
|
||||
data: {
|
||||
email: email ?? user.email
|
||||
},
|
||||
select: {
|
||||
displayName: true,
|
||||
username: true,
|
||||
createdAt: true
|
||||
}
|
||||
})
|
||||
|
||||
return { message: 'Successfully updated user email' }
|
||||
}
|
||||
|
||||
export default userUpdateEmailService
|
|
@ -1,9 +1,8 @@
|
|||
import type User from 'interfaces/user'
|
||||
import prisma from 'clients/prisma-client'
|
||||
|
||||
async function userUpdateService ({
|
||||
async function userUpdateNameService ({
|
||||
id,
|
||||
email,
|
||||
displayName,
|
||||
username
|
||||
}: User): Promise<Record<string, unknown> | Error> {
|
||||
|
@ -13,15 +12,8 @@ async function userUpdateService ({
|
|||
return new Error('User not found')
|
||||
}
|
||||
|
||||
// Check if the provided email or username is different from the current user's data
|
||||
// if they are different, additional queries are made to check if the new email or username is already in use.
|
||||
|
||||
if (email !== undefined && email.trim() !== user.email) {
|
||||
const existingUser = await prisma.user.findFirst({ where: { email } })
|
||||
if (existingUser != null && existingUser.email !== user.email) {
|
||||
return new Error('Email already in use')
|
||||
}
|
||||
}
|
||||
// Check if the provided username is different from the current user's data
|
||||
// if different, queries are made to check if the new username is already in use.
|
||||
|
||||
if (username !== undefined && username.trim() !== user.username) {
|
||||
const existingUser = await prisma.user.findFirst({ where: { username } })
|
||||
|
@ -34,14 +26,11 @@ async function userUpdateService ({
|
|||
return new Error('Forbidden')
|
||||
}
|
||||
|
||||
// TODO: /user/change-password | /user/change-email
|
||||
|
||||
const updatedUser = await prisma.user.update({
|
||||
where: {
|
||||
id
|
||||
},
|
||||
data: {
|
||||
email: email ?? user.email,
|
||||
displayName: displayName ?? user.displayName,
|
||||
username: username ?? user.username
|
||||
},
|
||||
|
@ -55,4 +44,4 @@ async function userUpdateService ({
|
|||
return updatedUser
|
||||
}
|
||||
|
||||
export default userUpdateService
|
||||
export default userUpdateNameService
|
56
src/services/users/update-password.ts
Normal file
56
src/services/users/update-password.ts
Normal file
|
@ -0,0 +1,56 @@
|
|||
import * as bcrypt from 'bcrypt'
|
||||
import prisma from 'clients/prisma-client'
|
||||
|
||||
const passwordRegex = /^(?=.*[0-9])(?=.*[!@#$%^&*_])[a-zA-Z0-9!@#$%^&*_]{8,}$/
|
||||
|
||||
async function userUpdatePasswordService (
|
||||
id: string,
|
||||
currentPassword: string,
|
||||
newPassword: string
|
||||
): Promise<Record<string, unknown> | Error> {
|
||||
if (!passwordRegex.test(newPassword)) {
|
||||
return new Error(
|
||||
'New password must have at least 8 characters, one number and one special character.'
|
||||
)
|
||||
}
|
||||
|
||||
const user = await prisma.user.findFirst({ where: { id } })
|
||||
|
||||
if (user === null) {
|
||||
return new Error('User not found')
|
||||
}
|
||||
|
||||
if (user.id !== id) {
|
||||
return new Error('Forbidden')
|
||||
}
|
||||
|
||||
const validPassword = await bcrypt.compare(
|
||||
currentPassword.replace(/ /g, ''),
|
||||
user.password
|
||||
)
|
||||
|
||||
if (!validPassword) {
|
||||
return new Error('Invalid password')
|
||||
}
|
||||
|
||||
const salt = await bcrypt.genSalt(15)
|
||||
const hashedPassword = await bcrypt.hash(newPassword.replace(/ /g, ''), salt)
|
||||
|
||||
await prisma.user.update({
|
||||
where: {
|
||||
id
|
||||
},
|
||||
data: {
|
||||
password: hashedPassword
|
||||
},
|
||||
select: {
|
||||
displayName: true,
|
||||
username: true,
|
||||
createdAt: true
|
||||
}
|
||||
})
|
||||
|
||||
return { message: 'Successfully updated user password' }
|
||||
}
|
||||
|
||||
export default userUpdatePasswordService
|
|
@ -12,7 +12,8 @@ describe('POST /post/create', () => {
|
|||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteUser(user.username ?? '')
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await deleteUser(user.username!)
|
||||
})
|
||||
|
||||
it('should respond with 200 status code if the user send the token and the content', async () => {
|
||||
|
|
|
@ -12,7 +12,8 @@ describe('DELETE /post/delete', () => {
|
|||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteUser(user.username ?? '')
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await deleteUser(user.username!)
|
||||
})
|
||||
|
||||
it('should delete the post successfully', async () => {
|
||||
|
|
|
@ -26,7 +26,8 @@ describe('POST /post/info', () => {
|
|||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteUser(user.username ?? '')
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await deleteUser(user.username!)
|
||||
})
|
||||
|
||||
it('should respond with 200 status code and return some info about the post', async () => {
|
||||
|
|
|
@ -12,7 +12,8 @@ describe('PUT /post/update', () => {
|
|||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteUser(user.username ?? '')
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await deleteUser(user.username!)
|
||||
})
|
||||
|
||||
it('should create a new post and update the content of it', async () => {
|
||||
|
|
|
@ -12,7 +12,8 @@ describe('POST /user/auth', () => {
|
|||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteUser(user.username ?? '')
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await deleteUser(user.username!)
|
||||
})
|
||||
|
||||
it('should respond with a error if the user does not exists', async () => {
|
||||
|
|
|
@ -12,7 +12,8 @@ describe('POST /user/info', () => {
|
|||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteUser(user.username ?? '')
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await deleteUser(user.username!)
|
||||
})
|
||||
|
||||
it('should respond with 200 status code and return the user data', async () => {
|
||||
|
|
|
@ -13,7 +13,8 @@ describe('POST /user/signup', () => {
|
|||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteUser(user.username ?? '')
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await deleteUser(user.username!)
|
||||
})
|
||||
|
||||
it('should respond with a 400 status code if sent any invalid data', async () => {
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
import request from 'supertest'
|
||||
import app from '../../app'
|
||||
import signUpNewUser from '../utils/create-user'
|
||||
import deleteUser from '../utils/delete-user'
|
||||
import type User from 'interfaces/user'
|
||||
|
||||
let user: User
|
||||
|
||||
describe('PUT /user/update', () => {
|
||||
beforeAll(async () => {
|
||||
user = await signUpNewUser()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteUser(user.username ?? '')
|
||||
})
|
||||
|
||||
it('should update the user successfully', async () => {
|
||||
const fieldsToUpdate = {
|
||||
displayName: 'Cookie'
|
||||
}
|
||||
|
||||
const response = await request(app)
|
||||
.put('/user/update')
|
||||
.send(fieldsToUpdate)
|
||||
.set('Authorization', `Bearer ${user.token ?? ''}`)
|
||||
.expect(200)
|
||||
|
||||
expect(response.body).toEqual(
|
||||
expect.objectContaining({
|
||||
displayName: expect.any(String),
|
||||
username: expect.any(String),
|
||||
createdAt: expect.any(String)
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
Loading…
Reference in a new issue