Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ec5a938
Code pour contrôler les moteurs
Raimanta-215 Mar 3, 2025
8dd229a
Add files via upload
Marry3005 Mar 13, 2025
214be3f
Update and rename code_bouton.txt to code_bouton.js
Marry3005 Mar 19, 2025
fea0a06
copie de la base de main pour travailler sur dev
Raimanta-215 Mar 25, 2025
9dc314f
frontend et api communiquant ensemble
Raimanta-215 Mar 25, 2025
4e27a03
corrections mini bugs
Marry3005 Apr 1, 2025
606cabe
maj
Raimanta-215 Apr 1, 2025
32f0db8
mini bug
Raimanta-215 Apr 3, 2025
f77689f
models pour les donnée joueur
Raimanta-215 Apr 7, 2025
3a9537f
définition de toutes les tables de la DB
Raimanta-215 Apr 7, 2025
6fc5fd7
Merge pull request #10 from loyde07/db
Marry3005 Apr 8, 2025
bf2f1dd
Merge branch 'Developpe' of https://github.com/loyde07/RDI25 into Dev…
Raimanta-215 Apr 8, 2025
bc7fd35
ajout page inscription, page de connexion, page utilisateur, mdp chif…
Hassan-mumu Apr 10, 2025
72c0db9
correction modification du profil + ajout css pour la page
Hassan-mumu Apr 13, 2025
ff6c7fc
update db joueur, update route pour authentification, ajout de verifi…
Hassan-mumu Apr 22, 2025
18f7146
Ajout modif du profil utilisateur (modifier les information personnel…
Hassan-mumu Apr 23, 2025
97ac36c
fix des bugs
Hassan-mumu Apr 23, 2025
9f044fa
initialisation environnement de test + debut de stests pour controlle…
Hassan-mumu Apr 24, 2025
f080055
+ ajout des modulles de test
Hassan-mumu Apr 29, 2025
ce0d4dd
+ Ajout des tests unitaire pour la page de login
Hassan-mumu Apr 29, 2025
0805548
+ ajoute des ecoles et des niveau dans le formulaire d'inscription
Hassan-mumu May 3, 2025
5ecff85
~update EditableField ajout de label
Hassan-mumu May 20, 2025
fdd48eb
update mise à jour du profil depuis test/register -> register
Hassan-mumu May 20, 2025
f20f133
-> déplacement des fichier .json de la racine au backend et adaptatio…
Hassan-mumu May 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.qodo
/api_backend/node_modules
/api_backend/.env
12 changes: 12 additions & 0 deletions api_backend/cloudinary/cloudinary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {v2 as cloudinary} from 'cloudinary'
import {config} from 'dotenv'

config()

cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET
})
export default cloudinary

13 changes: 13 additions & 0 deletions api_backend/config/db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import mongoose from "mongoose";

export const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.MONGO_URI); // se connecte à la DB en asynchrone
console.log(`MongoDB connected: ${conn.connection.host}`);
} catch (error){
console.error(`Error: ${error.message}`);
process.exit(1); // 1 c'est pour dire que y a eu erreur et 0 succès
}
}

//NoSQL enrgistre dans des collections avec des documents
288 changes: 288 additions & 0 deletions api_backend/controllers/auth.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
import bcrypt from 'bcryptjs';
import crypto from 'crypto';

import { User } from "../models/user.model.js";
import { generateTokenAndSetCookie} from '../utils/generateTokenAndSetCookie.js';

import { sendPasswordResetRequest,
sendResetSuccessEmail,
sendWelcomeEmail,
sendVerificationEmail } from "../mailtrap/emails.js";

import cloudinary from '../cloudinary/cloudinary.js';
import { console } from 'inspector/promises';

export const checkAuth = async (req, res) => {
try {
const user = await User.findById(req.userId).select("-password").populate('ecole_id');
if (!user){
return res.status(400).json({ success: false, message: "Utilisateur introuvable" });
}
res.status(200).json({ success: true, user });
} catch (error) {
console.log("Error in checkAuth ", error);
res.status(400).json({ success: false, message: error.message });
}
}


export const signup = async (req, res) => {
const {lName, fName, pseudo, ecole_id, niveau, email, password} = req.body;
try {

// les champs requis doivent etre entrée
if(!lName, !fName, !pseudo, !ecole_id, !niveau, !email, !password){
throw new Error("Tout les champs sont requis")
}

// Error si l'email existe déja dans la db
const userAlreadyExists = await User.findOne({email});
console.log("userAlreadyExists", userAlreadyExists)
if (userAlreadyExists){
return res.status(400).json({sucess:false, message: "Adresse email déjà utilisée"});
}

const pseudoAlreadyExists = await User.findOne({pseudo});
console.log("pseudoAlreadyExists", pseudoAlreadyExists)
if (pseudoAlreadyExists){
return res.status(400).json({sucess:false, message: "pseudo déjà utilisé"});
}

// permet de hashé le mot de passe pour qu'il ne soit pas stocké une fois enregistré
const hashedPassword = await bcrypt.hash(password, 12);

// generation d'un Token random pour l'authentification
const verificationToken = Math.floor(100000 + Math.random() * 900000 );toString();
const user = new User({
lName,
fName,
pseudo,
ecole_id,
niveau,
email,
password: hashedPassword,
verificationToken,
verificationTokenExpiresAt: Date.now() + 24 * 60 * 60 * 1000 // 24 heures
})

// sauvegarde dans la db
user.isVerified = true
user.verificationToken = undefined;
user.verificationTokenExpiresAt = undefined;
await user.save();

//jwt
generateTokenAndSetCookie(res, user._id);

// await sendVerificationEmail(user.email, verificationToken);



res.status(201).json({
sucess: true,
message: "Utilisateur créé avec succès",
user: {
...user._doc,
password: undefined // empèche le client de voire le mot de passe en le supprimant de la réponse
}
})

} catch (error) {
return res.status(400).json({sucess:false, message: error.message});
}
}

export const verifyEmail = async (req, res) => {
const {code} = req.body;
try {
const user = await User.findOne({
// s'assure qu'il y a un utilisateur avec ce Token et que le token n'est pas expiré

verificationToken: code,
verificationTokenExpiresAt: { $gt: Date.now()}
})
if (!user){
return res.status(400).json({sucess:false, message: "Invalid or expired verification code"})
}

user.isVerified = true;
user.verificationToken = undefined;
user.verificationTokenExpiresAt = undefined;
await user.save();

await sendWelcomeEmail(user.email, user.fName);
res.status(200).json({sucess:true,
message:"Email verified successfully",
user: {
...user._doc,
password: undefined
}
})
} catch (error) {
console.log("Error in verifyEmail ", error)
res.status(500).json({success:false, message: "Server error"})
}
}

export const login = async (req, res) => {
const {email, password} = req.body;
try {
const user = await User.findOne({email}).populate('ecole_id');;
if (!user){
return res.status(400).json({sucess:false, message:"email incorrecte"})
}

const ispasswordValid = await bcrypt.compare(password, user.password);
if (!ispasswordValid){
return res.status(400).json({sucess:false, message:"Mot de passe incorrect"})
}

generateTokenAndSetCookie(res, user._id);
user.lastLogin = new Date();
await user.save();

res.status(200).json({
success:true,
message: "Logged in sucessfully",
user: {
...user._doc,
password: undefined,
}

})
}
catch (error) {
console.log("Error in login, ", error)
return res.status(400).json({sucess:false, message: error.message});
}
}

export const logout = async (req, res) => {
res.clearCookie("token");
res.status(200).json({success: true, message: "Logged out successfully" });
}

export const forgotPassword = async (req, res) => {
const {email} = req.body;
try {
const user = await User.findOne({email});
if (!user){
return res.status(400).json({success:false, message:"Email does not exist"})
}

// génère un nouveau token pour le reset du mot de passe
const resetToken = crypto.randomBytes(20).toString("hex");

user.resetPasswordToken = resetToken;
user.resetPasswordExpiresAt = Date.now() + 1 * 60 * 60 * 1000; //1 heure

await user.save();

// envoie de l'email
await sendPasswordResetRequest(user.email, `${process.env.CLIENT_URL}/resetPassword/${resetToken}`);
res.status(200).json({success: true, message: "password reset link sent to your email" });

} catch (error) {
console.log("Error in forgotPassword ", error);
res.status(400).json({sucess: false, message: error.message});

}
}

export const resetPassword = async (req, res) => {
try {
const {token} = req.params;
const {password} = req.body;

const user = await User.findOne({
resetPasswordToken: token,
resetPasswordExpiresAt: {$gt: Date.now()},
});

if (!user){
return res.status(400).json({success: false, message: "invalid or expired reset token"})
}

// Nouveau mot de passe récupéré sera haché à nouveau
const hashedPassword = await bcrypt.hash(password,10);

user.password = hashedPassword
user.resetPasswordToken = undefined
user.resetPasswordExpiresAt = undefined
await user.save();

await sendResetSuccessEmail(user.email)

res.status(200).json({success:true, message:"Password reset successful"})
} catch (error) {
console.log("Error in resetPassword ", error)
res.status(400).json({success:false, message:error.message})

}

}

export const updatePic = async (req, res) => {
try {
const user = await User.findById(req.userId).select("-password"); // On récupère l'user de la DB
if (!user){
return res.status(400).json({success:false, message:"User not found"});
}

const {profilePic} = req.body
console.log("Données envoyées:", profilePic);

if(!profilePic){
return res.status(400).json({success:false, message:"Profile picture is required"})
}
const uploadResponse = await cloudinary.uploader.upload(profilePic, {
resource_type: "auto"
});
const updatedUser = await User.findByIdAndUpdate(user._id, {logo: uploadResponse.secure_url}, {new:true})

res.status(200).json({success:true, user:updatedUser})

} catch (error) {
console.log("error in update profile: ", error)
res.status(500).json({success:false, message:"Internal server error"})

}
}

export const updateProfile = async (req, res) => {
try {
const userId = req.userId; // Injecté par verifyToken
const { nom, prenom, pseudo, ecole_id, niveau, password } = req.body;

const user = await User.findById(userId).populate('ecole_id');;
if (!user) {
return res.status(404).json({ message: "Utilisateur non trouvé" });
}

// Mettre à jour les champs
if (nom) user.lName = nom;
if (prenom) user.fName = prenom;
if (pseudo) user.pseudo = pseudo;
if (ecole_id) user.ecole_id = ecole_id;
if (niveau) user.niveau = niveau;

// Si password fourni, on le hash avant update
if (password) {
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
user.password = hashedPassword;
}

// Sauvegarde
await user.save();

// On enlève le mot de passe de la réponse
const { password: pwd, ...userData } = user._doc;

return res.status(200).json({success: true, message: "Profil mis à jour avec succès", user: userData });

} catch (error) {
console.error("Erreur updateProfile:", error);
return res.status(500).json({ message: "Erreur serveur" });
}
};
Loading