Commit | Line | Data |
---|---|---|
da06a6eb BA |
1 | let express = require('express'); |
2 | let router = express.Router(); | |
3 | const createError = require('http-errors'); | |
4 | const sqlite3 = require('sqlite3');//.verbose(); | |
a48ee8b8 BA |
5 | const DbPath = __dirname.replace("/routes", "/db/vchess.sqlite"); |
6 | const db = new sqlite3.Database(DbPath); | |
da06a6eb | 7 | const sanitizeHtml = require('sanitize-html'); |
e8e4adbd | 8 | const MaxNbProblems = 20; |
1d184b4c | 9 | |
e081ffe3 | 10 | const supportedLang = ["en","es","fr"]; |
9a3c9f79 BA |
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 | ||
1d184b4c BA |
37 | // Home |
38 | router.get('/', function(req, res, next) { | |
da06a6eb BA |
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', | |
9a3c9f79 BA |
45 | variantArray: variants, |
46 | lang: selectLanguage(req, res), | |
47 | languages: supportedLang, | |
da06a6eb BA |
48 | }); |
49 | }); | |
1d184b4c BA |
50 | }); |
51 | }); | |
52 | ||
53 | // Variant | |
a5d56686 BA |
54 | router.get("/:variant([a-zA-Z0-9]+)", (req,res,next) => { |
55 | const vname = req.params["variant"]; | |
da06a6eb BA |
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)); | |
a5d56686 BA |
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 | }); | |
da06a6eb | 78 | }); |
1d184b4c BA |
79 | }); |
80 | }); | |
81 | ||
82 | // Load a rules page (AJAX) | |
83 | router.get("/rules/:variant([a-zA-Z0-9]+)", (req,res) => { | |
da06a6eb BA |
84 | if (!req.xhr) |
85 | return res.json({errmsg: "Unauthorized access"}); | |
9a3c9f79 BA |
86 | const lang = selectLanguage(req, res); |
87 | res.render("rules/" + req.params["variant"] + "/" + lang); | |
da06a6eb BA |
88 | }); |
89 | ||
a5d56686 | 90 | // Fetch N previous or next problems (AJAX) |
da06a6eb BA |
91 | router.get("/problems/:variant([a-zA-Z0-9]+)", (req,res) => { |
92 | if (!req.xhr) | |
93 | return res.json({errmsg: "Unauthorized access"}); | |
a5d56686 BA |
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"}); | |
7931e479 | 99 | db.serialize(function() { |
a5d56686 BA |
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 | }); | |
7931e479 | 110 | }); |
1d184b4c BA |
111 | }); |
112 | ||
da06a6eb BA |
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"]; | |
7931e479 BA |
118 | const timestamp = Date.now(); |
119 | // Sanitize them | |
120 | const fen = req.body["fen"]; | |
b5fb8e69 | 121 | if (!fen.match(/^[a-zA-Z0-9, /-]*$/)) |
7931e479 | 122 | return res.json({errmsg: "Bad characters in FEN string"}); |
a5d56686 BA |
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"}); | |
da06a6eb | 129 | db.serialize(function() { |
3a609580 BA |
130 | let stmt = db.prepare("INSERT INTO Problems " + |
131 | "(added,variant,fen,instructions,solution) VALUES (?,?,?,?,?)"); | |
da06a6eb BA |
132 | stmt.run(timestamp, vname, fen, instructions, solution); |
133 | stmt.finalize(); | |
134 | }); | |
135 | res.json({}); | |
136 | }); | |
137 | ||
1d184b4c | 138 | module.exports = router; |