Browse Source

Ajout possibilité inscription simplifiée (uniquement email). Déplacement fonction JS générant des mots de passe dans le backend.

master
Fabrice PENHOËT 11 months ago
parent
commit
b3312e9cf8
  1. 19
      controllers/user.js
  2. 89
      front/src/groupElement.js
  3. 4
      front/src/manageUsers.js
  4. 3
      front/src/subscribe.js
  5. 19
      front/src/tools/users.js
  6. 19
      tools/main.js
  7. 2
      views/wikilerni/quiz-element.pug
  8. 2
      views/wikilerni/quiz-group.pug

19
controllers/user.js

@ -61,6 +61,8 @@ exports.getGodfatherId = async (req, res, next) =>
}
}
// Contrôleur traitant les données envoyées pour une inscription
// Il peut n'y avoir qu'une adresse e-mail ou des données plus complètes (pseudo, parrain, etc.) dès l'inscription
exports.signup = async (req, res, next) =>
{
try
@ -68,10 +70,24 @@ exports.signup = async (req, res, next) =>
const db = require("../models/index");
if(req.body.cguOk !== "true")
res.status(400).json({ errors: [txt.needUGCOk] });
else if(req.body.password.length < config.passwordMinLength) // si le champ est supprimé du formulaire d'inscription, générer un mot de passe ok ici
else if((req.body.password !== undefined) && (req.body.password.length < config.passwordMinLength))
res.status(400).json({ errors: [txt.needLongPassWord.replace("MIN_LENGTH", config.passwordMinLength)] });
else if(tool.isEmpty(req.body.email))
res.status(400).json({ errors: [txt.needEmail] });
else
{
// Dans le cas d'une inscription simplifiée, seule l'adresse e-mail est demandée :
if(tool.isEmpty(req.body.password))
req.body.password=tool.getPassword(8, 12);
req.body.password=await bcrypt.hash(req.body.password, config.bcryptSaltRounds);
// Si pas de pseudo envoyé, on utilise la partie de l'e-mail précédant le "@"
if(tool.isEmpty(req.body.name))
{
const lastIndex=req.body.email.indexOf("@");
if(lastIndex === -1)
lastIndex=1;
req.body.name=req.body.email.substring(0,lastIndex);
}
req.body.GodfatherId=null;
if(req.body.codeGodfather!=="")
{
@ -79,7 +95,6 @@ exports.signup = async (req, res, next) =>
if(godfather)
req.body.GodfatherId=godfather.id;
}
req.body.password=await bcrypt.hash(req.body.password, config.bcryptSaltRounds);
const user=await db["User"].create({ ...req.body }, { fields: ["name", "email", "password", "newsletterOk", "GodfatherId", "timeDifference"] });
req.body.UserId=user.id;
// si l'utilisateur a répondu à un quiz avant de créer son compte, on enregistre son résultats.

89
front/src/groupElement.js

@ -1,51 +1,42 @@
// -- GESTION DU FORMULAIRE PERMETTANT DE CRÉER SON COMPTE
// -- PAGE AFFICHANT L'ÉLÉMENT D'UN GROUPE DE QUIZ ET PROPOSANT DE CRÉER SON COMPTE DE MANIÈRE SIMPLIFIÉE
/// L'utilisateur peut avoir répondu à un quiz avant d'arriver sur la page d'inscription
/// Des ce cas il faut enregistrer son résultat en même temps que les informations de son compte
/// L'utilisateur peut avoir répondu à un quiz avant de lancer la création de son compte
/// Dans ce cas il faut enregistrer son résultat en même temps que les informations de son compte
// Fichier de configuration tirés du backend :
import { apiUrl, availableLangs, theme } from "../../config/instance.js";
const lang=availableLangs[0];
const configTemplate = require("../../views/"+theme+"/config/"+lang+".js");// besoin de toutes les déclarations, car appel dynamique : configTemplate[homePage]
const configUsers = require("../../config/users");// idem pour configurer formulaire
// Importation des fonctions utiles au script :
import { getLocaly, removeLocaly, saveLocaly } from "./tools/clientstorage.js";
import { getLocaly, removeLocaly } from "./tools/clientstorage.js";
import { addElement } from "./tools/dom.js";
import { helloDev } from "./tools/everywhere.js";
import { helloDev, updateAccountLink } from "./tools/everywhere.js";
import { getDatasFromInputs, setAttributesToInputs } from "./tools/forms.js";
import { loadMatomo } from "./tools/matomo.js";
import { checkAnswerDatas, checkSession, getPassword, getTimeDifference } from "./tools/users.js";
import { checkAnswerDatas, checkSession, getTimeDifference } from "./tools/users.js";
// Dictionnaires :
const { notRequired, serverError } = require("../../lang/"+lang+"/general");
const { alreadyConnected, godfatherFound, godfatherNotFound, needUniqueEmail, passwordCopied } = require("../../lang/"+lang+"/user");
const { serverError } = require("../../lang/"+lang+"/general");
const { needUniqueEmail } = require("../../lang/"+lang+"/user");
// Principaux éléments du DOM manipulés :
const myForm=document.getElementById("subscription");
const btnSubmit=document.getElementById("submitDatas");
const divResponse=document.getElementById("response");
const passwordInput=document.getElementById("password");
const passwordLink=document.getElementById("getPassword");
const passwordHelp=document.getElementById("passwordMessage");
const emailInput=document.getElementById("email");
const btnSubmit=document.getElementById("submitDatas");
const codeGodfatherInput=document.getElementById("codeGodfather");
helloDev();
const myForm=document.getElementById("subscription");
// Test de connexion de l'utilisateur + affichage formulaire d'inscription.
// Test de connexion de l'utilisateur + affichage formulaire d'inscription :
const initialise = async () =>
{
try
{
const isConnected=await checkSession();
let isConnected=await checkSession(), user;
if(isConnected)
{
//saveLocaly("message", { message: alreadyConnected, color:"info" });// pour l'afficher sur la page suivante
//const user=getLocaly("user", true);
//const homePage=user.status+"HomePage";
//window.location.assign("/"+configTemplate[homePage]);
user=getLocaly("user", true);
updateAccountLink(user.status, configTemplate);// lien vers le compte adapté pour les utilisateurs connectés
}
else
{
@ -61,18 +52,7 @@ const initialise = async () =>
}
}
initialise();
// Générateur de mot de passe "aléatoire"
passwordLink.addEventListener("click", function(e)
{
e.preventDefault();
passwordInput.type="text";
passwordInput.value=getPassword(8, 12);
// Copie du mot de passe généré dans le "presse-papier" de l'ordinateur :
passwordInput.select();
document.execCommand("copy");
addElement(passwordHelp, "div", passwordCopied, "", ["success"]);
});
helloDev();
// Test si l'e-mail saisi est déjà utilisé par un autre compte.
// Si c'est le cas, la validation du formulaire est bloquée.
@ -92,7 +72,7 @@ emailInput.addEventListener("blur", function(e)
if (this.readyState == XMLHttpRequest.DONE)
{
let response=JSON.parse(this.responseText);
if (this.status === 200 && response.free!==undefined && response.free === false)
if (this.status === 200 && response.free !== undefined && response.free === false)
{
addElement(document.getElementById("emailMessage"), "div", needUniqueEmail.replace("#URL", configTemplate.connectionPage), "", ["error"]);
btnSubmit.setAttribute("disabled", true);
@ -107,34 +87,6 @@ emailInput.addEventListener("blur", function(e)
}
});
// Vérification que le code/e-mail de parrainage saisi est valide.
codeGodfatherInput.addEventListener("focus", function(e)
{ // on efface l'éventuel message d'erreur si on revient sur le champ pour tester un autre code.
addElement(document.getElementById("codeGodfatherMessage"), "i", notRequired);
});
codeGodfatherInput.addEventListener("blur", function(e)
{
const codeValue=codeGodfatherInput.value.trim();
if(codeValue!=="")
{
const xhr = new XMLHttpRequest();
xhr.open("POST", apiUrl+configUsers.userRoutes+configUsers.getGodfatherRoute);
xhr.onreadystatechange = function()
{
if (this.readyState == XMLHttpRequest.DONE)
{
if (this.status === 204)
addElement(document.getElementById("codeGodfatherMessage"), "div", godfatherNotFound, "", ["error"]);
else
addElement(document.getElementById("codeGodfatherMessage"), "div", godfatherFound, "", ["success"]);
}
}
xhr.setRequestHeader("Content-Type", "application/json");
const datas={ codeTest:codeValue };
xhr.send(JSON.stringify(datas));
}
});
// Traitement de l'envoi des données d'inscription :
myForm.addEventListener("submit", function(e)
{
@ -152,11 +104,14 @@ myForm.addEventListener("submit", function(e)
{
myForm.style.display="none";
addElement(divResponse, "p", response.message, "", ["success"]);
removeLocaly("lastAnswer");// ! important pour ne pas enregister plusieurs fois le résultat.
removeLocaly("lastAnswer");// !! important, pour ne pas enregister plusieurs fois le résultat.
}
else if (response.errors)
{
response.errors = response.errors.join("<br>");
if(Array.isArray(response.errors))
response.errors = response.errors.join("<br>");
else
response.errors = serverError;
addElement(divResponse, "p", response.errors, "", ["error"]);
}
else
@ -168,7 +123,7 @@ myForm.addEventListener("submit", function(e)
if(datas)
{
datas.timeDifference=getTimeDifference(configUsers);
// si l'utilisateur a précédement répondu à un quiz, j'ajoute les infos de son résultat :
// si l'utilisateur a précédement répondu à un quiz, on ajoute les données de son résultat :
datas=checkAnswerDatas(datas);
xhr.send(JSON.stringify(datas));
}

4
front/src/manageUsers.js

@ -20,9 +20,9 @@ import { getLocaly, removeLocaly } from "./tools/clientstorage.js";
import { addElement } from "./tools/dom.js";
import { helloDev, updateAccountLink } from "./tools/everywhere.js";
import { empyForm, getDatasFromInputs, setAttributesToInputs } from "./tools/forms.js";
import { dateFormat, isEmpty, replaceAll } from "../../tools/main";
import { dateFormat, getPassword, isEmpty, replaceAll } from "../../tools/main";
import { getUrlParams } from "./tools/url.js";
import { checkSession, getPassword } from "./tools/users.js";
import { checkSession } from "./tools/users.js";
// Dictionnaires :
const { addOkMessage, serverError } = require("../../lang/"+lang+"/general");

3
front/src/subscribe.js

@ -15,8 +15,9 @@ import { getLocaly, removeLocaly, saveLocaly } from "./tools/clientstorage.js";
import { addElement } from "./tools/dom.js";
import { helloDev } from "./tools/everywhere.js";
import { getDatasFromInputs, setAttributesToInputs } from "./tools/forms.js";
import { getPassword } from "../../tools/main";
import { loadMatomo } from "./tools/matomo.js";
import { checkAnswerDatas, checkSession, getPassword, getTimeDifference } from "./tools/users.js";
import { checkAnswerDatas, checkSession, getTimeDifference } from "./tools/users.js";
// Dictionnaires :
const { notRequired, serverError } = require("../../lang/"+lang+"/general");

19
front/src/tools/users.js

@ -18,25 +18,6 @@ export const getTimeDifference = () =>
return timeLocal;
}
// On enlève volontairement les 0/O pour éviter les confusions !
// Et mieux vaut aussi débuter et finir par une lettre simple.
export const getPassword = (nbCarMin, nbCarMax) =>
{
const nbCar=nbCarMin+Math.floor(Math.random()*(nbCarMax-nbCarMin));
const letters="ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnpqrstuvwxyz";
const others="123456789!?.*-_%@&ÉÀÈÙ€$ÂÊÛÎ";
let password=letters[Math.floor(Math.random()*letters.length)];
for(let i=1;i<(nbCar-1);i++)
{
if((i % 2) ===1)
password+=others[Math.floor(Math.random()*others.length)];
else
password+=letters[Math.floor(Math.random()*letters.length)];
}
password+=letters[Math.floor(Math.random()*letters.length)];
return password;
}
// J'utilise le stockage local du navigateur pour enregistrer les données permettant de reconnaître l'utilisateur par la suite
// Seul le serveur pourra vérifier que les identifiants sont (toujours) valides.
export const setSession = (userId, token, durationTS) =>

19
tools/main.js

@ -76,6 +76,25 @@ class Tool
else
return myMounth+"/"+myDay+"/"+myYear;
}
// On enlève volontairement les 0/O pour éviter les confusions !
// Et mieux vaut aussi débuter et finir par une lettre simple.
static getPassword (nbCarMin, nbCarMax)
{
const nbCar=nbCarMin+Math.floor(Math.random()*(nbCarMax-nbCarMin));
const letters="ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnpqrstuvwxyz";
const others="123456789!?.*-_%@&ÉÀÈÙ€$ÂÊÛÎ";
let password=letters[Math.floor(Math.random()*letters.length)];
for(let i=1;i<(nbCar-1);i++)
{
if((i % 2) ===1)
password+=others[Math.floor(Math.random()*others.length)];
else
password+=letters[Math.floor(Math.random()*letters.length)];
}
password+=letters[Math.floor(Math.random()*letters.length)];
return password;
}
}
module.exports = Tool;

2
views/wikilerni/quiz-element.pug

@ -85,7 +85,7 @@ block content
strong #{configTpl.noJSNotification}
- const cguOkLabel = txtUser.formsCGUOkLabel.replace("#link", "/"+configTpl.cguPage); /// remettre class="needJS" au formulaire ci-dessous
div#quizElementSignupForm
form(id="subscription" method="POST")
form(id="subscription" method="POST" class="needJS")
h3 #{configTpl.quizElementSubcriptionFormTitle}
fieldset
label(for="email") #{txtUser.formsEmailLabel}

2
views/wikilerni/quiz-group.pug

@ -58,7 +58,7 @@ block content
div
strong #{configTpl.noJSNotification}
// à cacher si pas de JS !
form(id="group" method="POST")
form(id="group" method="POST" class="needJS")
h2 #{group.Group.title}
div#response
div(class="subscribeBtns")

Loading…
Cancel
Save