337 lines
9.4 KiB
JavaScript
337 lines
9.4 KiB
JavaScript
const admin = require("firebase-admin");
|
|
const db = admin.firestore();
|
|
const logger = require("firebase-functions/logger");
|
|
const auth = require("../utils/auth");
|
|
const helpers = require("../utils/helpers");
|
|
|
|
// Créer un utilisateur
|
|
exports.createUser = async (req, res) => {
|
|
try {
|
|
const decodedToken = await auth.authenticateUser(req);
|
|
const isAdminUser = await auth.isAdmin(decodedToken.uid);
|
|
|
|
if (!isAdminUser) {
|
|
res.status(403).json({error: "Forbidden: Admin access required"});
|
|
return;
|
|
}
|
|
|
|
const userData = req.body.data;
|
|
const userId = userData.uid;
|
|
|
|
if (!userId) {
|
|
res.status(400).json({error: "User ID is required"});
|
|
return;
|
|
}
|
|
|
|
await db.collection("users").doc(userId).set(userData);
|
|
|
|
res.status(201).json({id: userId, message: "User created successfully"});
|
|
} catch (error) {
|
|
logger.error("Error creating user:", error);
|
|
res.status(500).json({error: error.message});
|
|
}
|
|
};
|
|
|
|
// Créer un utilisateur avec invitation par email
|
|
exports.createUserWithInvite = async (req, res) => {
|
|
try {
|
|
const decodedToken = await auth.authenticateUser(req);
|
|
const isAdminUser = await auth.isAdmin(decodedToken.uid);
|
|
|
|
if (!isAdminUser) {
|
|
res.status(403).json({error: "Forbidden: Admin access required"});
|
|
return;
|
|
}
|
|
|
|
const {email, firstName, lastName, phoneNumber, roleId} = req.body.data;
|
|
|
|
if (!email || !firstName || !lastName || !roleId) {
|
|
res.status(400).json({error: "email, firstName, lastName, and roleId are required"});
|
|
return;
|
|
}
|
|
|
|
const tempPassword = Math.random().toString(36).slice(-12) + "Aa1!";
|
|
|
|
let userRecord;
|
|
try {
|
|
userRecord = await admin.auth().createUser({
|
|
email: email,
|
|
password: tempPassword,
|
|
emailVerified: false,
|
|
displayName: `${firstName} ${lastName}`,
|
|
});
|
|
} catch (authError) {
|
|
logger.error("Error creating user in Auth:", authError);
|
|
res.status(500).json({error: `Failed to create user in Auth: ${authError.message}`});
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await db.collection("users").doc(userRecord.uid).set({
|
|
firstName: firstName,
|
|
lastName: lastName,
|
|
email: email,
|
|
phoneNumber: phoneNumber || "",
|
|
profilePhotoUrl: "",
|
|
role: db.collection("roles").doc(roleId),
|
|
createdAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
createdBy: decodedToken.uid,
|
|
});
|
|
} catch (firestoreError) {
|
|
logger.error("Error creating user in Firestore:", firestoreError);
|
|
try {
|
|
await admin.auth().deleteUser(userRecord.uid);
|
|
} catch (cleanupError) {
|
|
logger.error("Error cleaning up Auth user:", cleanupError);
|
|
}
|
|
res.status(500).json({error: `Failed to create user in Firestore: ${firestoreError.message}`});
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const axios = require("axios");
|
|
const firebaseApiKey = "AIzaSyARQL4P-t5l-cNjQNP9cMokQrLJ8BorF0U";
|
|
|
|
await axios.post(
|
|
`https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode?key=${firebaseApiKey}`,
|
|
{
|
|
requestType: "PASSWORD_RESET",
|
|
email: email,
|
|
},
|
|
);
|
|
logger.info(`Password reset email sent to ${email}`);
|
|
} catch (emailError) {
|
|
logger.warn(`Could not send password reset email to ${email}: ${emailError.message}`);
|
|
}
|
|
|
|
logger.info(`User ${userRecord.uid} created by ${decodedToken.uid}`);
|
|
res.status(201).json({
|
|
id: userRecord.uid,
|
|
message: "User created successfully. Password reset email sent.",
|
|
});
|
|
} catch (error) {
|
|
logger.error("Error in createUserWithInvite:", error);
|
|
res.status(500).json({error: error.message});
|
|
}
|
|
};
|
|
|
|
// Mettre à jour un utilisateur
|
|
exports.updateUser = async (req, res) => {
|
|
try {
|
|
const decodedToken = await auth.authenticateUser(req);
|
|
const {userId, data} = req.body.data;
|
|
|
|
if (!userId) {
|
|
res.status(400).json({error: "User ID is required"});
|
|
return;
|
|
}
|
|
|
|
// Un utilisateur ne peut modifier que son propre compte, sauf s'il est admin
|
|
const isAdminUser = await auth.isAdmin(decodedToken.uid);
|
|
if (decodedToken.uid !== userId && !isAdminUser) {
|
|
res.status(403).json({error: "Forbidden: Cannot update other user accounts"});
|
|
return;
|
|
}
|
|
|
|
// Empêcher les non-admins de modifier le rôle
|
|
if (!isAdminUser && data.role) {
|
|
delete data.role;
|
|
}
|
|
|
|
// Si le rôle est fourni et est un string, le convertir en DocumentReference
|
|
if (data.role && typeof data.role === "string") {
|
|
data.role = db.collection("roles").doc(data.role);
|
|
}
|
|
|
|
await db.collection("users").doc(userId).update(data);
|
|
|
|
res.status(200).json({message: "User updated successfully"});
|
|
} catch (error) {
|
|
logger.error("Error updating user:", error);
|
|
res.status(500).json({error: error.message});
|
|
}
|
|
};
|
|
|
|
// Supprimer un utilisateur
|
|
exports.deleteUser = async (req, res) => {
|
|
try {
|
|
const decodedToken = await auth.authenticateUser(req);
|
|
const isAdminUser = await auth.isAdmin(decodedToken.uid);
|
|
|
|
if (!isAdminUser) {
|
|
res.status(403).json({error: "Forbidden: Admin access required"});
|
|
return;
|
|
}
|
|
|
|
const {userId} = req.body.data;
|
|
|
|
if (!userId) {
|
|
res.status(400).json({error: "User ID is required"});
|
|
return;
|
|
}
|
|
|
|
if (decodedToken.uid === userId) {
|
|
res.status(400).json({error: "Cannot delete your own account"});
|
|
return;
|
|
}
|
|
|
|
await db.collection("users").doc(userId).delete();
|
|
|
|
try {
|
|
await admin.auth().deleteUser(userId);
|
|
} catch (authError) {
|
|
logger.warn(`Could not delete user from Auth: ${authError.message}`);
|
|
}
|
|
|
|
res.status(200).json({message: "User deleted successfully"});
|
|
} catch (error) {
|
|
logger.error("Error deleting user:", error);
|
|
res.status(500).json({error: error.message});
|
|
}
|
|
};
|
|
|
|
// Récupérer tous les utilisateurs (selon permissions)
|
|
exports.getUsers = async (req, res) => {
|
|
try {
|
|
const decodedToken = await auth.authenticateUser(req);
|
|
|
|
const canViewAll = await auth.hasPermission(decodedToken.uid, "view_all_users");
|
|
|
|
if (!canViewAll) {
|
|
const userDoc = await db.collection("users").doc(decodedToken.uid).get();
|
|
|
|
if (!userDoc.exists) {
|
|
res.status(404).json({error: "User not found"});
|
|
return;
|
|
}
|
|
|
|
let userData = userDoc.data();
|
|
userData = helpers.serializeTimestamps(userData);
|
|
userData = helpers.serializeReferences(userData);
|
|
|
|
res.status(200).json({
|
|
users: [{
|
|
id: userDoc.id,
|
|
...userData,
|
|
}],
|
|
});
|
|
return;
|
|
}
|
|
|
|
const snapshot = await db.collection("users").get();
|
|
const users = snapshot.docs.map((doc) => {
|
|
let data = doc.data();
|
|
data = helpers.serializeTimestamps(data);
|
|
data = helpers.serializeReferences(data);
|
|
return {
|
|
id: doc.id,
|
|
...data,
|
|
};
|
|
});
|
|
|
|
res.status(200).json({users});
|
|
} catch (error) {
|
|
logger.error("Error fetching users:", error);
|
|
res.status(500).json({error: error.message});
|
|
}
|
|
};
|
|
|
|
// Récupère un utilisateur spécifique
|
|
exports.getUser = async (req, res) => {
|
|
try {
|
|
const decodedToken = await auth.authenticateUser(req);
|
|
|
|
const {userId} = req.body.data || req.body || {};
|
|
if (!userId) {
|
|
res.status(400).json({error: "userId is required"});
|
|
return;
|
|
}
|
|
|
|
const userDoc = await db.collection("users").doc(userId).get();
|
|
if (!userDoc.exists) {
|
|
res.status(404).json({error: "User not found"});
|
|
return;
|
|
}
|
|
|
|
const user = userDoc.data();
|
|
|
|
const userData = {
|
|
id: userDoc.id,
|
|
uid: user.uid || userDoc.id,
|
|
email: user.email || "",
|
|
firstName: user.firstName || "",
|
|
lastName: user.lastName || "",
|
|
phoneNumber: user.phoneNumber || "",
|
|
profilePhotoUrl: user.profilePhotoUrl || "",
|
|
};
|
|
|
|
if (user.role) {
|
|
const roleDoc = await user.role.get();
|
|
if (roleDoc.exists) {
|
|
userData.role = {
|
|
id: roleDoc.id,
|
|
...roleDoc.data(),
|
|
};
|
|
}
|
|
}
|
|
|
|
res.status(200).json({user: userData});
|
|
} catch (error) {
|
|
logger.error("Error fetching user:", error);
|
|
res.status(500).json({error: error.message});
|
|
}
|
|
};
|
|
|
|
// Récupère l'utilisateur actuellement authentifié avec son rôle
|
|
exports.getCurrentUser = async (req, res) => {
|
|
try {
|
|
const decodedToken = await auth.authenticateUser(req);
|
|
const userId = decodedToken.uid;
|
|
|
|
const userDoc = await db.collection("users").doc(userId).get();
|
|
if (!userDoc.exists) {
|
|
res.status(404).json({error: "User not found"});
|
|
return;
|
|
}
|
|
|
|
const userData = userDoc.data();
|
|
|
|
let roleData = null;
|
|
if (userData.role) {
|
|
const roleDoc = await userData.role.get();
|
|
if (roleDoc.exists) {
|
|
roleData = {id: roleDoc.id, ...roleDoc.data()};
|
|
}
|
|
}
|
|
|
|
res.status(200).json({
|
|
user: {
|
|
uid: userId,
|
|
...helpers.serializeTimestamps(userData),
|
|
role: roleData,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
logger.error("Error getting current user:", error);
|
|
res.status(500).json({error: error.message});
|
|
}
|
|
};
|
|
|
|
// Récupère tous les rôles
|
|
exports.getRoles = async (req, res) => {
|
|
try {
|
|
await auth.authenticateUser(req);
|
|
|
|
const snapshot = await db.collection("roles").get();
|
|
const roles = snapshot.docs.map((doc) => ({
|
|
id: doc.id,
|
|
...helpers.serializeTimestamps(doc.data()),
|
|
}));
|
|
|
|
res.status(200).json({roles});
|
|
} catch (error) {
|
|
logger.error("Error fetching roles:", error);
|
|
res.status(500).json({error: error.message});
|
|
}
|
|
};
|