Experimental news notification system + fix Eightpieces variant
[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 setNewsRead: function(uid)
79 {
80 db.serialize(function() {
81 const query =
82 "UPDATE Users " +
83 "SET newsRead = " + Date.now() + " " +
84 "WHERE id = " + uid;
85 db.run(query);
86 });
87 },
88
89 // Set session token only if empty (first login)
90 // NOTE: weaker security (but avoid to re-login everywhere after each logout)
91 // TODO: option would be to reset all tokens periodically, e.g. every 3 months
92 trySetSessionToken: function(uid, cb)
93 {
94 db.serialize(function() {
95 let query =
96 "SELECT sessionToken " +
97 "FROM Users " +
98 "WHERE id = " + uid;
99 db.get(query, (err,ret) => {
100 const token = ret.sessionToken || genToken(params.token.length);
101 query =
102 "UPDATE Users " +
103 // Also empty the login token to invalidate future attempts
104 "SET loginToken = NULL" +
105 (!ret.sessionToken ? (", sessionToken = '" + token + "'") : "") + " " +
106 "WHERE id = " + uid;
107 db.run(query);
108 cb(token);
109 });
110 });
111 },
112
113 updateSettings: function(user)
114 {
115 db.serialize(function() {
116 const query =
117 "UPDATE Users " +
118 "SET name = '" + user.name + "'" +
119 ", email = '" + user.email + "'" +
120 ", notify = " + user.notify + " " +
121 "WHERE id = " + user.id;
122 db.run(query);
123 });
124 },
125
126 /////////////////
127 // NOTIFICATIONS
128
129 notify: function(user, message)
130 {
131 const subject = "vchess.club - notification";
132 const body = "Hello " + user.name + " !" + `
133 ` + message;
134 sendEmail(params.mail.noreply, user.email, subject, body);
135 },
136
137 tryNotify: function(id, message)
138 {
139 UserModel.getOne("id", id, (err,user) => {
140 if (!err && user.notify)
141 UserModel.notify(user, message);
142 });
143 },
144
145 ////////////
146 // CLEANING
147
148 cleanUsersDb: function()
149 {
150 const tsNow = Date.now();
151 // 86400000 = 24 hours in milliseconds
152 const day = 86400000;
153 db.serialize(function() {
154 const query =
155 "SELECT id, sessionToken, created, name, email " +
156 "FROM Users";
157 db.all(query, (err, users) => {
158 users.forEach(u => {
159 // Remove unlogged users for > 24h
160 if (!u.sessionToken && tsNow - u.created > day)
161 {
162 notify(
163 u,
164 "Your account has been deleted because " +
165 "you didn't log in for 24h after registration"
166 );
167 db.run("DELETE FROM Users WHERE id = " + u.id);
168 }
169 });
170 });
171 });
172 },
173 }
174
175 module.exports = UserModel;