X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=server%2Fmodels%2FGame.js;h=2e3303115b5527164264a326e4e1a579832c747b;hb=0baae6b9fd3536d674f35c704bd5571362798b4c;hp=27f5d1e3aede14f16f1f610161ccb8bd8d3ead9e;hpb=89021f181ac0689bbc785ce0ebd9a910e66352b0;p=vchess.git diff --git a/server/models/Game.js b/server/models/Game.js index 27f5d1e3..2e330311 100644 --- a/server/models/Game.js +++ b/server/models/Game.js @@ -1,4 +1,4 @@ -var db = require("../utils/database"); +const db = require("../utils/database"); const UserModel = require("./User"); /* @@ -34,17 +34,13 @@ const UserModel = require("./User"); const GameModel = { checkGameInfo: function(g) { - if (!g.vid.toString().match(/^[0-9]+$/)) - return "Wrong variant ID"; - if (!g.cadence.match(/^[0-9dhms +]+$/)) - return "Wrong characters in time control"; - if (!g.fen.match(/^[a-zA-Z0-9, /-]*$/)) - return "Bad FEN string"; - if (g.players.length != 2) - return "Need exactly 2 players"; - if (g.players.some(p => !p.id.toString().match(/^[0-9]+$/))) - return "Wrong characters in player ID"; - return ""; + return ( + g.vid.toString().match(/^[0-9]+$/) && + g.cadence.match(/^[0-9dhms +]+$/) && + g.fen.match(/^[a-zA-Z0-9, /-]*$/) && + g.players.length == 2 && + g.players.every(p => p.id.toString().match(/^[0-9]+$/)) + ); }, create: function(vid, fen, cadence, players, cb) @@ -56,16 +52,19 @@ const GameModel = "VALUES " + "(" + vid + ",'" + fen + "','" + fen + "','*','" + cadence + "'," + Date.now() + ",'')"; db.run(query, function(err) { - if (!!err) - return cb(err); - players.forEach((p,idx) => { - const color = (idx==0 ? "w" : "b"); - query = - "INSERT INTO Players VALUES " + - "(" + this.lastID + "," + p.id + ",'" + color + "')"; - db.run(query); - }); - cb(null, {gid: this.lastID}); + if (err) + cb(err) + else + { + players.forEach((p,idx) => { + const color = (idx==0 ? "w" : "b"); + query = + "INSERT INTO Players VALUES " + + "(" + this.lastID + "," + p.id + ",'" + color + "')"; + db.run(query); + }); + cb(null, {gid: this.lastID}); + } }); }); }, @@ -73,19 +72,16 @@ const GameModel = // TODO: some queries here could be async getOne: function(id, light, cb) { + // NOTE: ignoring errors (shouldn't happen at this stage) db.serialize(function() { let query = - // NOTE: g.scoreMsg can be NULL - // (in this case score = "*" and no reason to look at it) - "SELECT g.id, g.vid, g.fen, g.fenStart, g.cadence, g.score, " + + "SELECT g.id, g.vid, g.fen, g.fenStart, g.cadence, g.created, g.score, " + "g.scoreMsg, g.drawOffer, v.name AS vname " + "FROM Games g " + "JOIN Variants v " + " ON g.vid = v.id " + "WHERE g.id = " + id; db.get(query, (err,gameInfo) => { - if (!!err) - return cb(err); query = "SELECT p.uid, p.color, u.name " + "FROM Players p " + @@ -93,41 +89,46 @@ const GameModel = " ON p.uid = u.id " + "WHERE p.gid = " + id; db.all(query, (err2,players) => { - if (!!err2) - return cb(err2); if (light) { - const game = Object.assign({}, - gameInfo, - {players: players} - ); - return cb(null, game); - } - query = - "SELECT squares, played, idx " + - "FROM Moves " + - "WHERE gid = " + id; - db.all(query, (err3,moves) => { - if (!!err3) - return cb(err3); query = - "SELECT msg, name, added " + - "FROM Chats " + + "SELECT COUNT(*) AS nbMoves " + + "FROM Moves " + "WHERE gid = " + id; - db.all(query, (err4,chats) => { - if (!!err4) - return cb(err4); + db.get(query, (err,ret) => { const game = Object.assign({}, gameInfo, - { - players: players, - moves: moves, - chats: chats, - } + {players: players}, + {movesCount: ret.nbMoves} ); - return cb(null, game); + cb(null, game); }); - }); + } + else + { + // Full game requested: + query = + "SELECT squares, played, idx " + + "FROM Moves " + + "WHERE gid = " + id; + db.all(query, (err3,moves) => { + query = + "SELECT msg, name, added " + + "FROM Chats " + + "WHERE gid = " + id; + db.all(query, (err4,chats) => { + const game = Object.assign({}, + gameInfo, + { + players: players, + moves: moves, + chats: chats, + } + ); + cb(null, game); + }); + }); + } }); }); }); @@ -137,26 +138,41 @@ const GameModel = getByUser: function(uid, excluded, cb) { db.serialize(function() { - const query = - "SELECT DISTINCT gid " + - "FROM Players " + - "WHERE uid " + (excluded ? "<>" : "=") + " " + uid; + let query = ""; + if (uid == 0) + { + // Special case anonymous user: show all games + query = + "SELECT id AS gid " + + "FROM Games"; + } + else + { + // Registered user: + query = + "SELECT gid " + + "FROM Players " + + "GROUP BY gid " + + "HAVING COUNT(uid = " + uid + " OR NULL) " + + (excluded ? " = 0" : " > 0"); + } db.all(query, (err,gameIds) => { - if (!!err) - return cb(err); - if (gameIds.length == 0) - return cb(null, []); - let gameArray = []; - for (let i=0; i { - if (!!err2) - return cb(err2); - gameArray.push(game); - // Call callback function only when gameArray is complete: - if (i == gameIds.length - 1) - return cb(null, gameArray); - }); + let gameArray = []; + let gCounter = 0; + for (let i=0; i { + gameArray.push(game); + gCounter++; //TODO: let's hope this is atomic?! + // Call callback function only when gameArray is complete: + if (gCounter == gameIds.length) + cb(null, gameArray); + }); + } } }); }); @@ -166,7 +182,7 @@ const GameModel = { db.serialize(function() { const query = - "SELECT id " + + "SELECT uid " + "FROM Players " + "WHERE gid = " + id; db.all(query, (err,players) => { @@ -178,49 +194,47 @@ const GameModel = checkGameUpdate: function(obj) { // Check all that is possible (required) in obj: - if (!!obj.move) - { - if (!obj.move.played.toString().match(/^[0-9]+$/)) - return "Wrong move played time"; - if (!obj.move.idx.toString().match(/^[0-9]+$/)) - return "Wrong move index"; - } - if (!!obj.drawOffer && !obj.drawOffer.match(/^[wbtn]$/)) - return "Wrong draw offer format"; - if (!!obj.fen && !obj.fen.match(/^[a-zA-Z0-9, /-]*$/)) - return "Wrong FEN string"; - if (!!obj.score && !obj.score.match(/^[012?*\/-]+$/)) - return "Wrong characters in score"; - if (!!obj.scoreMsg && !obj.scoreMsg.match(/^[a-zA-Z ]+$/)) - return "Wrong characters in score message"; - if (!!obj.chat) - return UserModel.checkNameEmail({name: obj.chat.name}); - return ""; + return ( + ( + !obj.move || ( + obj.move.played.toString().match(/^[0-9]+$/) && + obj.move.idx.toString().match(/^[0-9]+$/) + ) + ) && ( + !obj.drawOffer || obj.drawOffer.match(/^[wbtn]$/) + ) && ( + !obj.fen || obj.fen.match(/^[a-zA-Z0-9, /-]*$/) + ) && ( + !obj.score || obj.score.match(/^[012?*\/-]+$/) + ) && ( + !obj.scoreMsg || obj.scoreMsg.match(/^[a-zA-Z ]+$/) + ) && ( + !obj.chat || UserModel.checkNameEmail({name: obj.chat.name}) + ) + ); }, - // obj can have fields move, chat, fen, drawOffer and/or score - update: function(id, obj) + // obj can have fields move, chat, fen, drawOffer and/or score + message + update: function(id, obj, cb) { db.parallelize(function() { let query = "UPDATE Games " + "SET "; let modifs = ""; - if (!!obj.message) - modifs += "message = message || ' ' || '" + obj.message + "',"; // NOTE: if drawOffer is set, we should check that it's player's turn // A bit overcomplicated. Let's trust the client on that for now... - if (!!obj.drawOffer) + if (obj.drawOffer) { if (obj.drawOffer == "n") //Special "None" update obj.drawOffer = ""; modifs += "drawOffer = '" + obj.drawOffer + "',"; } - if (!!obj.fen) + if (obj.fen) modifs += "fen = '" + obj.fen + "',"; - if (!!obj.score) + if (obj.score) modifs += "score = '" + obj.score + "',"; - if (!!obj.scoreMsg) + if (obj.scoreMsg) modifs += "scoreMsg = '" + obj.scoreMsg + "',"; modifs = modifs.slice(0,-1); //remove last comma if (modifs.length > 0) @@ -228,21 +242,51 @@ const GameModel = query += modifs + " WHERE id = " + id; db.run(query); } - if (!!obj.move) + + +return cb({errmsg: JSON.stringify(obj.move)}); + + + // NOTE: move, chat and delchat are mutually exclusive + if (obj.move) { - const m = obj.move; + // Security: only update moves if index is right query = - "INSERT INTO Moves (gid, squares, played, idx) VALUES " + - "(" + id + ",?," + m.played + "," + m.idx + ")"; - db.run(query, JSON.stringify(m.squares)); + "SELECT MAX(idx) AS maxIdx " + + "FROM Moves " + + "WHERE gid = " + id; + db.get(query, (err,ret) => { + const m = obj.move; + +return cb({errmsg: ret.maxIdx + " " + m.idx + " " + (!ret.maxIdx || ret.maxIdx + 1 == m.idx) + " " + query}); + + + if (!ret.maxIdx || ret.maxIdx + 1 == m.idx) { + query = + "INSERT INTO Moves (gid, squares, played, idx) VALUES " + + "(" + id + ",?," + m.played + "," + m.idx + ")"; + db.run(query, JSON.stringify(m.squares)); + cb(null); + } + else cb({errmsg:"Wrong move index"}); + }); } - if (!!obj.chat) + else cb(null); + if (obj.chat) { query = "INSERT INTO Chats (gid, msg, name, added) VALUES (" + id + ",?,'" + obj.chat.name + "'," + Date.now() + ")"; db.run(query, obj.chat.msg); } + else if (obj.delchat) + { + query = + "DELETE " + + "FROM Chats " + + "WHERE gid = " + id; + db.run(query); + } }); }, @@ -275,7 +319,7 @@ const GameModel = const day = 86400000; db.serialize(function() { let query = - "SELECT id,created " + + "SELECT id, created " + "FROM Games "; db.all(query, (err,games) => { games.forEach(g => {