var express = require('express'); var app = express(); var cors = require('cors'); var escape = require('escape-html') var bodyParser = require('body-parser'); var sqlite3 = require('sqlite3').verbose(); var db = new sqlite3.Database('db.sqlite', sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, function (err) { if(err) { console.log('error opening db:', err); } else { db.run("CREATE TABLE IF NOT EXISTS images (id INTEGER PRIMARY KEY, src TEXT, dataUri TEXT)"); db.run("CREATE TABLE IF NOT EXISTS questions (id INTEGER PRIMARY KEY, updated DATETIME DEFAULT CURRENT_TIMESTAMP, text TEXT, explanation TEXT)"); db.run("ALTER TABLE questions ADD COLUMN explanation TEXT", function (err) {}); db.run("CREATE TABLE IF NOT EXISTS answers (id INTEGER PRIMARY KEY, aid INTEGER, qid INTEGER, text TEXT, correct INTEGER, checked INTEGER)"); db.run("CREATE TABLE IF NOT EXISTS answering (id INTEGER PRIMARY KEY, created DATETIME DEFAULT CURRENT_TIMESTAMP, version TEXT, href TEXT)"); } }); app.use( bodyParser.json() ); app.use(cors()); app.set('view engine', 'pug'); var corsOptions = { origin: "*", methods: "POST" }; app.get('/', function (req, res) { db.all("SELECT q.id AS qid, q.text AS question, q.explanation AS explanation, i.dataUri AS image, T2.id AS aid, T2.text AS answer, T2.correct FROM questions q " + "LEFT JOIN (SELECT qid, MAX(aid) AS maxid FROM answers GROUP BY qid) AS T1 ON q.id = T1.qid " + "LEFT JOIN answers AS T2 ON T2.aid = T1.maxid AND T2.qid = q.id " + "LEFT JOIN images AS i ON i.id = q.id " + "ORDER BY T2.id DESC", function (err, rows) { //console.log('selected rows:', rows.length); if (err) { console.log('error:', err); res.sendStatus(500); } var prevQid, index = -1; var questions = rows.reduce(function(questions, row) { var a = { answer: row.answer, correct: row.correct }; if (prevQid != row.qid) { questions[++index] = { id: row.qid, question: row.question, explanation: row.explanation, image: row.image, answers: [a] }; } else { questions[index].answers.push(a); } prevQid = row.qid; return questions; }, []); console.log('total questions:', questions.length); res.render('index', { questions: questions }); }); }); app.post('/explanation/:id', function (req, res) { var id = req.params.id; var data = req.body; if (id && data.explanation) { console.log('updating', id, 'with', data); db.run('UPDATE questions SET explanation = $explanation WHERE id = $id', { $id: id, $explanation: escape(data.explanation) }, function (err) { if (err) { console.log('error updating explanation for', id, ':', err); res.sendStatus(500); } else { res.sendStatus(200); } }); } else { console.log('invalid explanation for', id, ':', data); res.sendStatus(400); } }); app.post('/qa', cors(corsOptions), function (req, res) { var data = req.body; console.log('NEW QA DATA:', JSON.stringify(data)); if (!data.version || !data.data || data.data.length < 15) { console.log('invalid qa data', !data.version, !data.data, data.data.length < 15); return res.sendStatus(400); } db.serialize(function() { var aid = null; db.run("INSERT INTO answering (version, href) VALUES ($version,$href)", { $version: data.version, $href: data.href }, function(err) { if (err) { console.log('inserting answering failed:', err); return; } console.log('inserted:', this.changes, 'lastID:', this.lastID); aid = this.lastID; if (aid) { var qstmt = db.prepare("REPLACE INTO questions (id, text) VALUES (?,?)"); var astmt = db.prepare("INSERT INTO answers (aid, qid, text, correct, checked) VALUES (?,?,?,?,?)"); for (var i = 0; i < data.data.length; i++) { var q = data.data[i]; qstmt.run(q.id, q.question); for (var j = 0; j < q.answers.length; j++) { var a = q.answers[j]; astmt.run(aid, q.id, a.text, a.correct, a.checked); } } qstmt.finalize(); astmt.finalize(); res.sendStatus(200); } else { console.log('no answering id'); res.sendStatus(400); } }); }); }); app.post('/img', cors(corsOptions), function (req, res) { var data = req.body; if (!data.version || !data.data.id || !data.data.src || !data.data.dataUri) { console.log('invalid img data', !data.version, !data.data.id, !data.data.src, !data.data.dataUri); return res.sendStatus(400); } var img = data.data; console.log('NEW IMG DATA:', JSON.stringify(data).substring(0,200)); var istmt = db.run("REPLACE INTO images (id, src, dataUri) VALUES (?,?,?)", img.id, img.src, img.dataUri, function (err) { if (err) { console.log('saving image ' + img.id + 'failed:', err); res.sendStatus(400); } else { res.sendStatus(200); } }); }); app.listen(3000, function () { console.log('Example app listening on port 3000!') });