Compare commits
2 Commits
d08c0a38fb
...
5684e19582
| Author | SHA1 | Date |
|---|---|---|
|
|
5684e19582 | |
|
|
9d189bfebd |
|
|
@ -1,6 +1,6 @@
|
||||||
import { useState } from 'react';
|
import { FormEvent, useState } from 'react';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import { getMeInfo, giveUserRights } from '@controllers/UserController';
|
import { getMeInfo, giveUserRights, changeUserInfo } from '@controllers/UserController';
|
||||||
import { toastError, toastSuccess } from '@controllers/Toasts';
|
import { toastError, toastSuccess } from '@controllers/Toasts';
|
||||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
|
@ -55,10 +55,20 @@ const User = ({ author }: any) => {
|
||||||
queryClient.invalidateQueries(['messages']);
|
queryClient.invalidateQueries(['messages']);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChange = (e: any) => {
|
const handleChangeInfo = (e: FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
toastSuccess('Infos personelles changées');
|
const data = new FormData(e.target as HTMLFormElement);
|
||||||
setShow(!show);
|
|
||||||
|
changeUserInfo(author.id, data).then((response) => {
|
||||||
|
if (response.error) {
|
||||||
|
return toastError(response.error);
|
||||||
|
}
|
||||||
|
toastSuccess('Infos personelles changées');
|
||||||
|
queryClient.invalidateQueries(['messages']);
|
||||||
|
setShow(false);
|
||||||
|
}).catch((error) => {
|
||||||
|
toastError(error.error);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -113,9 +123,13 @@ const User = ({ author }: any) => {
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="text-2xl text-white">Modifier ses infos personnelles</div>
|
<div className="text-2xl text-white">Modifier ses infos personnelles</div>
|
||||||
<form className="flex flex-col gap-2" onSubmit={handleChange}>
|
<div className="text-white">
|
||||||
|
{' '}
|
||||||
|
Tous les champs avec <span className="text-red">*</span> sont obligatoires
|
||||||
|
</div>
|
||||||
|
<form className="flex flex-col gap-2" onSubmit={handleChangeInfo}>
|
||||||
<label htmlFor="firstName" className="text-white">
|
<label htmlFor="firstName" className="text-white">
|
||||||
Prenom
|
Prenom <span className="text-red">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -123,9 +137,10 @@ const User = ({ author }: any) => {
|
||||||
id="firstName"
|
id="firstName"
|
||||||
className="rounded-lg p-2"
|
className="rounded-lg p-2"
|
||||||
defaultValue={author.firstName}
|
defaultValue={author.firstName}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
<label htmlFor="lastName" className="text-white">
|
<label htmlFor="lastName" className="text-white">
|
||||||
Nom
|
Nom <span className="text-red">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -133,17 +148,26 @@ const User = ({ author }: any) => {
|
||||||
id="lastName"
|
id="lastName"
|
||||||
className="rounded-lg p-2"
|
className="rounded-lg p-2"
|
||||||
defaultValue={author.lastName}
|
defaultValue={author.lastName}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
<label htmlFor="email" className="text-white">
|
<label htmlFor="email" className="text-white">
|
||||||
Adresse mail
|
Adresse mail (ne sera pas modifié)
|
||||||
</label>
|
</label>
|
||||||
<input type="email" name="email" id="email" className="rounded-lg p-2" defaultValue={author.email} />
|
<input
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
id="email"
|
||||||
|
className="rounded-lg p-2"
|
||||||
|
defaultValue={author.email}
|
||||||
|
required
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
<label htmlFor="password" className="text-white">
|
<label htmlFor="password" className="text-white">
|
||||||
Mot de passe
|
Mot de passe <span className="text-red">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input type="password" name="password" id="password" className="rounded-lg p-2" />
|
<input type="password" name="password" id="password" className="rounded-lg p-2" required />
|
||||||
<div>
|
<div>
|
||||||
<div className='text-white text-xl'>Changer son mot de passe:</div>
|
<div className="text-white text-xl">Changer son mot de passe:</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<label htmlFor="newPassword" className="text-white">
|
<label htmlFor="newPassword" className="text-white">
|
||||||
Nouveau mot de passe
|
Nouveau mot de passe
|
||||||
|
|
@ -156,12 +180,12 @@ const User = ({ author }: any) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
<button
|
<div
|
||||||
className="rounded-md border py-2 px-4 text-sm max-w-[100px] font-medium text-white hover:bg-white focus:outline-none focus:ring-2 focus:ring-offset-2 bg-transparent hover:text-red"
|
className="rounded-md border py-2 px-4 text-sm max-w-[100px] font-medium text-white hover:bg-white focus:outline-none focus:ring-2 focus:ring-offset-2 bg-transparent hover:text-red"
|
||||||
onClick={() => setShow(false)}
|
onClick={(e) => setShow(false)}
|
||||||
>
|
>
|
||||||
Annuler
|
Annuler
|
||||||
</button>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="rounded-md border border-red bg-red py-2 px-4 text-sm max-w-[100px] font-medium text-white hover:bg-white hover:text-red focus:outline-none focus:ring-2 focus:ring-red focus:ring-offset-2"
|
className="rounded-md border border-red bg-red py-2 px-4 text-sm max-w-[100px] font-medium text-white hover:bg-white hover:text-red focus:outline-none focus:ring-2 focus:ring-red focus:ring-offset-2"
|
||||||
|
|
|
||||||
|
|
@ -88,4 +88,58 @@ export const giveUserRights = async (userId: string, role: string) => {
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const changeUserInfo = async (userId: string, formData: FormData) => {
|
||||||
|
const token = new Cookies().get('token');
|
||||||
|
|
||||||
|
const firstName = formData.get('firstName');
|
||||||
|
const lastName = formData.get('lastName');
|
||||||
|
const password = formData.get('password');
|
||||||
|
|
||||||
|
if (!firstName || !lastName || !password) {
|
||||||
|
throw {error: 'Les champs ne peuvent pas être vides'};
|
||||||
|
}
|
||||||
|
|
||||||
|
const newPassword = formData.get('newPassword');
|
||||||
|
const confirmPassword = formData.get('confirmPassword');
|
||||||
|
|
||||||
|
if (newPassword) {
|
||||||
|
if (newPassword === password) {
|
||||||
|
throw {error: 'Le nouveau mot de passe doit être différent de l\'ancien'};
|
||||||
|
}
|
||||||
|
// regex to check if password is strong enough
|
||||||
|
const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
|
||||||
|
if (!regex.test(newPassword as string)) {
|
||||||
|
throw {error: 'Le mot de passe doit contenir au moins 8 caractères, une majuscule, une minuscule, un chiffre et un caractère spécial'};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPassword !== confirmPassword) {
|
||||||
|
throw {error: 'Les mots de passe ne correspondent pas'};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`/api/users/${userId}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
mode: 'cors',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
password,
|
||||||
|
newPassword,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
return {error: response.statusText};
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.error) {
|
||||||
|
return {error: data.error};
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export { getMeInfo, login, signup };
|
export { getMeInfo, login, signup };
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import Roles from "./roles";
|
import Roles from "./roles";
|
||||||
import { NextFunction, Request, Response, Router } from 'express';
|
import { NextFunction, Request, Response, Router } from 'express';
|
||||||
import { verifyToken } from "@/controller/AuthController";
|
import { verifyToken } from "@/controller/AuthController";
|
||||||
|
import { changeUserInfo } from "@/controller/UserController";
|
||||||
|
|
||||||
const users = Router();
|
const users = Router();
|
||||||
|
|
||||||
|
|
@ -28,4 +29,26 @@ users.use(checkAuth);
|
||||||
|
|
||||||
users.post('/:id/roles', Roles);
|
users.post('/:id/roles', Roles);
|
||||||
|
|
||||||
|
users.put('/:id', (req: Request, res: Response) => {
|
||||||
|
const userId = parseInt(req.params.id);
|
||||||
|
const token = getToken(req);
|
||||||
|
if (token === undefined) {
|
||||||
|
return res.status(401).send({ error: 'No token provided' });
|
||||||
|
}
|
||||||
|
if (req.userId !== userId) {
|
||||||
|
return res.status(401).send({ error: 'Unauthorized' });
|
||||||
|
}
|
||||||
|
return changeUserInfo(userId, req.body)
|
||||||
|
.then((data) => {
|
||||||
|
if (data instanceof Error) {
|
||||||
|
return res.status(400).send({ error: data.message });
|
||||||
|
}
|
||||||
|
return res.status(200).send(data);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
return res.status(400).send({ error: error.message });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
export default users;
|
export default users;
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { PrismaClient, Role } from '@prisma/client';
|
import { PrismaClient, Role } from '@prisma/client';
|
||||||
import { User } from '@/models/UserModel';
|
import { User } from '@/models/UserModel';
|
||||||
import { exclude } from '@/lib/utils';
|
import { exclude } from '@/lib/utils';
|
||||||
|
import { comparePassword } from './AuthController';
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
|
@ -96,4 +97,57 @@ export const changeUserRoles = async (id: number, role: Role) => {
|
||||||
return exclude(updatedUser, 'password');
|
return exclude(updatedUser, 'password');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const changeUserInfo = async (
|
||||||
|
id: number,
|
||||||
|
userInfo: { firstName: string; lastName: string; password: string; newPassword?: string; confirmPassword: string }
|
||||||
|
) => {
|
||||||
|
const currentUser = await prisma.user.findUnique({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!currentUser) {
|
||||||
|
return new Error('User not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const isPasswordCorrect = await comparePassword(userInfo.password, currentUser.password);
|
||||||
|
if (!isPasswordCorrect) {
|
||||||
|
return new Error('Password is incorrect');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userInfo.newPassword) {
|
||||||
|
if (userInfo.newPassword !== userInfo.confirmPassword) {
|
||||||
|
return new Error('New password and confirm password do not match');
|
||||||
|
}
|
||||||
|
|
||||||
|
const isPasswordSame = await comparePassword(userInfo.password, currentUser.password);
|
||||||
|
if (isPasswordSame) {
|
||||||
|
return new Error('Password are the same');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userInfo.newPassword !== userInfo.confirmPassword) {
|
||||||
|
return new Error('New password and confirm password are not the same');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
firstName: userInfo.firstName,
|
||||||
|
lastName: userInfo.lastName,
|
||||||
|
};
|
||||||
|
if (userInfo.newPassword) Object.setPrototypeOf(data, { password: userInfo.newPassword });
|
||||||
|
|
||||||
|
const updatedUser = await prisma.user.update({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!updatedUser) {
|
||||||
|
return new Error('User not found');
|
||||||
|
}
|
||||||
|
return exclude(updatedUser, 'password');
|
||||||
|
};
|
||||||
|
|
||||||
export { getUser, newUser, isUserExist, getUserById };
|
export { getUser, newUser, isUserExist, getUserById };
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue