| 1 | let express = require('express'); |
| 2 | let router = express.Router(); |
| 3 | const createError = require('http-errors'); |
| 4 | const sqlite3 = require('sqlite3');//.verbose(); |
| 5 | const DbPath = __dirname.replace("/routes", "/db/vchess.sqlite"); |
| 6 | const db = new sqlite3.Database(DbPath); |
| 7 | const sanitizeHtml = require('sanitize-html'); |
| 8 | const MaxNbProblems = 20; |
| 9 | |
| 10 | const supportedLang = ["fr","en"]; |
| 11 | function selectLanguage(req, res) |
| 12 | { |
| 13 | // If preferred language already set: |
| 14 | if (!!req.cookies["lang"]) |
| 15 | return req.cookies["lang"]; |
| 16 | |
| 17 | // Else: search and set it |
| 18 | const langString = req.headers["accept-language"]; |
| 19 | let langArray = langString |
| 20 | .replace(/;q=[0-9.]+/g, "") //priority |
| 21 | .replace(/-[A-Z]+/g, "") //region (skipped for now...) |
| 22 | .split(",") //may have some duplicates, but removal is too costly |
| 23 | let bestLang = "en"; //default: English |
| 24 | for (let lang of langArray) |
| 25 | { |
| 26 | if (supportedLang.includes(lang)) |
| 27 | { |
| 28 | bestLang = lang; |
| 29 | break; |
| 30 | } |
| 31 | } |
| 32 | // Cookie expires in 183 days (expressed in milliseconds) |
| 33 | res.cookie('lang', bestLang, { maxAge: 183*24*3600*1000 }); |
| 34 | return bestLang; |
| 35 | } |
| 36 | |
| 37 | // Home |
| 38 | router.get('/', function(req, res, next) { |
| 39 | db.serialize(function() { |
| 40 | db.all("SELECT * FROM Variants", (err,variants) => { |
| 41 | if (!!err) |
| 42 | return next(err); |
| 43 | res.render('index', { |
| 44 | title: 'club', |
| 45 | variantArray: variants, |
| 46 | lang: selectLanguage(req, res), |
| 47 | languages: supportedLang, |
| 48 | }); |
| 49 | }); |
| 50 | }); |
| 51 | }); |
| 52 | |
| 53 | // Variant |
| 54 | router.get("/:variant([a-zA-Z0-9]+)", (req,res,next) => { |
| 55 | const vname = req.params["variant"]; |
| 56 | db.serialize(function() { |
| 57 | db.all("SELECT * FROM Variants WHERE name='" + vname + "'", (err,variant) => { |
| 58 | if (!!err) |
| 59 | return next(err); |
| 60 | if (!variant || variant.length==0) |
| 61 | return next(createError(404)); |
| 62 | // Get only N most recent problems |
| 63 | const query2 = "SELECT * FROM Problems " + |
| 64 | "WHERE variant='" + vname + "' " + |
| 65 | "ORDER BY added DESC " + |
| 66 | "LIMIT " + MaxNbProblems; |
| 67 | db.all(query2, (err2,problems) => { |
| 68 | if (!!err2) |
| 69 | return next(err2); |
| 70 | res.render('variant', { |
| 71 | title: vname + ' Variant', |
| 72 | variant: vname, |
| 73 | problemArray: problems, |
| 74 | lang: selectLanguage(req, res), |
| 75 | languages: supportedLang, |
| 76 | }); |
| 77 | }); |
| 78 | }); |
| 79 | }); |
| 80 | }); |
| 81 | |
| 82 | // Load a rules page (AJAX) |
| 83 | router.get("/rules/:variant([a-zA-Z0-9]+)", (req,res) => { |
| 84 | if (!req.xhr) |
| 85 | return res.json({errmsg: "Unauthorized access"}); |
| 86 | const lang = selectLanguage(req, res); |
| 87 | res.render("rules/" + req.params["variant"] + "/" + lang); |
| 88 | }); |
| 89 | |
| 90 | // Fetch N previous or next problems (AJAX) |
| 91 | router.get("/problems/:variant([a-zA-Z0-9]+)", (req,res) => { |
| 92 | if (!req.xhr) |
| 93 | return res.json({errmsg: "Unauthorized access"}); |
| 94 | const vname = req.params["variant"]; |
| 95 | const directionStr = (req.query.direction == "forward" ? ">" : "<"); |
| 96 | const lastDt = req.query.last_dt; |
| 97 | if (!lastDt.match(/[0-9]+/)) |
| 98 | return res.json({errmsg: "Bad timestamp"}); |
| 99 | db.serialize(function() { |
| 100 | const query = "SELECT * FROM Problems " + |
| 101 | "WHERE variant='" + vname + "' " + |
| 102 | " AND added " + directionStr + " " + lastDt + " " + |
| 103 | "ORDER BY added " + (directionStr=="<" ? "DESC " : "") + |
| 104 | "LIMIT " + MaxNbProblems; |
| 105 | db.all(query, (err,problems) => { |
| 106 | if (!!err) |
| 107 | return res.json(err); |
| 108 | return res.json({problems: problems}); |
| 109 | }); |
| 110 | }); |
| 111 | }); |
| 112 | |
| 113 | // Upload a problem (AJAX) |
| 114 | router.post("/problems/:variant([a-zA-Z0-9]+)", (req,res) => { |
| 115 | if (!req.xhr) |
| 116 | return res.json({errmsg: "Unauthorized access"}); |
| 117 | const vname = req.params["variant"]; |
| 118 | const timestamp = Date.now(); |
| 119 | // Sanitize them |
| 120 | const fen = req.body["fen"]; |
| 121 | if (!fen.match(/^[a-zA-Z0-9, /-]*$/)) |
| 122 | return res.json({errmsg: "Bad characters in FEN string"}); |
| 123 | const instructions = sanitizeHtml(req.body["instructions"]).trim(); |
| 124 | const solution = sanitizeHtml(req.body["solution"]).trim(); |
| 125 | if (instructions.length == 0) |
| 126 | return res.json({errmsg: "Empty instructions"}); |
| 127 | if (solution.length == 0) |
| 128 | return res.json({errmsg: "Empty solution"}); |
| 129 | db.serialize(function() { |
| 130 | let stmt = db.prepare("INSERT INTO Problems " + |
| 131 | "(added,variant,fen,instructions,solution) VALUES (?,?,?,?,?)"); |
| 132 | stmt.run(timestamp, vname, fen, instructions, solution); |
| 133 | stmt.finalize(); |
| 134 | }); |
| 135 | res.json({}); |
| 136 | }); |
| 137 | |
| 138 | module.exports = router; |