Draft Hiddenqueen, Grasshopper and Knightmate chess (rules unwritten)
[vchess.git] / server / models / User.js
1 const db = require("../utils/database");
2 const genToken = require("../utils/tokenGenerator");
3 const params = require("../config/parameters");
4 const sendEmail = require('../utils/mailer');
5
6 /*
7 * Structure:
8 * _id: integer
9 * name: varchar
10 * email: varchar
11 * loginToken: token on server only
12 * loginTime: datetime (validity)
13 * sessionToken: token in cookies for authentication
14 * notify: boolean (send email notifications for corr games)
15 * created: datetime
16 */
17
18 const UserModel =
19 {
20 checkNameEmail: function(o)
21 {
22 return (
23 (!o.name || o.name.match(/^[\w]+$/)) &&
24 (!o.email || o.email.match(/^[\w.+-]+@[\w.+-]+$/))
25 );
26 },
27
28 create: function(name, email, notify, cb)
29 {
30 db.serialize(function() {
31 const query =
32 "INSERT INTO Users " +
33 "(name, email, notify, created) VALUES " +
34 "('" + name + "','" + email + "'," + notify + "," + Date.now() + ")";
35 db.run(query, function(err) {
36 cb(err, {uid: this.lastID});
37 });
38 });
39 },
40
41 // Find one user by id, name, email, or token
42 getOne: function(by, value, cb)
43 {
44 const delimiter = (typeof value === "string" ? "'" : "");
45 db.serialize(function() {
46 const query =
47 "SELECT * " +
48 "FROM Users " +
49 "WHERE " + by + " = " + delimiter + value + delimiter;
50 db.get(query, cb);
51 });
52 },
53
54 getByIds: function(ids, cb) {
55 db.serialize(function() {
56 const query =
57 "SELECT id, name " +
58 "FROM Users " +
59 "WHERE id IN (" + ids + ")";
60 db.all(query, cb);
61 });
62 },
63
64 /////////
65 // MODIFY
66
67 setLoginToken: function(token, uid)
68 {
69 db.serialize(function() {
70 const query =
71 "UPDATE Users " +
72 "SET loginToken = '" + token + "',loginTime = " + Date.now() + " " +
73 "WHERE id = " + uid;
74 db.run(query);
75 });
76 },
77
78 // Set session token only if empty (first login)
79 // NOTE: weaker security (but avoid to re-login everywhere after each logout)
80 // TODO: option would be to reset all tokens periodically, e.g. every 3 months
81 trySetSessionToken: function(uid, cb)
82 {
83 db.serialize(function() {
84 let query =
85 "SELECT sessionToken " +
86 "FROM Users " +
87 "WHERE id = " + uid;
88 db.get(query, (err,ret) => {
89 const token = ret.sessionToken || genToken(params.token.length);
90 query =
91 "UPDATE Users " +
92 // Also empty the login token to invalidate future attempts
93 "SET loginToken = NULL" +
94 (!ret.sessionToken ? (", sessionToken = '" + token + "'") : "") + " " +
95 "WHERE id = " + uid;
96 db.run(query);
97 cb(token);
98 });
99 });
100 },
101
102 updateSettings: function(user)
103 {
104 db.serialize(function() {
105 const query =
106 "UPDATE Users " +
107 "SET name = '" + user.name + "'" +
108 ", email = '" + user.email + "'" +
109 ", notify = " + user.notify + " " +
110 "WHERE id = " + user.id;
111 db.run(query);
112 });
113 },
114
115 /////////////////
116 // NOTIFICATIONS
117
118 notify: function(user, message)
119 {
120 const subject = "vchess.club - notification";
121 const body = "Hello " + user.name + "!" + `
122 ` + message;
123 sendEmail(params.mail.noreply, user.email, subject, body);
124 },
125
126 tryNotify: function(id, message)
127 {
128 UserModel.getOne("id", id, (err,user) => {
129 if (!err && user.notify)
130 UserModel.notify(user, message);
131 });
132 },
133
134 ////////////
135 // CLEANING
136
137 cleanUsersDb: function()
138 {
139 const tsNow = Date.now();
140 // 86400000 = 24 hours in milliseconds
141 const day = 86400000;
142 db.serialize(function() {
143 const query =
144 "SELECT id, sessionToken, created, name, email " +
145 "FROM Users";
146 db.all(query, (err, users) => {
147 users.forEach(u => {
148 // Remove unlogged users for > 24h
149 if (!u.sessionToken && tsNow - u.created > day)
150 {
151 notify(
152 u,
153 "Your account has been deleted because " +
154 "you didn't log in for 24h after registration"
155 );
156 db.run("DELETE FROM Users WHERE id = " + u.id);
157 }
158 });
159 });
160 });
161 },
162 }
163
164 module.exports = UserModel;