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:
Manus
2026-04-21 05:31:44 -04:00
parent 535dd19188
commit b3137e8f27
10 changed files with 866 additions and 43 deletions

View File

@@ -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!");
}
}