Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 0 additions & 8 deletions .example.env

This file was deleted.

4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,7 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
.vscode

/generated/prisma

docs
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Ce dépôt est un squelette réutilisable pour mes backends Node.js avec PostgreSQL + Prisma + Supabase. N'y ajoutez pas de logique métier spécifique.
197 changes: 142 additions & 55 deletions controllers/http/AuthController.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,35 @@
const { Validator } = require('node-input-validator');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const { mailjet } = require('../../services/mailjet')
const { mailjet } = require('../../services/mailjet');
const generator = require('generate-password');
const db = require('../../services/database');
const User = db.User;
const { v4: uuidv4 } = require('uuid'); // Pour la génération d'UUID
const { prisma } = require('../../services/database'); // Prisma centralisé
const jwksClient = require('jwks-rsa');

console.log('SUPABASE_ANON_KEY:', process.env.SUPABASE_ANON_KEY);

// Remplace <TON-PROJET> par le vrai nom de ton projet Supabase
const client = jwksClient({
jwksUri: 'https://hvhpfjkiesiaygxycynt.supabase.co/auth/v1/jwks',
requestHeaders: {
apikey: process.env.SUPABASE_ANON_KEY
}
});

function getKey(header, callback, res) {
client.getSigningKey(header.kid, function(err, key) {
if (err || !key) {
console.error('[GoogleAuth] JWKS error:', err);
if (res) {
return res.status(500).json({ status: 500, error: "JWKS key not found", details: err?.message });
}
return callback(new Error('JWKS key not found'), null);
}
const signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}

const login = async (req, res) => {
const v = new Validator(req.body, {
Expand All @@ -14,38 +39,39 @@ const login = async (req, res) => {
const matched = await v.check();
if (!matched) {
return res.status(422).json({
status: 422,
error: v.errors
})
status: 422,
error: v.errors
});
}
let user = await User.findOne({email: req.body.email})
// Recherche l'utilisateur par email (findFirst car email n'est pas unique)
let user = await prisma.user.findFirst({ where: { email: req.body.email } });
if (!user) {
return res.status(401).json({
status: 401,
error: "Email don't match !"
})
});
}
const compare = await bcrypt.compare(req.body.password, user.password)
if (compare){
const compare = await bcrypt.compare(req.body.password, user.encrypted_password);
if (compare) {
const token = jwt.sign({
identity: user.email,
role: user.role
}, process.env.APP_JWT, { expiresIn: '7d' });
const { password, ...userWithoutHash } = user.toObject();
const { encrypted_password, ...userWithoutHash } = user;
return res.status(200).json({
status: 200,
data: {
user: userWithoutHash,
token: token
}
})
});
} else {
return res.status(401).json({
status: 401,
error: "Password don't match !"
})
});
}
}
};

const register = async (req, res) => {
const v = new Validator(req.body, {
Expand All @@ -57,56 +83,69 @@ const register = async (req, res) => {
return res.status(422).json({
status: 422,
error: v.errors
})
});
}
const checkEmail = await User.findOne({email: req.body.email})
if (checkEmail){
// Check if user already exists
const userExists = await prisma.user.findFirst({ where: { email: req.body.email } });
if (userExists) {
return res.status(409).json({
status: 409,
message: "Email already exist !"
})
});
}
const user = new User();
user.email = req.body.email;
user.role = ['user']
user.password = await bcrypt.hashSync(req.body.password, 10);
await user.save()
// Hash password
const hashedPassword = await bcrypt.hash(req.body.password, 10);
// Génère un UUID pour le champ id
const id = uuidv4();
// Insert new user avec Prisma ORM
const user = await prisma.user.create({
data: {
id: id,
email: req.body.email,
encrypted_password: hashedPassword,
created_at: new Date(),
updated_at: new Date()
}
});
// Generate JWT token (optional, as before)
const token = jwt.sign({
identity: user.email,
role: user.role
}, process.env.APP_JWT, { expiresIn: '7d' });
const { password, ...userWithoutHash } = user.toObject();
identity: req.body.email,
// role: user.role // if you want to include role
}, process.env.APP_JWT, { expiresIn: '7d' });
// Remove sensitive info from response
const { encrypted_password, ...userWithoutHash } = user;
return res.status(200).json({
status: 200,
data: {
user: userWithoutHash,
token: token
}
})
}
});
};

const forgetPassword = async (req, res) => {

const v = new Validator(req.body, {
email: 'required|email',
});
const matched = await v.check();
if (!matched) {
return res.status(422).json({
status: 422,
error: v.errors
})
status: 422,
error: v.errors
});
}
const user = await User.findOne({email: req.body.email})
const user = await prisma.user.findUnique({ where: { email: req.body.email } });
if (!user){
return res.status(401).json({
status: 401,
error: "Email don't match !"
})
});
}
const key = generator.generate({length: 40, numbers: true});
user.tokenResetPassword = key;
await user.save()
await prisma.user.update({
where: { email: req.body.email },
data: { recovery_token: key, updated_at: new Date() }
});
mailjet.post("send", {'version': 'v3.1'}).request({
"Messages":[
{
Expand All @@ -122,15 +161,15 @@ const forgetPassword = async (req, res) => {
],
"Subject": "Réinitialisation mots de passe.",
"TextPart": "Réinitialisation du mots de passe",
"HTMLPart": "<h3>Nous avons cru comprendre que vous vouliez réinitialiser votre mot de passe.<br>Cliquez sur le lien ci-dessous et vous serez redirigé vers un site sécurisé où vous pourrez définir un nouveau mot de passe.<br><br> <a href='https://uslow.io/reset?token="+key+"'>Click ici</a>!</h3><br />L'équipe Uslow",
"HTMLPart": `<h3>Nous avons cru comprendre que vous vouliez réinitialiser votre mot de passe.<br>Cliquez sur le lien ci-dessous et vous serez redirigé vers un site sécurisé où vous pourrez définir un nouveau mot de passe.<br><br> <a href='https://uslow.io/reset?token=${key}'>Click ici</a>!</h3><br />L'équipe Uslow`,
}
]
})
});
return res.status(200).json({
status: 200,
data: req.body.email
})
}
});
};

const resetPassword = async (req, res) => {
const v = new Validator(req.body, {
Expand All @@ -140,33 +179,81 @@ const resetPassword = async (req, res) => {
const matched = await v.check();
if (!matched) {
return res.status(422).json({
status: 422,
error: v.errors
})
status: 422,
error: v.errors
});
}
console.log(req.body.token)
const user = await User.findOne({ tokenResetPassword: req.body.token})
const user = await prisma.user.findFirst({ where: { recovery_token: req.body.token } });
if (user) {
user.password = await bcrypt.hashSync(req.body.password, 10);
user.tokenResetPassword = null;
await user.save()
const hashedPassword = await bcrypt.hash(req.body.password, 10);
await prisma.user.update({
where: { id: user.id },
data: { encrypted_password: hashedPassword, recovery_token: null, updated_at: new Date() }
});
return res.status(200).json({
status: 200,
data: req.body.email
})
});
}

return res.status(403).json({
status: 403,
data: "access forbidden"
})

}
});
};

const updateUser = async (req, res) => {
const v = new Validator(req.body, {
id: 'required',
email: 'email', // optionnel, mais si fourni doit être un email
role: 'string', // optionnel
// Ajoute ici d'autres champs modifiables si besoin
});
const matched = await v.check();
if (!matched) {
return res.status(422).json({
status: 422,
error: v.errors
});
}
// Prépare les données à mettre à jour (on ne met à jour que ce qui est fourni)
const { id, email, role } = req.body;
const dataToUpdate = {};
if (email !== undefined) dataToUpdate.email = email;
if (role !== undefined) dataToUpdate.role = role;
dataToUpdate.updated_at = new Date();

try {
const updatedUser = await prisma.user.update({
where: { id },
data: dataToUpdate
});
const { encrypted_password, ...userWithoutHash } = updatedUser;
return res.status(200).json({
status: 200,
data: userWithoutHash
});
} catch (error) {
return res.status(404).json({
status: 404,
error: "User not found or update failed",
details: error.message
});
}
};

const logout = async (req, res) => {
// Si tu veux juste un logout stateless :
return res.status(200).json({
status: 200,
message: "Logged out. Please delete your token client-side."
});
};

module.exports = {
login,
register,
forgetPassword,
resetPassword,
}
updateUser,
logout,
};
13 changes: 13 additions & 0 deletions controllers/http/DbTestController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const { prisma } = require('../../services/database');

const testDbConnection = async (req, res) => {
try {
// On fait une requête simple sur la table users
const count = await prisma.user.count();
res.status(200).json({ status: 200, message: 'Connexion à la base OK', userCount: count });
} catch (error) {
res.status(500).json({ status: 500, message: 'Erreur de connexion à la base', error: error.message });
}
};

module.exports = { testDbConnection };
Loading