From: Benjamin Auder Date: Wed, 15 Jan 2020 13:45:26 +0000 (+0100) Subject: Drop problems from server + draft DB cleaning functions X-Git-Url: https://git.auder.net/variants/Apocalypse/scripts/current/pieces/doc/DESCRIPTION?a=commitdiff_plain;h=d431028c73d41a22636130bd6aff562762eaf2bb;p=vchess.git Drop problems from server + draft DB cleaning functions --- diff --git a/server/TODO b/server/TODO index bcffc7bd..bd5d7309 100644 --- a/server/TODO +++ b/server/TODO @@ -2,8 +2,3 @@ Later: use http2 https://webapplog.com/http2-node/ https://www.npmjs.com/package/spdy Express 5? - -Finish : - - models - - routes - - sockets diff --git a/server/bin/www b/server/bin/www index e287d503..94f39e13 100755 --- a/server/bin/www +++ b/server/bin/www @@ -21,6 +21,21 @@ app.set('port', port); var server = http.createServer(app); +/* + * CRON tasks + */ + +var cron = require('node-cron'); +var UserModel = require("../models/User"); +var ChallengeModel = require("../models/Challenge"); +var GameModel = require("../models/Game"); +cron.schedule('0 0 0 * * *', function() { + // Remove some old users, challenges and games every 24h + UserModel.cleanUsersDb(); //TODO: write this + ChallengeModel.removeOld(); //TODO: this too + GameModel.cleanGamesDb(); +}); + /** * Listen on provided port, on all network interfaces. */ diff --git a/server/db/create.sql b/server/db/create.sql index 11c3574e..a8c8d687 100644 --- a/server/db/create.sql +++ b/server/db/create.sql @@ -16,18 +16,6 @@ create table Users ( notify boolean ); -create table Problems ( - id integer primary key, - added datetime, - uid integer, - vid integer, - fen varchar, - instructions text, - solution text, - foreign key (uid) references Users(id), - foreign key (vid) references Variants(id) -); - -- All the following tables are for correspondance play only -- (Live games are stored in browser) @@ -51,6 +39,7 @@ create table Games ( fen varchar, --current state score varchar, timeControl varchar, + created datetime, --used only for DB cleaning foreign key (vid) references Variants(id) ); diff --git a/server/models/Challenge.js b/server/models/Challenge.js index f82f9e81..ba805aa1 100644 --- a/server/models/Challenge.js +++ b/server/models/Challenge.js @@ -94,4 +94,22 @@ const ChallengeModel = }, } +// TODO: adapt +// Remove challenges older than 1 month, and 1to1 older than 36h +//exports.removeOld = function() +//{ +// var tsNow = new Date().getTime(); +// // 86400000 = 24 hours in milliseconds +// var day = 86400000; +// db.challenges.find({}, (err,challengeArray) => { +// challengeArray.forEach( c => { +// if (c._id.getTimestamp() + 30*day < tsNow //automatch +// || (!!c.to && c._id.getTimestamp() + 1.5*day < tsNow)) //1 to 1 +// { +// db.challenges.remove({"_id": c._id}); +// } +// }); +// }); +//} + module.exports = ChallengeModel; diff --git a/server/models/Game.js b/server/models/Game.js index 0949fc98..3d2184b8 100644 --- a/server/models/Game.js +++ b/server/models/Game.js @@ -28,8 +28,9 @@ const GameModel = { db.serialize(function() { let query = - "INSERT INTO Games (vid, fenStart, fen, score, timeControl) VALUES " + - "(" + vid + ",'" + fen + "','" + fen + "','*','" + timeControl + "')"; + "INSERT INTO Games (vid, fenStart, fen, score, timeControl, created)" + + " VALUES (" + vid + ",'" + fen + "','" + fen + "','*','" + + timeControl + "'," + Date.now() + ")"; db.run(query, function(err) { if (!!err) return cb(err); @@ -174,6 +175,36 @@ const GameModel = db.run(query); }); }, + + cleanGamesDb: function() + { + const tsNow = Date.now(); + // 86400000 = 24 hours in milliseconds + const day = 86400000; + db.serialize(function() { + let query = + "SELECT id,score " + + "FROM Games "; + db.all(query, (err,games) => { + games.forEach(g => { + query = + "SELECT max(played) AS lastMaj " + + "FROM Moves " + + "WHERE gid = " + g.id; + db.get(query, (err2,updated) { + if (!updated && tsNow - g.created > 7*day) + return GameModel.remove(g.id); + const lastMaj = updated.lastMaj; + if (g.score != "*" && tsNow - lastMaj > 7*day || + g.score == "*" && tsNow - lastMaj > 91*day) + { + GameModel.remove(g.id); + } + }); + }); + }); + }); + }, } module.exports = GameModel; diff --git a/server/models/Problem.js b/server/models/Problem.js deleted file mode 100644 index a1f99031..00000000 --- a/server/models/Problem.js +++ /dev/null @@ -1,81 +0,0 @@ -var db = require("../utils/database"); - -/* - * Structure: - * id: problem number (int) - * uid: user id (int) - * vid: variant id (int) - * added: timestamp - * instructions: text - * solution: text - */ - -const ProblemModel = -{ - create: function(uid, vid, fen, instructions, solution, cb) - { - db.serialize(function() { - const insertQuery = - "INSERT INTO Problems (added, uid, vid, fen, instructions, solution) " + - "VALUES (" + Date.now() + "," + uid + "," + vid + ",'" + fen + "',?,?)"; - db.run(insertQuery, [instructions, solution], err => { - if (!!err) - return cb(err); - db.get("SELECT last_insert_rowid() AS rowid", cb); - }); - }); - }, - - getOne: function(id, callback) - { - db.serialize(function() { - const query = - "SELECT * " + - "FROM Problems " + - "WHERE id = " + id; - db.get(query, callback); - }); - }, - - fetchN: function(vid, uid, type, directionStr, lastDt, MaxNbProblems, callback) - { - db.serialize(function() { - let typeLine = ""; - if (uid > 0) - typeLine = "AND uid " + (type=="others" ? "!=" : "=") + " " + uid; - const query = - "SELECT * FROM Problems " + - "WHERE vid = " + vid + - " AND added " + directionStr + " " + lastDt + " " + typeLine + " " + - "ORDER BY added " + (directionStr=="<" ? "DESC " : "") + - "LIMIT " + MaxNbProblems; - db.all(query, callback); - }); - }, - - // TODO: update fails (but insert is OK) - update: function(id, uid, fen, instructions, solution, cb) - { - db.serialize(function() { - const query = - "UPDATE Problems SET " + - "fen = '" + fen + "', " + - "instructions = ?, " + - "solution = ? " + - "WHERE id = " + id + " AND uid = " + uid; - db.run(query, [instructions,solution], cb); - }); - }, - - remove: function(id, uid) - { - db.serialize(function() { - const query = - "DELETE FROM Problems " + - "WHERE id = " + id + " AND uid = " + uid; - db.run(query); - }); - }, -} - -module.exports = ProblemModel; diff --git a/server/models/User.js b/server/models/User.js index b2a99e05..c2e78837 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -142,4 +142,23 @@ const UserModel = } } +// TODO: adapt +//exports.cleanUsersDb = function() +//{ +// var tsNow = new Date().getTime(); +// // 86400000 = 24 hours in milliseconds +// var day = 86400000; +// +// db.users.find({}, (err,userArray) => { +// userArray.forEach( u => { +// if ((u.sessionTokens.length==0 && +// u._id.getTimestamp().getTime() + day < tsNow) //unlogged +// || u.updated + 365*day < tsNow) //inactive for one year +// { +// db.users.remove({"_id": u._id}); +// } +// }); +// }); +//} + module.exports = UserModel; diff --git a/server/routes/all.js b/server/routes/all.js index fbf3433e..a890e609 100644 --- a/server/routes/all.js +++ b/server/routes/all.js @@ -9,7 +9,6 @@ router.get("/", access.ajax, (req,res) => { router.use("/", require("./challenges")); router.use("/", require("./games")); router.use("/", require("./messages")); -router.use("/", require("./problems")); router.use("/", require("./users")); router.use("/", require("./variants")); diff --git a/server/routes/problems.js b/server/routes/problems.js deleted file mode 100644 index 777543b1..00000000 --- a/server/routes/problems.js +++ /dev/null @@ -1,94 +0,0 @@ -// AJAX methods to get, create, update or delete a problem - -let router = require("express").Router(); -const access = require("../utils/access"); -const ProblemModel = require("../models/Problem"); -const sanitizeHtml = require('sanitize-html'); -const MaxNbProblems = 20; - -function sanitizeUserInput(fen, instructions, solution) -{ - if (!fen.match(/^[a-zA-Z0-9, /-]*$/)) - return "Bad characters in FEN string"; - instructions = sanitizeHtml(instructions); - solution = sanitizeHtml(solution); - if (instructions.length == 0) - return "Empty instructions"; - if (solution.length == 0) - return "Empty solution"; - return { - fen: fen, - instructions: instructions, - solution: solution - }; -} - -// Get one problem (TODO: vid unused, here for URL de-ambiguification) -router.get("/problems/:vid([0-9]+)/:id([0-9]+)", access.ajax, (req,res) => { - const pid = req.params["id"]; - ProblemModel.getOne(pid, (err,problem) => { - if (!!err) - return res.json(err); - return res.json({problem: problem}); - }); -}); - -// Fetch N previous or next problems -router.get("/problems/:vid([0-9]+)", access.ajax, (req,res) => { - const vid = req.params["vid"]; - const directionStr = (req.query.direction == "forward" ? ">" : "<"); - const lastDt = req.query.last_dt; - const type = req.query.type; - if (!lastDt.match(/[0-9]+/)) - return res.json({errmsg: "Bad timestamp"}); - if (!["others","mine"].includes(type)) - return res.json({errmsg: "Bad type"}); - ProblemModel.fetchN(vid, req.userId, type, directionStr, lastDt, MaxNbProblems, - (err,problems) => { - if (!!err) - return res.json(err); - return res.json({problems: problems}); - } - ); -}); - -// Upload a problem (sanitize inputs) -router.post("/problems/:vid([0-9]+)", access.logged, access.ajax, (req,res) => { - const vid = req.params["vid"]; - const s = sanitizeUserInput( - req.body["fen"], req.body["instructions"], req.body["solution"]); - if (typeof s === "string") - return res.json({errmsg: s}); - ProblemModel.create(req.userId, vid, s.fen, s.instructions, s.solution, - (err,pid) => { - if (!!err) - return res.json(err); - res.json({id: pid["rowid"]}); - } - ); -}); - -// Update a problem (also sanitize inputs) -router.put("/problems/:id([0-9]+)", access.logged, access.ajax, (req,res) => { - const pid = req.params["id"]; //problem ID - const s = sanitizeUserInput( - req.body["fen"], req.body["instructions"], req.body["solution"]); - if (typeof s === "string") - return res.json({errmsg: s}); - ProblemModel.update(pid, req.userId, s.fen, s.instructions, s.solution, - err => { - if (!!err) - return res.json(err); - res.json({}); - } - ); -}); - -// Delete a problem -router.delete("/problems/:id([0-9]+)", access.logged, access.ajax, (req,res) => { - const pid = req.params["id"]; //problem ID - ProblemModel.remove(pid, req.userId); - res.json({}); -}); - -module.exports = router;