feat: secu migrate to user uuid
This commit is contained in:
@@ -9,7 +9,82 @@ import {
|
||||
|
||||
export class EvaluationService {
|
||||
/**
|
||||
* Crée ou met à jour un utilisateur
|
||||
* Crée ou met à jour un utilisateur et retourne son UUID
|
||||
*/
|
||||
async upsertUserUuid(profile: UserProfile): Promise<string> {
|
||||
const pool = getPool();
|
||||
const client = await pool.connect();
|
||||
|
||||
try {
|
||||
// Vérifier si l'utilisateur existe déjà (par firstName + lastName + teamId)
|
||||
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,
|
||||
]);
|
||||
|
||||
if (existingUser.rows.length > 0) {
|
||||
// Retourner l'UUID de l'utilisateur existant
|
||||
return existingUser.rows[0].uuid_id;
|
||||
} else {
|
||||
// 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;
|
||||
}
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère un utilisateur par son UUID
|
||||
*/
|
||||
async getUserByUuid(userUuid: string): Promise<UserProfile | null> {
|
||||
const pool = getPool();
|
||||
const client = await pool.connect();
|
||||
|
||||
try {
|
||||
const query = `
|
||||
SELECT u.first_name, u.last_name, u.team_id
|
||||
FROM users u
|
||||
WHERE u.uuid_id = $1
|
||||
`;
|
||||
|
||||
const result = await client.query(query, [userUuid]);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const user = result.rows[0];
|
||||
return {
|
||||
firstName: user.first_name,
|
||||
lastName: user.last_name,
|
||||
teamId: user.team_id,
|
||||
};
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée ou met à jour un utilisateur (legacy - retourne l'ID numérique)
|
||||
*/
|
||||
async upsertUser(profile: UserProfile): Promise<number> {
|
||||
const pool = getPool();
|
||||
@@ -98,7 +173,50 @@ export class EvaluationService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sauvegarde une évaluation utilisateur complète
|
||||
* Sauvegarde une évaluation utilisateur complète (version UUID)
|
||||
*/
|
||||
async saveUserEvaluationUuid(evaluation: UserEvaluation): Promise<void> {
|
||||
const pool = getPool();
|
||||
const client = await pool.connect();
|
||||
|
||||
try {
|
||||
await client.query("BEGIN");
|
||||
|
||||
// 1. Upsert user avec UUID
|
||||
const userUuid = await this.upsertUserUuid(evaluation.profile);
|
||||
|
||||
// 2. Upsert user_evaluation avec user_uuid
|
||||
const userEvalQuery = `
|
||||
INSERT INTO user_evaluations (user_uuid, last_updated)
|
||||
VALUES ($1, $2)
|
||||
ON CONFLICT (user_uuid)
|
||||
DO UPDATE SET last_updated = $2
|
||||
RETURNING id
|
||||
`;
|
||||
|
||||
const userEvalResult = await client.query(userEvalQuery, [
|
||||
userUuid,
|
||||
new Date(evaluation.lastUpdated),
|
||||
]);
|
||||
|
||||
const userEvaluationId = userEvalResult.rows[0].id;
|
||||
|
||||
// 3. Sauvegarde chaque catégorie d'évaluation
|
||||
for (const catEval of evaluation.evaluations) {
|
||||
await this.saveSkillEvaluations(client, userEvaluationId, catEval);
|
||||
}
|
||||
|
||||
await client.query("COMMIT");
|
||||
} catch (error) {
|
||||
await client.query("ROLLBACK");
|
||||
throw error;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sauvegarde une évaluation utilisateur complète (legacy)
|
||||
*/
|
||||
async saveUserEvaluation(evaluation: UserEvaluation): Promise<void> {
|
||||
const pool = getPool();
|
||||
@@ -208,7 +326,7 @@ export class EvaluationService {
|
||||
const userQuery = `
|
||||
SELECT u.*, ue.id as user_evaluation_id, ue.last_updated
|
||||
FROM users u
|
||||
LEFT JOIN user_evaluations ue ON u.id = ue.user_id
|
||||
LEFT JOIN user_evaluations ue ON u.uuid_id = ue.user_uuid
|
||||
WHERE u.first_name = $1 AND u.last_name = $2 AND u.team_id = $3
|
||||
`;
|
||||
|
||||
@@ -293,7 +411,128 @@ export class EvaluationService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le niveau d'une skill
|
||||
* Met à jour le niveau d'une skill (version UUID)
|
||||
*/
|
||||
async updateSkillLevelUuid(
|
||||
profile: UserProfile,
|
||||
category: string,
|
||||
skillId: string,
|
||||
level: SkillLevel
|
||||
): Promise<void> {
|
||||
await this.updateSkillPropertyUuid(
|
||||
profile,
|
||||
category,
|
||||
skillId,
|
||||
"level",
|
||||
level
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le statut mentor d'une skill (version UUID)
|
||||
*/
|
||||
async updateSkillMentorStatusUuid(
|
||||
profile: UserProfile,
|
||||
category: string,
|
||||
skillId: string,
|
||||
canMentor: boolean
|
||||
): Promise<void> {
|
||||
await this.updateSkillPropertyUuid(
|
||||
profile,
|
||||
category,
|
||||
skillId,
|
||||
"can_mentor",
|
||||
canMentor
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le statut apprentissage d'une skill (version UUID)
|
||||
*/
|
||||
async updateSkillLearningStatusUuid(
|
||||
profile: UserProfile,
|
||||
category: string,
|
||||
skillId: string,
|
||||
wantsToLearn: boolean
|
||||
): Promise<void> {
|
||||
await this.updateSkillPropertyUuid(
|
||||
profile,
|
||||
category,
|
||||
skillId,
|
||||
"wants_to_learn",
|
||||
wantsToLearn
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode utilitaire pour mettre à jour une propriété de skill (version UUID)
|
||||
*/
|
||||
private async updateSkillPropertyUuid(
|
||||
profile: UserProfile,
|
||||
category: string,
|
||||
skillId: string,
|
||||
property: string,
|
||||
value: any
|
||||
): Promise<void> {
|
||||
const pool = getPool();
|
||||
const client = await pool.connect();
|
||||
|
||||
try {
|
||||
await client.query("BEGIN");
|
||||
|
||||
const userUuid = await this.upsertUserUuid(profile);
|
||||
|
||||
// Upsert user_evaluation avec user_uuid
|
||||
const userEvalResult = await client.query(
|
||||
`
|
||||
INSERT INTO user_evaluations (user_uuid, last_updated)
|
||||
VALUES ($1, CURRENT_TIMESTAMP)
|
||||
ON CONFLICT (user_uuid)
|
||||
DO UPDATE SET last_updated = CURRENT_TIMESTAMP
|
||||
RETURNING id
|
||||
`,
|
||||
[userUuid]
|
||||
);
|
||||
|
||||
const userEvaluationId = userEvalResult.rows[0].id;
|
||||
|
||||
// Upsert skill evaluation avec gestion conditionnelle du level
|
||||
let updateQuery: string;
|
||||
let queryParams: any[];
|
||||
|
||||
if (property === "level") {
|
||||
// Si on met à jour le level, utiliser la valeur fournie
|
||||
updateQuery = `
|
||||
INSERT INTO skill_evaluations (user_evaluation_id, skill_id, level, is_selected)
|
||||
VALUES ($1, $2, $3, true)
|
||||
ON CONFLICT (user_evaluation_id, skill_id)
|
||||
DO UPDATE SET level = $3, is_selected = true
|
||||
`;
|
||||
queryParams = [userEvaluationId, skillId, value];
|
||||
} else {
|
||||
// Si on met à jour une autre propriété, level par défaut = 'never'
|
||||
updateQuery = `
|
||||
INSERT INTO skill_evaluations (user_evaluation_id, skill_id, level, ${property}, is_selected)
|
||||
VALUES ($1, $2, 'never', $3, true)
|
||||
ON CONFLICT (user_evaluation_id, skill_id)
|
||||
DO UPDATE SET ${property} = $3, is_selected = true
|
||||
`;
|
||||
queryParams = [userEvaluationId, skillId, value];
|
||||
}
|
||||
|
||||
await client.query(updateQuery, queryParams);
|
||||
|
||||
await client.query("COMMIT");
|
||||
} catch (error) {
|
||||
await client.query("ROLLBACK");
|
||||
throw error;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le niveau d'une skill (legacy)
|
||||
*/
|
||||
async updateSkillLevel(
|
||||
profile: UserProfile,
|
||||
@@ -407,7 +646,60 @@ export class EvaluationService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute une skill à l'évaluation
|
||||
* Ajoute une skill à l'évaluation (version UUID)
|
||||
*/
|
||||
async addSkillToEvaluationUuid(
|
||||
profile: UserProfile,
|
||||
category: string,
|
||||
skillId: string
|
||||
): Promise<void> {
|
||||
await this.updateSkillPropertyUuid(
|
||||
profile,
|
||||
category,
|
||||
skillId,
|
||||
"level",
|
||||
"never" // Valeur par défaut pour une skill nouvellement sélectionnée
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime une skill de l'évaluation (version UUID)
|
||||
*/
|
||||
async removeSkillFromEvaluationUuid(
|
||||
profile: UserProfile,
|
||||
category: string,
|
||||
skillId: string
|
||||
): Promise<void> {
|
||||
const pool = getPool();
|
||||
const client = await pool.connect();
|
||||
|
||||
try {
|
||||
await client.query("BEGIN");
|
||||
|
||||
const userUuid = await this.upsertUserUuid(profile);
|
||||
|
||||
// Supprimer directement la skill evaluation
|
||||
const deleteQuery = `
|
||||
DELETE FROM skill_evaluations
|
||||
WHERE user_evaluation_id = (
|
||||
SELECT id FROM user_evaluations WHERE user_uuid = $1
|
||||
)
|
||||
AND skill_id = $2
|
||||
`;
|
||||
|
||||
await client.query(deleteQuery, [userUuid, skillId]);
|
||||
|
||||
await client.query("COMMIT");
|
||||
} catch (error) {
|
||||
await client.query("ROLLBACK");
|
||||
throw error;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute une skill à l'évaluation (legacy)
|
||||
*/
|
||||
async addSkillToEvaluation(
|
||||
profile: UserProfile,
|
||||
|
||||
Reference in New Issue
Block a user