diff --git a/middleware.ts b/middleware.ts
index 632f4ad..e388223 100644
--- a/middleware.ts
+++ b/middleware.ts
@@ -1,6 +1,6 @@
import { NextRequest, NextResponse } from "next/server";
-const COOKIE_NAME = "peakSkills_userId";
+const COOKIE_NAME = "session_token";
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
diff --git a/package.json b/package.json
index 66b1204..2c48fd1 100644
--- a/package.json
+++ b/package.json
@@ -45,9 +45,11 @@
"@radix-ui/react-toggle-group": "1.1.1",
"@radix-ui/react-tooltip": "1.1.6",
"@types/bcrypt": "^6.0.0",
+ "@types/bcryptjs": "^3.0.0",
"@types/pg": "^8.11.10",
"autoprefixer": "^10.4.20",
"bcrypt": "^6.0.0",
+ "bcryptjs": "^3.0.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "1.0.4",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b72e822..675059c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -107,6 +107,9 @@ importers:
'@types/bcrypt':
specifier: ^6.0.0
version: 6.0.0
+ '@types/bcryptjs':
+ specifier: ^3.0.0
+ version: 3.0.0
'@types/pg':
specifier: ^8.11.10
version: 8.15.5
@@ -116,6 +119,9 @@ importers:
bcrypt:
specifier: ^6.0.0
version: 6.0.0
+ bcryptjs:
+ specifier: ^3.0.2
+ version: 3.0.2
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
@@ -1354,6 +1360,10 @@ packages:
'@types/bcrypt@6.0.0':
resolution: {integrity: sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==}
+ '@types/bcryptjs@3.0.0':
+ resolution: {integrity: sha512-WRZOuCuaz8UcZZE4R5HXTco2goQSI2XxjGY3hbM/xDvwmqFWd4ivooImsMx65OKM6CtNKbnZ5YL+YwAwK7c1dg==}
+ deprecated: This is a stub types definition. bcryptjs provides its own type definitions, so you do not need this installed.
+
'@types/d3-array@3.2.1':
resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
@@ -1410,6 +1420,10 @@ packages:
resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==}
engines: {node: '>= 18'}
+ bcryptjs@3.0.2:
+ resolution: {integrity: sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==}
+ hasBin: true
+
browserslist@4.25.3:
resolution: {integrity: sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@@ -3067,6 +3081,10 @@ snapshots:
dependencies:
'@types/node': 22.17.2
+ '@types/bcryptjs@3.0.0':
+ dependencies:
+ bcryptjs: 3.0.2
+
'@types/d3-array@3.2.1': {}
'@types/d3-color@3.1.3': {}
@@ -3128,6 +3146,8 @@ snapshots:
node-addon-api: 8.5.0
node-gyp-build: 4.8.4
+ bcryptjs@3.0.2: {}
+
browserslist@4.25.3:
dependencies:
caniuse-lite: 1.0.30001735
diff --git a/services/auth-service.ts b/services/auth-service.ts
index aa64d0c..1e30e77 100644
--- a/services/auth-service.ts
+++ b/services/auth-service.ts
@@ -2,8 +2,8 @@ import { cookies } from "next/headers";
import { UserProfile } from "@/lib/types";
import { userService } from "@/services/user-service";
-// Constantes pour les cookies (définies ici car auth-service.ts a été supprimé)
-export const COOKIE_NAME = "peakSkills_userId";
+// Constantes pour les cookies
+export const COOKIE_NAME = "session_token";
export const COOKIE_MAX_AGE = 30 * 24 * 60 * 60; // 30 jours
/**
@@ -61,6 +61,8 @@ export class AuthService {
/**
* Authentifie un utilisateur et retourne la configuration du cookie
+ * Note: Cette méthode est maintenant obsolète avec le nouveau système d'auth
+ * Utilisez login/register à la place
*/
static async authenticateUser(profile: UserProfile): Promise<{
userUuid: string;
@@ -78,24 +80,18 @@ export class AuthService {
}> {
// Vérifier si l'utilisateur existe déjà avec ces informations
const existingUser = await userService.findUserByProfile(profile);
- let userUuid: string;
- if (existingUser) {
- // Mettre à jour l'utilisateur existant si nécessaire
- if (existingUser.teamId !== profile.teamId) {
- await userService.updateUserByUuid(existingUser.uuid, profile);
- }
- userUuid = existingUser.uuid;
- } else {
- // Créer un nouvel utilisateur
- userUuid = await userService.upsertUserUuid(profile);
+ if (!existingUser) {
+ throw new Error(
+ "Utilisateur non trouvé. Veuillez vous connecter avec votre email et mot de passe."
+ );
}
return {
- userUuid,
+ userUuid: existingUser.uuid,
cookieConfig: {
name: COOKIE_NAME,
- value: userUuid,
+ value: existingUser.uuid,
options: {
maxAge: COOKIE_MAX_AGE,
httpOnly: true,
@@ -106,4 +102,25 @@ export class AuthService {
},
};
}
+
+ /**
+ * Crée une nouvelle session pour un utilisateur
+ */
+ static async createSession(userUuid: string): Promise {
+ // Pour l'instant, on utilise l'UUID comme token de session
+ // Plus tard, on pourra implémenter un système de JWT ou de sessions en base
+ return userUuid;
+ }
+
+ /**
+ * Valide un token de session et retourne l'UUID utilisateur
+ */
+ static async validateSession(sessionToken: string): Promise {
+ // Pour l'instant, on considère que le token est valide s'il correspond à un UUID
+ // Plus tard, on pourra ajouter une validation plus robuste
+ if (sessionToken && sessionToken.length > 0) {
+ return sessionToken;
+ }
+ return null;
+ }
}
diff --git a/services/evaluation-service.ts b/services/evaluation-service.ts
index ce563d3..2475d63 100644
--- a/services/evaluation-service.ts
+++ b/services/evaluation-service.ts
@@ -1,5 +1,6 @@
import { getPool } from "./database";
import { userService } from "./user-service";
+import { AuthService } from "./auth-service";
import {
UserEvaluation,
UserProfile,
@@ -121,7 +122,7 @@ export class EvaluationService {
await client.query("BEGIN");
// 1. Upsert user avec UUID
- const userUuid = await userService.upsertUserUuid(evaluation.profile);
+ const userUuid = await AuthService.getUserUuidFromCookie();
// 2. Upsert user_evaluation avec user_uuid
const userEvalQuery = `
@@ -413,7 +414,15 @@ export class EvaluationService {
try {
await client.query("BEGIN");
- const userUuid = await userService.upsertUserUuid(profile);
+ // Trouver l'utilisateur existant au lieu d'en créer un nouveau
+ const existingUser = await userService.findUserByProfile(profile);
+ if (!existingUser) {
+ throw new Error(
+ "Utilisateur non trouvé. Veuillez vous connecter avec votre email et mot de passe."
+ );
+ }
+
+ const userUuid = existingUser.uuid;
// Upsert user_evaluation avec user_uuid
const userEvalResult = await client.query(
@@ -609,7 +618,7 @@ export class EvaluationService {
try {
await client.query("BEGIN");
- const userUuid = await userService.upsertUserUuid(profile);
+ const userUuid = await AuthService.getUserUuidFromCookie();
// Supprimer directement la skill evaluation
const deleteQuery = `
diff --git a/services/user-service.ts b/services/user-service.ts
index 6da8f5f..50a99d3 100644
--- a/services/user-service.ts
+++ b/services/user-service.ts
@@ -4,51 +4,9 @@ import { UserProfile } from "../lib/types";
export class UserService {
/**
* Crée ou met à jour un utilisateur et retourne son UUID
+ * Note: Cette méthode est pour la compatibilité avec l'ancien système
+ * Les nouveaux utilisateurs doivent utiliser createUser avec email/password
*/
- async upsertUserUuid(profile: UserProfile): Promise {
- const pool = getPool();
- const client = await pool.connect();
-
- try {
- // Créer un nouvel utilisateur avec UUID auto-généré
- const insertQuery = `
- INSERT INTO users (first_name, last_name, team_id, uuid_id)
- VALUES ($1, $2, $3, uuid_generate_v4())
- RETURNING uuid_id
- `;
-
- const result = await client.query(insertQuery, [
- profile.firstName,
- profile.lastName,
- profile.teamId,
- ]);
-
- return result.rows[0].uuid_id;
- } catch (error: any) {
- // Si erreur de contrainte unique, l'utilisateur existe déjà
- if (
- error.code === "23505" &&
- error.constraint === "users_first_name_last_name_team_id_key"
- ) {
- // Récupérer l'utilisateur existant
- const existingUserQuery = `
- SELECT uuid_id FROM users
- WHERE first_name = $1 AND last_name = $2 AND team_id = $3
- `;
-
- const existingUser = await client.query(existingUserQuery, [
- profile.firstName,
- profile.lastName,
- profile.teamId,
- ]);
-
- return existingUser.rows[0].uuid_id;
- }
- throw error;
- } finally {
- client.release();
- }
- }
/**
* Met à jour un utilisateur existant par son UUID
@@ -314,6 +272,132 @@ export class UserService {
throw new Error("Failed to fetch users");
}
}
+
+ /**
+ * Récupère un utilisateur par son email
+ */
+ async getUserByEmail(email: string): Promise<{
+ uuid_id: string;
+ first_name: string;
+ last_name: string;
+ email: string;
+ team_id: string;
+ } | null> {
+ const pool = getPool();
+ const client = await pool.connect();
+
+ try {
+ const query = `
+ SELECT uuid_id, first_name, last_name, email, team_id
+ FROM users
+ WHERE email = $1
+ `;
+
+ const result = await client.query(query, [email]);
+
+ if (result.rows.length === 0) {
+ return null;
+ }
+
+ return result.rows[0];
+ } finally {
+ client.release();
+ }
+ }
+
+ /**
+ * Crée un nouvel utilisateur avec email et mot de passe hashé
+ */
+ async createUser(data: {
+ firstName: string;
+ lastName: string;
+ email: string;
+ passwordHash: string;
+ teamId: string;
+ }): Promise<{
+ uuid_id: string;
+ first_name: string;
+ last_name: string;
+ email: string;
+ team_id: string;
+ } | null> {
+ const pool = getPool();
+ const client = await pool.connect();
+
+ try {
+ const query = `
+ INSERT INTO users (first_name, last_name, email, password_hash, team_id, uuid_id)
+ VALUES ($1, $2, $3, $4, $5, uuid_generate_v4())
+ RETURNING uuid_id, first_name, last_name, email, team_id
+ `;
+
+ const result = await client.query(query, [
+ data.firstName,
+ data.lastName,
+ data.email,
+ data.passwordHash,
+ data.teamId,
+ ]);
+
+ if (result.rows.length === 0) {
+ return null;
+ }
+
+ return result.rows[0];
+ } finally {
+ client.release();
+ }
+ }
+
+ /**
+ * Vérifie les identifiants de connexion
+ */
+ async verifyCredentials(
+ email: string,
+ password: string
+ ): Promise<{
+ uuid_id: string;
+ first_name: string;
+ last_name: string;
+ email: string;
+ team_id: string;
+ } | null> {
+ const pool = getPool();
+ const client = await pool.connect();
+
+ try {
+ const query = `
+ SELECT uuid_id, first_name, last_name, email, team_id, password_hash
+ FROM users
+ WHERE email = $1
+ `;
+
+ const result = await client.query(query, [email]);
+
+ if (result.rows.length === 0) {
+ return null;
+ }
+
+ const user = result.rows[0];
+
+ // Vérifier le mot de passe
+ const bcrypt = require("bcryptjs");
+ const isValidPassword = await bcrypt.compare(
+ password,
+ user.password_hash
+ );
+
+ if (!isValidPassword) {
+ return null;
+ }
+
+ // Retourner l'utilisateur sans le hash du mot de passe
+ const { password_hash, ...userWithoutPassword } = user;
+ return userWithoutPassword;
+ } finally {
+ client.release();
+ }
+ }
}
// Instance singleton