Checkpoint: Ajout du champ username dans la table local_users, adaptation de l'auth backend (connexion par username OU email), mise à jour de la page Gestion des utilisateurs avec le champ username visible et éditable, compte adminItinova migré avec username propre.
This commit is contained in:
@@ -7,7 +7,7 @@ import { ENV } from "./_core/env";
|
||||
|
||||
const SALT_ROUNDS = 12;
|
||||
const JWT_EXPIRY = "7d";
|
||||
const LOCAL_AUTH_COOKIE = "veille_local_auth";
|
||||
export const LOCAL_AUTH_COOKIE = "veille_local_auth";
|
||||
|
||||
export async function hashPassword(password: string): Promise<string> {
|
||||
return bcrypt.hash(password, SALT_ROUNDS);
|
||||
@@ -37,24 +37,30 @@ export async function verifyLocalToken(token: string): Promise<{ userId: number;
|
||||
}
|
||||
}
|
||||
|
||||
export async function loginLocalUser(email: string, password: string) {
|
||||
/**
|
||||
* Connexion par username OU email (insensible à la casse pour l'email).
|
||||
* Le champ `identifier` peut être un nom d'utilisateur libre ou une adresse e-mail.
|
||||
*/
|
||||
export async function loginLocalUser(identifier: string, password: string) {
|
||||
const db = await getDb();
|
||||
if (!db) throw new Error("Base de données indisponible");
|
||||
|
||||
// Recherche par e-mail (insensible à la casse) OU par identifiant exact
|
||||
const identifier = email.trim();
|
||||
const users = await db
|
||||
const id = identifier.trim();
|
||||
|
||||
// Cherche d'abord par username exact, puis par email (insensible à la casse)
|
||||
const results = await db
|
||||
.select()
|
||||
.from(localUsers)
|
||||
.where(
|
||||
or(
|
||||
eq(localUsers.email, identifier.toLowerCase()),
|
||||
eq(localUsers.email, identifier)
|
||||
eq(localUsers.username, id),
|
||||
eq(localUsers.email, id.toLowerCase()),
|
||||
eq(localUsers.email, id)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
const user = users[0];
|
||||
const user = results[0];
|
||||
if (!user || !user.isActive) {
|
||||
throw new Error("Identifiants incorrects ou compte désactivé");
|
||||
}
|
||||
@@ -62,21 +68,29 @@ export async function loginLocalUser(email: string, password: string) {
|
||||
const valid = await verifyPassword(password, user.passwordHash);
|
||||
if (!valid) throw new Error("Identifiants incorrects ou compte désactivé");
|
||||
|
||||
// Mise à jour lastSignedIn
|
||||
await db
|
||||
.update(localUsers)
|
||||
.set({ lastSignedIn: new Date() })
|
||||
.where(eq(localUsers.id, user.id));
|
||||
|
||||
const token = await generateLocalToken(user.id, user.role);
|
||||
return { token, user: { id: user.id, name: user.name, email: user.email, role: user.role } };
|
||||
return {
|
||||
token,
|
||||
user: {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
username: user.username ?? null,
|
||||
email: user.email ?? null,
|
||||
role: user.role,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function getLocalUserById(id: number) {
|
||||
const db = await getDb();
|
||||
if (!db) return null;
|
||||
const users = await db.select().from(localUsers).where(eq(localUsers.id, id)).limit(1);
|
||||
return users[0] ?? null;
|
||||
const results = await db.select().from(localUsers).where(eq(localUsers.id, id)).limit(1);
|
||||
return results[0] ?? null;
|
||||
}
|
||||
|
||||
export async function ensureAdminExists() {
|
||||
@@ -93,11 +107,12 @@ export async function ensureAdminExists() {
|
||||
const hash = await hashPassword("Admin@Itinova2024!");
|
||||
await db.insert(localUsers).values({
|
||||
name: "Administrateur",
|
||||
username: "admin",
|
||||
email: "admin@itinova.fr",
|
||||
passwordHash: hash,
|
||||
role: "admin",
|
||||
isActive: true,
|
||||
});
|
||||
console.log("[LocalAuth] Compte admin par défaut créé : admin@itinova.fr / Admin@Itinova2024!");
|
||||
console.log("[LocalAuth] Compte admin par défaut créé : admin / Admin@Itinova2024!");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user