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

This commit is contained in:
Fabrice PENHOËT 2020-10-20 16:41:34 +02:00
parent 9f795e8b63
commit b3312e9cf8
8 changed files with 65 additions and 94 deletions

View File

@ -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.

View File

@ -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 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");
const divResponse=document.getElementById("response");
const emailInput=document.getElementById("email");
const myForm=document.getElementById("subscription");
helloDev();
// 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));
}

View File

@ -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");

View File

@ -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");

View File

@ -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) =>

View File

@ -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;

View File

@ -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}

View File

@ -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")