From: Benjamin Auder Date: Thu, 17 Jan 2019 11:57:43 +0000 (+0100) Subject: Debugging problems page; TODO: hash navigation is wrong X-Git-Url: https://git.auder.net/%7B%7B%20asset%28%27mixstore/css/static/current/config.php?a=commitdiff_plain;h=badeb466c977ed9a8e1b464a2236001126decb9e;p=vchess.git Debugging problems page; TODO: hash navigation is wrong --- diff --git a/models/Problem.js b/models/Problem.js index 7ac92f78..99e5b620 100644 --- a/models/Problem.js +++ b/models/Problem.js @@ -10,20 +10,20 @@ var db = require("../utils/database"); * solution: text */ -// TODO: callback ? -exports.create = function(vid, fen, instructions, solution) +exports.create = function(uid, vid, fen, instructions, solution, cb) { db.serialize(function() { - const query = - "INSERT INTO Problems (added, vid, fen, instructions, solution) VALUES " + - "(" + - Date.now() + "," + - vid + "," + - fen + "," + - instructions + "," + - solution + - ")"; - db.run(query); + 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); + }); +// const stmt = db.prepare(query); +// stmt.run(instructions, solution); +// stmt.finalize(); }); } @@ -43,7 +43,7 @@ exports.fetchN = function(vid, uid, type, directionStr, lastDt, MaxNbProblems, c db.serialize(function() { let typeLine = ""; if (uid > 0) - typeLine = "AND id " + (type=="others" ? "!=" : "=") + " " + uid; + typeLine = "AND uid " + (type=="others" ? "!=" : "=") + " " + uid; const query = "SELECT * FROM Problems " + "WHERE vid = " + vid + @@ -54,16 +54,17 @@ exports.fetchN = function(vid, uid, type, directionStr, lastDt, MaxNbProblems, c }); } -exports.update = function(id, uid, fen, instructions, solution) +// TODO: update fails (but insert is OK) +exports.update = function(id, uid, fen, instructions, solution, cb) { db.serialize(function() { const query = - "UPDATE Problems " + - "fen = " + fen + ", " + - "instructions = " + instructions + ", " + - "solution = " + solution + " " + + "UPDATE Problems SET " + + "fen = '" + fen + "', " + + "instructions = ?, " + + "solution = ? " + "WHERE id = " + id + " AND uid = " + uid; - db.run(query); + db.run(query, [instructions,solution], cb); }); } diff --git a/models/User.js b/models/User.js index 171dc2c2..4b5c840a 100644 --- a/models/User.js +++ b/models/User.js @@ -1,6 +1,6 @@ var db = require("../utils/database"); var maild = require("../utils/mailer.js"); -var TokenGen = require("../utils/tokenGenerator"); +var genToken = require("../utils/tokenGenerator"); var params = require("../config/parameters"); /* @@ -14,10 +14,7 @@ var params = require("../config/parameters"); * notify: boolean (send email notifications for corr games) */ -// TODO: consider sanitizing http://www.unixwiz.net/techtips/sql-injection.html -// But parameters are supposed to already be cleaned (in controller). - -// User creation +// NOTE: parameters are already cleaned (in controller), thus no sanitization here exports.create = function(name, email, notify, callback) { db.serialize(function() { @@ -73,7 +70,7 @@ exports.trySetSessionToken = function(uid, cb) db.get(querySessionToken, (err,ret) => { if (!!err) return cb(err); - const token = ret.sessionToken || TokenGen.generate(params.token.length); + const token = ret.sessionToken || genToken(params.token.length); const queryUpdate = "UPDATE Users " + "SET loginToken = NULL" + diff --git a/public/javascripts/components/problemSummary.js b/public/javascripts/components/problemSummary.js index d7f239ee..3d579156 100644 --- a/public/javascripts/components/problemSummary.js +++ b/public/javascripts/components/problemSummary.js @@ -1,6 +1,6 @@ // Preview a problem on variant page Vue.component('my-problem-summary', { - props: ['prob','userid'], + props: ['prob','userid','preview'], template: `

{{ timestamp2date(prob.added) }}

-
+ +
diff --git a/public/javascripts/components/problems.js b/public/javascripts/components/problems.js index 6f03fd97..6edab93d 100644 --- a/public/javascripts/components/problems.js +++ b/public/javascripts/components/problems.js @@ -14,6 +14,7 @@ Vue.component('my-problems', { // New problem (to upload), or existing problem to edit: modalProb: { id: 0, //defined if it's an edit + uid: 0, //...also fen: "", instructions: "", solution: "", @@ -24,48 +25,51 @@ Vue.component('my-problems', { template: `
- - -
-

- {{ curProb.instructions }} -

+

{{ curProb.instructions }}

- +

{{ translations["Show solution"] }}

-

- {{ curProb.solution }} -

+

{{ curProb.solution }}

- +
- +
- @@ -73,9 +77,7 @@ Vue.component('my-problems', {
-

- {{ translate("Add a problem") }} -

+

{{ translate("Add a problem") }}

@@ -83,39 +85,28 @@ Vue.component('my-problems', { :placeholder='translate("Full FEN description")'/>
-

- {{ translate("Safe HTML tags allowed") }} -

- +

{{ translate("Safe HTML tags allowed") }}

+ - + - +
-
@@ -123,9 +114,7 @@ Vue.component('my-problems', {
-

- {{ nomoreMessage }} -

+

{{ nomoreMessage }}

@@ -160,11 +149,17 @@ Vue.component('my-problems', { }, showProblem: function(num) { const pid = num || this.pbNum; - location.hash = "#" + pid; - const pIdx = this.singletons.findIndex(p => p.id == pid); - if (pIdx >= 0) - curProb = this.singletons[pIdx]; - else + location.hash = "#problems?id=" + pid; + for (let parray of [this.singletons,this.problems,this.myProblems]) + { + const pIdx = parray.findIndex(p => p.id == pid); + if (pIdx >= 0) + { + curProb = parray[pIdx]; + break; + } + } + if (!curProb) { // Cannot find problem in current set; get from server, and add to singletons. ajax( @@ -240,27 +235,30 @@ Vue.component('my-problems', { }, displayList: function() { this.curProb = null; - location.hash = ""; + location.hash = "#problems"; // Fetch problems if first call (if #num, and then lists) if (!this.listsInitialized) this.firstFetch(); }, toggleListDisplay: function() { - this.display = (this.display == "others" ? "mine" : "others"); + const displays = ["mine","others"]; + const curIndex = displays.findIndex(item => item == this.display); + this.display = displays[1-curIndex]; }, fetchProblems: function(type, direction) { let problems = (type == "others" ? this.problems : this.myProblems); + // "last datetime" set at a value OK for an empty initial array let last_dt = (direction=="forward" ? 0 : Number.MAX_SAFE_INTEGER); - if (this.problems.length > 0) + if (problems.length > 0) { // Search for newest date (or oldest) last_dt = problems[0].added; for (let i=1; i last_dt) || - (direction == "backward" && this.problems[i].added < last_dt)) + if ((direction == "forward" && problems[i].added > last_dt) || + (direction == "backward" && problems[i].added < last_dt)) { - last_dt = this.problems[i].added; + last_dt = problems[i].added; } } } @@ -276,7 +274,7 @@ Vue.component('my-problems', { if (response.problems.length > 0) { Array.prototype.push.apply(problems, - response.problems.sort((p1,p2) => { return p1.added - p2.added; })); + response.problems.sort((p1,p2) => { return p2.added - p1.added; })); // If one list is empty but not the other, show the non-empty const otherArray = (type == "mine" ? this.problems : this.myProblems); if (problems.length > 0 && otherArray.length == 0) @@ -286,21 +284,22 @@ Vue.component('my-problems', { ); }, previewProblem: function() { - if (!V.IsGoodFen(this.newProblem.fen)) + if (!V.IsGoodFen(this.modalProb.fen)) return alert(translations["Bad FEN description"]); - if (this.newProblem.instructions.trim().length == 0) + if (this.modalProb.instructions.trim().length == 0) return alert(translations["Empty instructions"]); - if (this.newProblem.solution.trim().length == 0) + if (this.modalProb.solution.trim().length == 0) return alert(translations["Empty solution"]); - this.modalProb.preview = true; + Vue.set(this.modalProb, "preview", true); }, editProblem: function(prob) { this.modalProb = prob; + Vue.set(this.modalProb, "preview", false); document.getElementById("modal-newproblem").checked = true; }, deleteProblem: function(pid) { ajax( - "/problems/" + variant.id + "/" + pid, + "/problems/" + pid, "DELETE", response => { // Delete problem from the list on client side @@ -318,11 +317,17 @@ Vue.component('my-problems', { this.modalProb, response => { document.getElementById("modal-newproblem").checked = false; + Vue.set(this.modalProb, "preview", false); if (this.modalProb.id == 0) { - this.modalProb.added = Date.now(); - this.modalProb.preview = false; - this.myProblems.push(JSON.parse(JSON.stringify(this.modalProb))); + this.myProblems.unshift({ + added: Date.now(), + id: response.id, + uid: user.id, + fen: this.modalProb.fen, + instructions: this.modalProb.instructions, + solution: this.modalProb.solution, + }); } else this.modalProb.id = 0; diff --git a/public/javascripts/components/tabGames.js b/public/javascripts/components/tabGames.js index 3181be6b..3861dbac 100644 --- a/public/javascripts/components/tabGames.js +++ b/public/javascripts/components/tabGames.js @@ -24,6 +24,7 @@ Vue.component("my-tab-games", { +
`, created: function() { diff --git a/public/stylesheets/variant.sass b/public/stylesheets/variant.sass index 1141d2ca..ed55f96b 100644 --- a/public/stylesheets/variant.sass +++ b/public/stylesheets/variant.sass @@ -385,3 +385,8 @@ ul:not(.browser-default) > li .problem margin: 10px 0 + +.only-mine + background-color: yellow + &:hover + background-color: yellow diff --git a/routes/problems.js b/routes/problems.js index 3434f0cc..777543b1 100644 --- a/routes/problems.js +++ b/routes/problems.js @@ -55,27 +55,39 @@ router.get("/problems/:vid([0-9]+)", access.ajax, (req,res) => { // 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"]); + const s = sanitizeUserInput( + req.body["fen"], req.body["instructions"], req.body["solution"]); if (typeof s === "string") return res.json({errmsg: s}); - ProblemModel.create(vid, s.fen, s.instructions, s.solution); - res.json({}); + 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"]); + 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, fen, instructions, solution); - res.json({}); + 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.delete(pid, req.userId); + ProblemModel.remove(pid, req.userId); res.json({}); }); diff --git a/routes/users.js b/routes/users.js index 9c88d08c..95992c53 100644 --- a/routes/users.js +++ b/routes/users.js @@ -3,7 +3,7 @@ var router = require("express").Router(); var UserModel = require('../models/User'); var sendEmail = require('../utils/mailer'); -var TokenGen = require("../utils/tokenGenerator"); +var genToken = require("../utils/tokenGenerator"); var access = require("../utils/access"); var params = require("../config/parameters"); var checkNameEmail = require("../public/javascripts/shared/userCheck") @@ -12,7 +12,7 @@ var checkNameEmail = require("../public/javascripts/shared/userCheck") function setAndSendLoginToken(subject, to, res) { // Set login token and send welcome(back) email with auth link - const token = TokenGen.generate(params.token.length); + const token = genToken(params.token.length); UserModel.setLoginToken(token, to.id, err => { if (!!err) return res.json({errmsg: err.toString()}); diff --git a/utils/database.js b/utils/database.js index 39c7e5e0..ae7c7a66 100644 --- a/utils/database.js +++ b/utils/database.js @@ -1,4 +1,9 @@ const sqlite3 = require('sqlite3'); +const params = require("../config/parameters") + +if (params.env == "development") + sqlite3.verbose(); + const DbPath = __dirname.replace("/utils", "/db/vchess.sqlite"); const db = new sqlite3.Database(DbPath); diff --git a/utils/tokenGenerator.js b/utils/tokenGenerator.js index 1bc172cd..b549198a 100644 --- a/utils/tokenGenerator.js +++ b/utils/tokenGenerator.js @@ -1,14 +1,13 @@ -module.exports = +function randString() { - rand: function() { - return Math.random().toString(36).substr(2); // remove `0.` - }, + return Math.random().toString(36).substr(2); // remove `0.` +} - generate: function(tlen) { - var res = ""; - var nbRands = Math.ceil(tlen/10); //10 = min length of a rand() string - for (var i = 0; i < nbRands; i++) - res += TokenGen.rand(); - return res.substr(0, tlen); - }, +module.exports = function(tlen) +{ + let res = ""; + let nbRands = Math.ceil(tlen/10); //10 = min length of a rand() string + for (let i = 0; i < nbRands; i++) + res += randString(); + return res.substr(0, tlen); }