Remove tourneyPath: doesn't make sense without tournament website
[vchess.git] / server / models / User.js
index 9e1aded..cc104ab 100644 (file)
-var db = require("../utils/database");
-var maild = require("../utils/mailer.js");
-var genToken = require("../utils/tokenGenerator");
-var params = require("../config/parameters");
+const db = require("../utils/database");
+const genToken = require("../utils/tokenGenerator");
+const params = require("../config/parameters");
+const sendEmail = require('../utils/mailer');
+const { exec } = require("child_process");
 
 /*
  * Structure:
- *   _id: integer
+ *   id: integer
  *   name: varchar
  *   email: varchar
  *   loginToken: token on server only
  *   loginTime: datetime (validity)
  *   sessionToken: token in cookies for authentication
  *   notify: boolean (send email notifications for corr games)
+ *   created: datetime
+ *   bio: text
  */
 
-const UserModel =
-{
-       checkNameEmail: function(o)
-       {
-               if (typeof o.name === "string")
-               {
-                       if (o.name.length == 0)
-                               return "Empty name";
-                       if (!o.name.match(/^[\w]+$/))
-                               return "Bad characters in name";
-               }
-               if (typeof o.email === "string")
-               {
-                       if (o.email.length == 0)
-                               return "Empty email";
-                       if (!o.email.match(/^[\w.+-]+@[\w.+-]+$/))
-                               return "Bad characters in email";
-               }
-       },
-
-       // NOTE: parameters are already cleaned (in controller), thus no sanitization here
-       create: function(name, email, notify, callback)
-       {
-               db.serialize(function() {
-                       const insertQuery =
-                               "INSERT INTO Users " +
-                               "(name, email, notify) VALUES " +
-                               "('" + name + "', '" + email + "', " + notify + ")";
-                       db.run(insertQuery, err => {
-                               if (!!err)
-                                       return callback(err);
-                               db.get("SELECT last_insert_rowid() AS rowid", callback);
-                       });
-               });
-       },
-
-       // Find one user (by id, name, email, or token)
-       getOne: function(by, value, cb)
-       {
-               const delimiter = (typeof value === "string" ? "'" : "");
-               db.serialize(function() {
-                       const query =
-                               "SELECT * " +
-                               "FROM Users " +
-                               "WHERE " + by + " = " + delimiter + value + delimiter;
-                       db.get(query, cb);
-               });
-       },
-
-  getByName: function(names, cb) {
-               db.serialize(function() {
-                       const query =
-                               "SELECT id " +
+const UserModel = {
+
+  checkNameEmail: function(o) {
+    return (
+      (!o.name || !!(o.name.match(/^[\w-]+$/))) &&
+      (!o.email || !!(o.email.match(/^[\w.+-]+@[\w.+-]+$/)))
+    );
+  },
+
+  create: function(name, email, notify, cb) {
+    db.serialize(function() {
+      const query =
+        "INSERT INTO Users " +
+        "(name, email, notify, created) VALUES " +
+        "('" + name + "','" + email + "'," + notify + "," + Date.now() + ")";
+      db.run(query, function(err) {
+        cb(err, { id: this.lastID });
+      });
+    });
+  },
+
+  // Find one user by id, name, email, or token
+  getOne: function(by, value, fields, cb) {
+    const delimiter = (typeof value === "string" ? "'" : "");
+    db.serialize(function() {
+      const query =
+        "SELECT " + fields + " " +
+        "FROM Users " +
+        "WHERE " + by + " = " + delimiter + value + delimiter;
+      db.get(query, cb);
+    });
+  },
+
+  getByIds: function(ids, cb) {
+    db.serialize(function() {
+      const query =
+        "SELECT id, name " +
+        "FROM Users " +
+        "WHERE id IN (" + ids + ")";
+      db.all(query, cb);
+    });
+  },
+
+  getBio: function(id, cb) {
+    db.serialize(function() {
+      const query =
+        "SELECT bio " +
+        "FROM Users " +
+        "WHERE id = " + id;
+      db.get(query, cb);
+    });
+  },
+
+  /////////
+  // MODIFY
+
+  setLoginToken: function(token, id) {
+    db.serialize(function() {
+      const query =
+        "UPDATE Users " +
+        "SET loginToken = '" + token + "',loginTime = " + Date.now() + " " +
+        "WHERE id = " + id;
+      db.run(query);
+    });
+  },
+
+  setBio: function(id, bio) {
+    db.serialize(function() {
+      const query =
+        "UPDATE Users " +
+        "SET bio = ? " +
+        "WHERE id = " + id;
+      db.run(query, bio);
+    });
+  },
+
+  // Set session token only if empty (first login)
+  // NOTE: weaker security (but avoid to re-login everywhere after each logout)
+  // TODO: option would be to reset all tokens periodically (every 3 months?)
+  trySetSessionToken: function(id, cb) {
+    db.serialize(function() {
+      let query =
+        "SELECT sessionToken " +
         "FROM Users " +
-                               "WHERE name IN ('" + names.join("','") + "')";
-                       db.all(query, cb);
-               });
+        "WHERE id = " + id;
+      db.get(query, (err, ret) => {
+        const token = ret.sessionToken || genToken(params.token.length);
+        const setSessionToken =
+          (!ret.sessionToken ? (", sessionToken = '" + token + "'") : "");
+        query =
+          "UPDATE Users " +
+          // Also empty the login token to invalidate future attempts
+          "SET loginToken = NULL, loginTime = NULL " +
+          setSessionToken + " " +
+          "WHERE id = " + id;
+        db.run(query);
+        cb(token);
+      });
+    });
+  },
+
+  updateSettings: function(user) {
+    db.serialize(function() {
+      const query =
+        "UPDATE Users " +
+        "SET name = '" + user.name + "'" +
+        ", email = '" + user.email + "'" +
+        ", notify = " + user.notify + " " +
+        "WHERE id = " + user.id;
+      db.run(query);
+    });
   },
 
-       /////////
-       // MODIFY
-
-       setLoginToken: function(token, uid, cb)
-       {
-               db.serialize(function() {
-                       const query =
-                               "UPDATE Users " +
-                               "SET loginToken = '" + token + "', loginTime = " + Date.now() + " " +
-                               "WHERE id = " + uid;
-                       db.run(query, cb);
-               });
-       },
-
-       // Set session token only if empty (first login)
-       // TODO: weaker security (but avoid to re-login everywhere after each logout)
-       trySetSessionToken: function(uid, cb)
-       {
-               // Also empty the login token to invalidate future attempts
-               db.serialize(function() {
-                       const querySessionToken =
-                               "SELECT sessionToken " +
-                               "FROM Users " +
-                               "WHERE id = " + uid;
-                       db.get(querySessionToken, (err,ret) => {
-                               if (!!err)
-                                       return cb(err);
-                               const token = ret.sessionToken || genToken(params.token.length);
-                               const queryUpdate =
-                                       "UPDATE Users " +
-                                       "SET loginToken = NULL" +
-                                       (!ret.sessionToken ? (", sessionToken = '" + token + "'") : "") + " " +
-                                       "WHERE id = " + uid;
-                               db.run(queryUpdate);
-                               cb(null, token);
-                       });
-               });
-       },
-
-       updateSettings: function(user, cb)
-       {
-               db.serialize(function() {
-                       const query =
-                               "UPDATE Users " +
-                               "SET name = '" + user.name + "'" +
-                               ", email = '" + user.email + "'" +
-                               ", notify = " + user.notify + " " +
-                               "WHERE id = " + user.id;
-                       db.run(query, cb);
-               });
-       },
-}
+  /////////////////
+  // NOTIFICATIONS
+
+  notify: function(user, message) {
+    const subject = "vchess.club - notification";
+    const body = "Hello " + user.name + " !" + `
+` + message;
+    sendEmail(params.mail.noreply, user.email, subject, body);
+  },
+
+  tryNotify: function(id, message) {
+    UserModel.getOne("id", id, "name, email, notify", (err, user) => {
+      if (!err && user.notify) UserModel.notify(user, message);
+    });
+  },
+
+  ////////////
+  // CLEANING
+
+  cleanUsersDb: function() {
+    const tsNow = Date.now();
+    // 86400000 = 24 hours in milliseconds
+    const day = 86400000;
+    db.serialize(function() {
+      const query =
+        "SELECT id, sessionToken, created, name, email " +
+        "FROM Users";
+      db.all(query, (err, users) => {
+        let toRemove = [];
+        users.forEach(u => {
+          // Remove users unlogged for > 24h
+          if (!u.sessionToken && tsNow - u.created > day)
+          {
+            toRemove.push(u.id);
+            UserModel.notify(
+              u,
+              "Your account has been deleted because " +
+              "you didn't log in for 24h after registration"
+            );
+          }
+        });
+        if (toRemove.length > 0) {
+          const remArg = toRemove.join(",");
+          db.run(
+            "DELETE FROM Users " +
+            "WHERE id IN (" + remArg + ")"
+          );
+        }
+      });
+    });
+  }
+
+};
 
 module.exports = UserModel;