fix: connexion par identifiant (adminItinova) ou email - conformité skill itinova-user-management
This commit is contained in:
@@ -30,16 +30,20 @@ const router = Router();
|
|||||||
// POST /api/auth/login
|
// POST /api/auth/login
|
||||||
router.post('/login', async (req: Request, res: Response) => {
|
router.post('/login', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { email, password } = req.body;
|
// Accepter 'login' ou 'email' (compatibilité skill itinova-user-management)
|
||||||
|
const login = (req.body.login || req.body.email || '').trim();
|
||||||
|
const { password } = req.body;
|
||||||
|
|
||||||
if (!email || !password) {
|
if (!login || !password) {
|
||||||
return res.status(400).json({ error: 'Email et mot de passe requis' });
|
return res.status(400).json({ error: 'Identifiant et mot de passe requis' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const pool = getPool();
|
const pool = getPool();
|
||||||
|
// Recherche par email exact OU par identifiant (login sans @)
|
||||||
|
// Ex: 'adminItinova' trouvera 'adminItinova@santinova-soft.org'
|
||||||
const [rows]: any = await pool.execute(
|
const [rows]: any = await pool.execute(
|
||||||
'SELECT * FROM users WHERE email = ? AND is_active = TRUE',
|
`SELECT * FROM users WHERE (email = ? OR email = CONCAT(?, '@santinova-soft.org')) AND is_active = TRUE`,
|
||||||
[email]
|
[login, login]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!rows.length) {
|
if (!rows.length) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { authAPI } from '../services/api';
|
|||||||
interface AuthContextType {
|
interface AuthContextType {
|
||||||
user: User | null;
|
user: User | null;
|
||||||
token: string | null;
|
token: string | null;
|
||||||
login: (email: string, password: string) => Promise<void>;
|
login: (login: string, password: string) => Promise<void>;
|
||||||
logout: () => void;
|
logout: () => void;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
}
|
}
|
||||||
@@ -45,8 +45,8 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const login = async (email: string, password: string) => {
|
const login = async (login: string, password: string) => {
|
||||||
const res = await authAPI.login(email, password);
|
const res = await authAPI.login(login, password);
|
||||||
const { token: newToken, user: newUser } = res.data;
|
const { token: newToken, user: newUser } = res.data;
|
||||||
setToken(newToken);
|
setToken(newToken);
|
||||||
setUser(newUser);
|
setUser(newUser);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useAuth } from '../context/AuthContext';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
const [email, setEmail] = useState('');
|
const [loginValue, setLoginValue] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const { login } = useAuth();
|
const { login } = useAuth();
|
||||||
@@ -14,7 +14,7 @@ export default function Login() {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
await login(email, password);
|
await login(loginValue, password);
|
||||||
toast.success('Connexion réussie');
|
toast.success('Connexion réussie');
|
||||||
navigate('/');
|
navigate('/');
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@@ -79,18 +79,19 @@ export default function Login() {
|
|||||||
<div className="bg-white rounded-2xl shadow-lg p-8">
|
<div className="bg-white rounded-2xl shadow-lg p-8">
|
||||||
<form onSubmit={handleSubmit} className="space-y-5">
|
<form onSubmit={handleSubmit} className="space-y-5">
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1">
|
<label htmlFor="login" className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Adresse email
|
Identifiant ou email
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="email"
|
id="login"
|
||||||
type="email"
|
type="text"
|
||||||
value={email}
|
value={loginValue}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setLoginValue(e.target.value)}
|
||||||
className="input-field"
|
className="input-field"
|
||||||
placeholder="votre@email.com"
|
placeholder="adminItinova ou votre@email.com"
|
||||||
required
|
required
|
||||||
autoFocus
|
autoFocus
|
||||||
|
autoComplete="username"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default api;
|
|||||||
|
|
||||||
// Auth
|
// Auth
|
||||||
export const authAPI = {
|
export const authAPI = {
|
||||||
login: (email: string, password: string) => api.post('/auth/login', { email, password }),
|
login: (login: string, password: string) => api.post('/auth/login', { login, password }),
|
||||||
me: () => api.get('/auth/me'),
|
me: () => api.get('/auth/me'),
|
||||||
getUsers: () => api.get('/auth/users'),
|
getUsers: () => api.get('/auth/users'),
|
||||||
createUser: (data: any) => api.post('/auth/users', data),
|
createUser: (data: any) => api.post('/auth/users', data),
|
||||||
|
|||||||
Reference in New Issue
Block a user