From f0c68a04e31bb6a4b2f8b94a532ef3ca2eebbe3e Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Sun, 1 Mar 2020 18:57:45 +0100
Subject: [PATCH] Fix some translations and error messages

---
 client/src/components/UpsertUser.vue |  2 +-
 client/src/data/userCheck.js         |  6 ++--
 client/src/parameters.js.dist        |  3 ++
 client/src/store.js                  | 42 ++++++++++++++++++----------
 client/src/translations/en.js        |  6 +++-
 client/src/translations/es.js        |  6 +++-
 client/src/translations/fr.js        |  6 +++-
 client/src/utils/ajax.js             |  4 +--
 client/src/views/Auth.vue            | 11 ++++++--
 client/src/views/Logout.vue          | 11 ++++++--
 server/routes/users.js               |  9 ++++--
 server/routes/variants.js            |  2 +-
 server/utils/access.js               |  4 +--
 13 files changed, 80 insertions(+), 32 deletions(-)

diff --git a/client/src/components/UpsertUser.vue b/client/src/components/UpsertUser.vue
index ac15324c..2be637c6 100644
--- a/client/src/components/UpsertUser.vue
+++ b/client/src/components/UpsertUser.vue
@@ -142,7 +142,7 @@ export default {
         case "Login":
           return "Connection token sent. Check your emails!";
         case "Register":
-          return "Registration complete! Please check your emails";
+          return "Registration complete! Please check your emails now";
         case "Update":
           return "Modifications applied!";
       }
diff --git a/client/src/data/userCheck.js b/client/src/data/userCheck.js
index 0dccf1fb..0d3a801b 100644
--- a/client/src/data/userCheck.js
+++ b/client/src/data/userCheck.js
@@ -1,11 +1,11 @@
 export function checkNameEmail(o) {
   if (typeof o.name === "string") {
-    if (o.name.length == 0) return "Empty name";
-    if (!o.name.match(/^[\w]+$/)) return "Name: alphanumerics and underscore";
+    if (o.name.length == 0) return "Missing name";
+    if (!o.name.match(/^[\w-]+$/)) return "Name: alphanumerics, hyphen and underscore";
   }
 
   if (typeof o.email === "string") {
-    if (o.email.length == 0) return "Empty email";
+    if (o.email.length == 0) return "Missing email";
     if (!o.email.match(/^[\w.+-]+@[\w.+-]+$/)) return "Invalid email";
   }
 
diff --git a/client/src/parameters.js.dist b/client/src/parameters.js.dist
index edad3bbd..e96535b0 100644
--- a/client/src/parameters.js.dist
+++ b/client/src/parameters.js.dist
@@ -8,6 +8,9 @@ const Parameters =
 
   // true if the server is at a different address
   cors: true,
+
+  // "include" if the server is at a different address
+  credentials: "same-origin"
 };
 
 export default Parameters;
diff --git a/client/src/store.js b/client/src/store.js
index b81ec552..10ad10e6 100644
--- a/client/src/store.js
+++ b/client/src/store.js
@@ -1,4 +1,5 @@
-import { ajax } from "./utils/ajax";
+// NOTE: do not use ajax() here because ajax.js require the store for translations
+import params from "./parameters"; //for server URL
 import { getRandString } from "./utils/alea";
 
 // Global store: see https://medium.com/fullstackio/managing-state-in-vue-js-23a0352b1c87
@@ -12,8 +13,13 @@ export const store = {
   },
   socketCloseListener: null,
   initialize() {
-    ajax("/variants", "GET", res => {
-      this.state.variants = res.variantArray.sort(
+    fetch(
+      params.serverUrl + "/variants",
+      {method: "GET"},
+    )
+    .then(res => res.json())
+    .then(json => {
+      this.state.variants = json.variantArray.sort(
         (v1,v2) => v1.name.localeCompare(v2.name));
     });
     let mysid = localStorage.getItem("mysid");
@@ -32,25 +38,33 @@ export const store = {
     };
     // Slow verification through the server:
     // NOTE: still superficial identity usurpation possible, but difficult.
-    ajax("/whoami", "GET", res => {
-      this.state.user.id = res.id;
+    fetch(
+      params.serverUrl + "/whoami",
+      {
+        method: "GET",
+        credentials: params.credentials
+      }
+    )
+    .then(res => res.json())
+    .then(json => {
+      this.state.user.id = json.id;
       const storedId = localStorage.getItem("myid");
-      if (res.id > 0 && !storedId)
+      if (json.id > 0 && !storedId)
         // User cleared localStorage
-        localStorage.setItem("myid", res.id);
-      else if (res.id == 0 && !!storedId)
+        localStorage.setItem("myid", json.id);
+      else if (json.id == 0 && !!storedId)
         // User cleared cookie
         localStorage.removeItem("myid");
-      this.state.user.name = res.name;
+      this.state.user.name = json.name;
       const storedName = localStorage.getItem("myname");
-      if (!!res.name && !storedName)
+      if (!!json.name && !storedName)
         // User cleared localStorage
-        localStorage.setItem("myname", res.name);
-      else if (!res.name && !!storedName)
+        localStorage.setItem("myname", json.name);
+      else if (!json.name && !!storedName)
         // User cleared cookie
         localStorage.removeItem("myname");
-      this.state.user.email = res.email;
-      this.state.user.notify = res.notify;
+      this.state.user.email = json.email;
+      this.state.user.notify = json.notify;
     });
     // Settings initialized with values from localStorage
     const getItemDefaultTrue = (item) => {
diff --git a/client/src/translations/en.js b/client/src/translations/en.js
index c8074575..190b4127 100644
--- a/client/src/translations/en.js
+++ b/client/src/translations/en.js
@@ -10,6 +10,7 @@ export const translations = {
   Apply: "Apply",
   "Are you sure?": "Are you sure?",
   "Authentication successful!": "Authentication successful!",
+  "Back to Hall in 3 seconds...": "Back to Hall in 3 seconds...",
   "Back to list": "Back to list",
   "Black to move": "Black to move",
   "Black surrender": "Black surrender",
@@ -36,6 +37,7 @@ export const translations = {
   Email: "Email",
   "Email sent!": "Email sent!",
   "Empty message": "Empty message",
+  "Error: try to delete cookies": "Error: try to delete cookies",
   "Errors in FEN": "Errors in FEN",
   "Example game": "Example game",
   Go: "Go",
@@ -64,7 +66,7 @@ export const translations = {
   "Mutual agreement": "Mutual agreement",
   "My games": "My games",
   "My problems": "My problems",
-  "Name: alphanumerics and underscore": "Name: alphanumerics and underscore",
+  "Name: alphanumerics, hyphen and underscore": "Name: alphanumerics, hyphen and underscore",
   "Name or Email": "Name or Email",
   Next: "Next",
   "New connexion detected: tab now offline": "New connexion detected: tab now offline",
@@ -115,7 +117,9 @@ export const translations = {
   Time: "Time",
   "Undetermined result": "Undetermined result",
   Update: "Update",
+  "User creation failed. Try again": "User creation failed. Try again",
   "User name": "User name",
+  "User name or email already in use": "User name or email already in use",
   Variant: "Variant",
   Variants: "Variants",
   Versus: "Versus",
diff --git a/client/src/translations/es.js b/client/src/translations/es.js
index 3c63ffb7..94b8390d 100644
--- a/client/src/translations/es.js
+++ b/client/src/translations/es.js
@@ -10,6 +10,7 @@ export const translations = {
   Apply: "Aplicar",
   "Are you sure?": "¿Está usted seguro?",
   "Authentication successful!": "¡Autenticación exitosa!",
+  "Back to Hall in 3 seconds...": "Regreso al salón en 3 segundos...",
   "Back to list": "Volver a la lista",
   "Black to move": "Juegan las negras",
   "Black surrender": "Las negras abandonan",
@@ -36,6 +37,7 @@ export const translations = {
   Email: "Email",
   "Email sent!": "¡Email enviado!",
   "Empty message": "Mensaje vacio",
+  "Error: try to delete cookies": "Error: intente eliminar las cookies",
   "Errors in FEN": "FEN errónea",
   "Example game": "Ejemplo de partida",
   Go: "Go",
@@ -64,7 +66,7 @@ export const translations = {
   "Mutual agreement": "Acuerdo mutuo",
   "My games": "Mis partidas",
   "My problems": "Mis problemas",
-  "Name: alphanumerics and underscore": "Nombre: alfanuméricos y underscore",
+  "Name: alphanumerics, hyphen and underscore": "Nombre: alfanuméricos, guión y underscore",
   "Name or Email": "Nombre o Email",
   Next: "Próximo",
   "New connexion detected: tab now offline": "Nueva conexión detectada: pestaña ahora desconectada",
@@ -115,7 +117,9 @@ export const translations = {
   Time: "Tiempo",
   "Undetermined result": "Resultado indeterminado",
   Update: "Actualización",
+  "User creation failed. Try again": "Error al crear cuenta. inténtelo de nuevo",
   "User name": "Nombre de usuario",
+  "User name or email already in use": "Nombre de usuario o correo electrónico ya en uso",
   Variant: "Variante",
   Variants: "Variantes",
   Versus: "Contra",
diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js
index c7246814..c5d82e92 100644
--- a/client/src/translations/fr.js
+++ b/client/src/translations/fr.js
@@ -10,6 +10,7 @@ export const translations = {
   Apply: "Appliquer",
   "Authentication successful!": "Authentification réussie !",
   "Are you sure?": "Étes vous sûr?",
+  "Back to Hall in 3 seconds...": "Retour au Hall dans 3 secondes...",
   "Back to list": "Retour à la liste",
   "Black to move": "Trait aux noirs",
   "Black surrender": "Les noirs abandonnent",
@@ -36,6 +37,7 @@ export const translations = {
   Email: "Email",
   "Email sent!": "Email envoyé !",
   "Empty message": "Message vide",
+  "Error: try to delete cookies": "Erreur : essayez de supprimer les cookies",
   "Errors in FEN": "FEN erronée",
   "Example game": "Partie exemple",
   Go: "Go",
@@ -64,7 +66,7 @@ export const translations = {
   "Mutual agreement": "Accord mutuel",
   "My games": "Mes parties",
   "My problems": "Mes problèmes",
-  "Name: alphanumerics and underscore": "Nom: alphanumériques et underscore",
+  "Name: alphanumerics, hyphen and underscore": "Nom: alphanumériques, tiret et underscore",
   "Name or Email": "Nom ou Email",
   Next: "Suivant",
   "New connexion detected: tab now offline": "Nouvelle connexion détectée : onglet désormais hors ligne",
@@ -115,7 +117,9 @@ export const translations = {
   Time: "Temps",
   "Undetermined result": "Résultat indéterminé",
   Update: "Mise à jour",
+  "User creation failed. Try again": "Échec de la création du compte. Réessayez",
   "User name": "Nom d'utilisateur",
+  "User name or email already in use": "Nom d'utilisateur ou email déjà utilisés",
   Variant: "Variante",
   Variants: "Variantes",
   Versus: "Contre",
diff --git a/client/src/utils/ajax.js b/client/src/utils/ajax.js
index e539f468..4573dd85 100644
--- a/client/src/utils/ajax.js
+++ b/client/src/utils/ajax.js
@@ -1,8 +1,8 @@
 import params from "../parameters"; //for server URL
+import { store } from "../store"; //for translations
 
 // TODO: replace by fetch API ?
 // https://www.sitepoint.com/xmlhttprequest-vs-the-fetch-api-whats-best-for-ajax-in-2019/
-// Problem: fetch() does not set req.xhr... see access/ajax() security especially for /whoami
 
 // From JSON (encoded string values!) to "arg1=...&arg2=..."
 function toQueryString(data) {
@@ -25,7 +25,7 @@ export function ajax(url, method, data, success, error) {
   if (!success) success = () => {}; //by default, do nothing
   if (!error)
     error = errmsg => {
-      alert(errmsg);
+      alert(store.state.tr[errmsg] || errmsg);
     };
   xhr.onreadystatechange = function() {
     if (this.readyState == 4 && this.status == 200) {
diff --git a/client/src/views/Auth.vue b/client/src/views/Auth.vue
index 36e79c89..7d682a0e 100644
--- a/client/src/views/Auth.vue
+++ b/client/src/views/Auth.vue
@@ -2,7 +2,9 @@
 main
   .row
     .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
-      p {{ st.tr["Authentication successful!"] }}
+      div(v-if="authOk")
+        p {{ st.tr["Authentication successful!"] }}
+        p {{ st.tr["Back to Hall in 3 seconds..."] }}
 </template>
 
 <script>
@@ -12,7 +14,8 @@ export default {
   name: "my-auth",
   data: function() {
     return {
-      st: store.state
+      st: store.state,
+      authOk: false
     };
   },
   created: function() {
@@ -21,12 +24,16 @@ export default {
       "GET",
       { token: this.$route.params["token"] },
       res => {
+        this.authOk = true;
         this.st.user.id = res.id;
         this.st.user.name = res.name;
         this.st.user.email = res.email;
         this.st.user.notify = res.notify;
         localStorage["myname"] = res.name;
         localStorage["myid"] = res.id;
+        setTimeout(() => {
+          this.$router.replace("/");
+        }, 3000);
       }
     );
   }
diff --git a/client/src/views/Logout.vue b/client/src/views/Logout.vue
index 62f057c6..1740c7da 100644
--- a/client/src/views/Logout.vue
+++ b/client/src/views/Logout.vue
@@ -2,7 +2,9 @@
 main
   .row
     .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
-      p {{ st.tr["Logout successful!"] }}
+      div(v-if="logoutOk")
+        p {{ st.tr["Logout successful!"] }}
+        p {{ st.tr["Back to Hall in 3 seconds..."] }}
 </template>
 
 <script>
@@ -12,7 +14,8 @@ export default {
   name: "my-logout",
   data: function() {
     return {
-      st: store.state
+      st: store.state,
+      logoutOk: false
     };
   },
   created: function() {
@@ -20,12 +23,16 @@ export default {
       "/logout",
       "GET",
       () => {
+        this.logoutOk = true;
         this.st.user.id = 0;
         this.st.user.name = "";
         this.st.user.email = "";
         this.st.user.notify = false;
         localStorage.removeItem("myid");
         localStorage.removeItem("myname");
+        setTimeout(() => {
+          this.$router.replace("/");
+        }, 3000);
       }
     );
   }
diff --git a/server/routes/users.js b/server/routes/users.js
index f9f1d86f..11966754 100644
--- a/server/routes/users.js
+++ b/server/routes/users.js
@@ -13,7 +13,12 @@ router.post('/register', access.unlogged, access.ajax, (req,res) => {
   {
     UserModel.create(name, email, notify, (err,ret) => {
       if (err)
-        res.json({errmsg: "User creation failed. Try again"});
+      {
+        const msg = err.code == "SQLITE_CONSTRAINT"
+          ? "User name or email already in use"
+          : "User creation failed. Try again";
+        res.json({errmsg: msg});
+      }
       else
       {
         const user = {
@@ -29,7 +34,7 @@ router.post('/register', access.unlogged, access.ajax, (req,res) => {
 });
 
 // NOTE: this method is safe because the sessionToken must be guessed
-router.get("/whoami", access.ajax, (req,res) => {
+router.get("/whoami", (req,res) => {
   const callback = (user) => {
     res.json({
       name: user.name,
diff --git a/server/routes/variants.js b/server/routes/variants.js
index 3f7417e3..57d4a30b 100644
--- a/server/routes/variants.js
+++ b/server/routes/variants.js
@@ -4,7 +4,7 @@ let router = require("express").Router();
 const VariantModel = require("../models/Variant");
 const access = require("../utils/access");
 
-router.get('/variants', access.ajax, function(req, res) {
+router.get('/variants', function(req, res) {
   VariantModel.getAll((err,variants) => {
     res.json(err || {variantArray:variants});
   });
diff --git a/server/utils/access.js b/server/utils/access.js
index df86b756..36511dba 100644
--- a/server/utils/access.js
+++ b/server/utils/access.js
@@ -6,7 +6,7 @@ module.exports =
   logged: function(req, res, next) {
     const callback = () => {
       if (!loggedIn)
-        res.json({errmsg: "Error: please delete cookies and cross fingers"});
+        res.json({errmsg: "Error: try to delete cookies"});
       else next();
     };
     let loggedIn = undefined;
@@ -40,7 +40,7 @@ module.exports =
     // Just a quick heuristic, which should be enough
     const loggedIn = !!req.cookies.token;
     if (loggedIn)
-      res.json({errmsg: "Error: please delete cookies and cross fingers"});
+      res.json({errmsg: "Error: try to delete cookies"});
     else next();
   },
 
-- 
2.44.0