This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
Zaimki/server/routes/census.js

178 lines
5.7 KiB
JavaScript
Raw Normal View History

2020-12-18 02:34:58 -08:00
import { Router } from 'express';
import SQL from 'sql-template-strings';
import sha1 from 'sha1';
import {ulid} from "ulid";
2021-02-02 07:49:49 -08:00
import Papa from 'papaparse';
import {handleErrorAsync} from "../../src/helpers";
2020-12-18 02:34:58 -08:00
const getIp = req => {
try {
return req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.ips.join(',') || req.ip;
} catch {
return '';
}
}
2020-12-18 02:34:58 -08:00
const buildFingerprint = req => sha1(`
${getIp(req)}
2020-12-18 02:34:58 -08:00
${req.headers['user-agent']}
${req.headers['accept-language']}
`);
const hasFinished = async req => {
if (req.user) {
const byUser = await req.db.get(SQL`
SELECT * FROM census
WHERE locale = ${global.config.locale}
AND edition = ${global.config.census.edition}
2020-12-18 02:34:58 -08:00
AND userId = ${req.user.id}
`);
return !!byUser;
2020-12-18 02:34:58 -08:00
}
const fingerprint = buildFingerprint(req);
const byFingerprint = await req.db.get(SQL`
SELECT * FROM census
WHERE locale = ${global.config.locale}
AND edition = ${global.config.census.edition}
2020-12-18 02:34:58 -08:00
AND fingerprint = ${fingerprint}
AND userId IS NULL
2020-12-18 02:34:58 -08:00
`);
return !!byFingerprint;
2020-12-18 02:34:58 -08:00
}
const router = Router();
router.get('/census/finished', handleErrorAsync(async (req, res) => {
2020-12-18 02:34:58 -08:00
return res.json(await hasFinished(req));
}));
2020-12-18 02:34:58 -08:00
router.post('/census/submit', handleErrorAsync(async (req, res) => {
const suspicious = await hasFinished(req);
2020-12-18 02:34:58 -08:00
const id = ulid();
await req.db.get(SQL`INSERT INTO census (id, locale, edition, userId, fingerprint, answers, writins, ip, userAgent, acceptLanguage, suspicious) VALUES (
2020-12-18 02:34:58 -08:00
${id},
${global.config.locale},
${global.config.census.edition},
2020-12-18 02:34:58 -08:00
${req.user ? req.user.id : null},
${buildFingerprint(req)},
${req.body.answers},
${req.body.writins},
null,
null,
null,
${suspicious}
2020-12-18 02:34:58 -08:00
)`);
return res.json(id);
}));
2020-12-18 02:34:58 -08:00
router.get('/census/count', handleErrorAsync(async (req, res) => {
2022-02-17 07:38:16 -08:00
// if (!req.isGranted('census')) {
// return res.status(401).json({error: 'Unauthorised'});
// }
2022-02-17 06:19:33 -08:00
// duplication reason: https://github.com/felixfbecker/node-sql-template-strings/issues/71
return res.json({
all: (await req.db.get(SQL`
SELECT COUNT(*) as c FROM census
WHERE locale = ${global.config.locale}
AND edition = ${global.config.census.edition}
`)).c,
nonbinary: (await req.db.get(SQL`
SELECT COUNT(*) as c FROM census
WHERE locale = ${global.config.locale}
AND edition = ${global.config.census.edition}
AND (answers LIKE '{"0":"osobą niebinarną"%' OR answers LIKE '{"0":"nie wiem"%') -- TODO polish-specific
`)).c,
usable: (await req.db.get(SQL`
SELECT COUNT(*) as c FROM census
WHERE locale = ${global.config.locale}
AND edition = ${global.config.census.edition}
AND (answers LIKE '{"0":"osobą niebinarną"%' OR answers LIKE '{"0":"nie wiem"%') -- TODO polish-specific
AND troll = 0
`)).c,
awaiting: (await req.db.get(SQL`
SELECT COUNT(*) as c FROM census
WHERE locale = ${global.config.locale}
AND edition = ${global.config.census.edition}
AND (answers LIKE '{"0":"osobą niebinarną"%' OR answers LIKE '{"0":"nie wiem"%') -- TODO polish-specific
AND troll IS NULL
`)).c,
});
}));
2020-12-18 08:41:01 -08:00
router.get('/census/export', handleErrorAsync(async (req, res) => {
2021-02-02 07:49:49 -08:00
if (!req.isGranted('census')) {
return res.status(401).json({error: 'Unauthorised'});
2021-02-02 07:49:49 -08:00
}
const report = [];
2022-02-17 06:19:33 -08:00
for (let {answers, writins, troll} of await req.db.all(SQL`
2021-02-02 07:49:49 -08:00
SELECT answers, writins FROM census
WHERE locale = ${global.config.locale}
2022-02-17 06:19:33 -08:00
AND edition = ${global.config.census.edition}
AND suspicious = 0
AND troll = 0
2021-02-02 07:49:49 -08:00
`)) {
answers = JSON.parse(answers);
writins = JSON.parse(writins);
const answer = {};
let i = 0;
for (let question of config.census.questions) {
if (question.type === 'checkbox') {
for (let [option, comment] of question.options) {
2021-02-02 08:42:40 -08:00
answer[`${i}_${option}`] = (answers[i.toString()] || []).includes(option) ? 1 : '';
2021-02-02 07:49:49 -08:00
}
} else {
answer[`${i}_`] = answers[i.toString()] || '';
}
if (question.writein) {
2021-02-02 08:42:40 -08:00
answer[`${i}__writein`] = writins[i.toString()] || '';
2021-02-02 07:49:49 -08:00
}
i++;
}
report.push(answer);
}
return res.set('content-type', 'text/csv').send(Papa.unparse(report));
}));
2021-02-02 07:49:49 -08:00
2022-02-17 06:19:33 -08:00
router.get('/census/moderation/queue', handleErrorAsync(async (req, res) => {
if (!req.isGranted('census')) {
return res.status(401).json({error: 'Unauthorised'});
}
const queue = await req.db.all(SQL`
SELECT id, answers, writins FROM census
WHERE locale = ${global.config.locale}
AND edition = ${global.config.census.edition}
AND (answers LIKE '{"0":"osobą niebinarną"%' OR answers LIKE '{"0":"nie wiem"%') -- TODO polish-specific
AND troll IS NULL
ORDER BY RANDOM()
`);
return res.json({
count: queue.length,
next: queue.length ? queue[0] : null,
});
}));
router.post('/census/moderation/decide', handleErrorAsync(async (req, res) => {
if (!req.isGranted('census')) {
return res.status(401).json({error: 'Unauthorised'});
}
const queue = await req.db.get(SQL`
UPDATE census SET troll = ${parseInt(req.body.decision)} WHERE id = ${req.body.id}
`);
return res.json('ok');
}));
2020-12-18 02:34:58 -08:00
export default router;