From: Benjamin Auder Date: Fri, 14 Feb 2020 00:14:52 +0000 (+0100) Subject: Draft of problems section X-Git-Url: https://git.auder.net/%7B%7B%20asset%28%27mixstore/css/static/img/doc/%24%7BgetWhatsApp%28link%29%7D?a=commitdiff_plain;h=89021f181ac0689bbc785ce0ebd9a910e66352b0;p=vchess.git Draft of problems section --- diff --git a/client/src/App.vue b/client/src/App.vue index 4cd1124f..d720b6eb 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -8,7 +8,7 @@ .row .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 // Menu (top of page): - // Left: hall, variants, mygames + // Left: hall, variants, problems, mygames // Right: usermenu, settings, flag nav label.drawer-toggle(for="drawerControl") @@ -20,6 +20,8 @@ | {{ st.tr["Hall"] }} router-link(to="/variants") | {{ st.tr["Variants"] }} + router-link(to="/problems") + | {{ st.tr["Problems"] }} router-link(to="/mygames") | {{ st.tr["My games"] }} #rightMenu @@ -36,8 +38,6 @@ router-link.menuitem(to="/about") {{ st.tr["About"] }} p.clickable(onClick="doClick('modalContact')") | {{ st.tr["Contact"] }} - a.menuitem(href="https://forum.vchess.club") - | {{ st.tr["Forum"] }} + + diff --git a/client/src/views/Rules.vue b/client/src/views/Rules.vue index 5f9d6dd7..d06ae269 100644 --- a/client/src/views/Rules.vue +++ b/client/src/views/Rules.vue @@ -186,48 +186,4 @@ ul:not(.browser-default) ul:not(.browser-default) > li list-style-type: disc - -.light-square-diag - background-color: #e5e5ca - -.dark-square-diag - background-color: #6f8f57 - -// TODO: following is duplicated (Board.vue) -div.board - float: left - height: 0 - display: inline-block - position: relative - -div.board8 - width: 12.5% - padding-bottom: 12.5% - -div.board10 - width: 10% - padding-bottom: 10% - -div.board11 - width: 9.09% - padding-bottom: 9.1% - -img.piece - width: 100% - -img.piece, img.mark-square - max-width: 100% - height: auto - display: block - -img.mark-square - opacity: 0.6 - width: 76% - position: absolute - top: 12% - left: 12% - opacity: .7 - -.in-shadow - filter: brightness(50%) diff --git a/server/db/create.sql b/server/db/create.sql index b7b76c9b..5bf3a3b0 100644 --- a/server/db/create.sql +++ b/server/db/create.sql @@ -17,8 +17,16 @@ create table Users ( notify boolean ); --- All the following tables are for correspondance play only --- (Live games are stored in browser) +create table Problems ( + id integer primary key, + added datetime, + uid integer, + vid integer, + instruction text, + solution text, + foreign key (uid) references Users(id), + foreign key (vid) references Variants(id) +); create table Challenges ( id integer primary key, diff --git a/server/models/Game.js b/server/models/Game.js index 9d700826..27f5d1e3 100644 --- a/server/models/Game.js +++ b/server/models/Game.js @@ -289,7 +289,7 @@ const GameModel = if ((mstats.nbMoves == 0 && tsNow - g.created > 91*day) || (mstats.nbMoves == 1 && tsNow - mstats.lastMaj > 91*day)) { - return GameModel.remove(g.id); + GameModel.remove(g.id); } }); }); diff --git a/server/models/Problem.js b/server/models/Problem.js new file mode 100644 index 00000000..75c2e146 --- /dev/null +++ b/server/models/Problem.js @@ -0,0 +1,106 @@ +var db = require("../utils/database"); + +/* + * Structure: + * id: integer + * added: datetime + * uid: user id (int) + * vid: variant id (int) + * fen: varchar (optional) + * instruction: text + * solution: text + */ + +const ProblemModel = +{ + checkProblem: function(p) + { + if (!p.vid.toString().match(/^[0-9]+$/)) + return "Wrong variant ID"; + if (!p.fen.match(/^[a-zA-Z0-9, /-]*$/)) + return "Bad FEN string"; + return ""; + }, + + create: function(p, cb) + { + db.serialize(function() { + const query = + "INSERT INTO Problems " + + "(added, uid, vid, fen, instruction, solution) " + + "VALUES " + + "(" + Date.now() + "," + p.uid + ",'" + p.fen + "',?,?)"; + db.run(query, p.instruction, p.solution, function(err) { + return cb(err, {pid: this.lastID}); + }); + }); + }, + + getAll: function(cb) + { + db.serialize(function() { + const query = + "SELECT * " + + "FROM Problems"; + db.all(query, (err,problems) => { + return cb(err, problems); + }); + }); + }, + + getOne: function(id, cb) + { + db.serialize(function() { + const query = + "SELECT * " + + "FROM Problems " + + "WHERE id = " + id; + db.get(query, (err,problem) => { + return cb(err, problem); + }); + }); + }, + + update: function(id, prob) + { + db.serialize(function() { + let query = + "UPDATE Problems " + + "SET " + + "vid = " + prob.vid + "," + + "fen = " + prob.fen + "," + + "instruction = " + prob.instruction + "," + + "solution = " + prob.solution + " " + + "WHERE id = " + id; + db.run(query); + }); + }, + + remove: function(id) + { + db.serialize(function() { + const query = + "DELETE FROM Problems " + + "WHERE id = " + id; + db.run(query); + }); + }, + + safeRemove: function(id, uid, cb) + { + db.serialize(function() { + const query = + "SELECT 1 " + + "FROM Problems " + + "WHERE id = " + id + " AND uid = " + uid; + db.get(query, (err,prob) => { + if (!prob) + return cb({errmsg: "Not your problem"}); + ProvlemModel.remove(id); + cb(null); + }); + }); + }, +} + +module.exports = ProblemModel; diff --git a/server/package-lock.json b/server/package-lock.json index 0daef0ab..f84f6174 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -2750,9 +2750,9 @@ }, "dependencies": { "readable-stream": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.5.0.tgz", - "integrity": "sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", diff --git a/server/routes/all.js b/server/routes/all.js index a3eb8587..cca315f1 100644 --- a/server/routes/all.js +++ b/server/routes/all.js @@ -11,5 +11,6 @@ router.use("/", require("./games")); router.use("/", require("./messages")); router.use("/", require("./users")); router.use("/", require("./variants")); +router.use("/", require("./problems")); module.exports = router; diff --git a/server/routes/problems.js b/server/routes/problems.js new file mode 100644 index 00000000..c45a1bac --- /dev/null +++ b/server/routes/problems.js @@ -0,0 +1,70 @@ +// 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'); + +router.get("/problems", (req,res) => { + const probId = req.query["pid"]; + if (!!probId) + { + if (!probId.match(/^[0-9]+$/)) + return res.json({errmsg: "Wrong problem ID"}); + ProblemModel.getOne(req.query["pid"], (err,problem) => { + access.checkRequest(res, err, problem, "Problem not found", () => { + res.json({problem: problem}); + }); + }); + } + else + { + ProblemModel.getAll((err,problems) => { + res.json(err || {problems:problems}); + }); + } +}); + +router.post("/problems", access.logged, access.ajax, (req,res) => { + const error = ProblemModel.checkProblem(req.body.prob); + if (!!error) + return res.json({errmsg:error}); + const problem = + { + vid: req.body.prob.vid, + fen: req.body.prob.fen, + uid: req.userId, + instruction: sanitizeHtml(req.body.prob.instruction), + solution: sanitizeHtml(req.body.prob.solution), + }; + ProblemModel.create(problem, (err,ret) => { + return res.json(err || {pid:ret.pid}); + }); +}); + +router.put("/problems", access.logged, access.ajax, (req,res) => { + const pid = req.body.pid; + let error = ""; + if (!pid.toString().match(/^[0-9]+$/)) + error = "Wrong problem ID"; + let obj = req.body.newProb; + error = ProblemModel.checkProblem(obj); + obj.instruction = sanitizeHtml(obj.instruction); + obj.solution = sanitizeHtml(obj.solution); + if (!!error) + return res.json({errmsg: error}); + ProblemModel.update(pid, obj, (err) => { + res.json(err || {}); + }); +}); + +router.delete("/problems", access.logged, access.ajax, (req,res) => { + const pid = req.query.id; + if (!pid.match(/^[0-9]+$/)) + res.json({errmsg: "Bad problem ID"}); + ProblemModel.safeRemove(pid, req.userId, err => { + res.json(err || {}); + }); +}); + +module.exports = router;