import { Router } from 'express'; import SQL from 'sql-template-strings'; import { ulid } from 'ulid'; import { findAdmins, handleErrorAsync } from '../../src/helpers.ts'; import { auditLog } from '../audit.ts'; import { deduplicateEmailPreset } from './user.ts'; const router = Router(); const TRANSLATION_STATUS = { REJECTED: -1, AWAITING: 0, APPROVED: 1, MERGED: 2, }; router.post('/translations/propose', handleErrorAsync(async (req, res) => { if (!req.user) { return res.status(401).json({ error: 'Unauthorised' }); } for (const [tKey, tValue] of Object.entries(req.body.changes)) { // TODO single insert await req.db.get(SQL`INSERT INTO translations (id, locale, tKey, tValue, status, author_id) VALUES ( ${ulid()}, ${global.config.locale}, ${tKey}, ${JSON.stringify(tValue)}, ${TRANSLATION_STATUS.AWAITING}, ${req.user.id} )`); } await auditLog(req, 'translations/proposed', { locale: global.config.locale, autoApproved: req.isGranted('translations'), changes: req.body.changes, }); for (const { email } of await findAdmins(req.db, global.config.locale, 'translations')) { await deduplicateEmailPreset(req.db, email, 'translationProposed', { locale: global.config.locale }); } return res.json('OK'); })); router.get('/translations/proposals', handleErrorAsync(async (req, res) => { if (!req.isGranted('translations') && !req.isGranted('code')) { return res.status(401).json({ error: 'Unauthorised' }); } return res.json((await req.db.all<{ id: string; tKey: string; tValue: string; author: string }>(SQL` SELECT t.id, t.tKey, t.tValue, u.username AS author, t.status FROM translations t LEFT JOIN users u ON t.author_id = u.id WHERE locale = ${global.config.locale} AND (status = ${TRANSLATION_STATUS.AWAITING} OR status = ${TRANSLATION_STATUS.APPROVED}) `)).map((tp) => { return { ...tp, tValue: JSON.parse(tp.tValue), }; })); })); router.post('/translations/reject-proposal', handleErrorAsync(async (req, res) => { if (!req.isGranted('translations')) { return res.status(401).json({ error: 'Unauthorised' }); } await req.db.get(SQL`UPDATE translations SET status = ${TRANSLATION_STATUS.REJECTED} WHERE id = ${req.body.id}`); await auditLog(req, 'translations/rejected', { locale: global.config.locale, id: req.body.id, }); return res.json('OK'); })); router.post('/translations/accept-proposal', handleErrorAsync(async (req, res) => { if (!req.isGranted('translations')) { return res.status(401).json({ error: 'Unauthorised' }); } await req.db.get(SQL`UPDATE translations SET status = ${TRANSLATION_STATUS.APPROVED} WHERE id = ${req.body.id}`); for (const { email } of await findAdmins(req.db, global.config.locale, 'code')) { await deduplicateEmailPreset(req.db, email, 'translationToMerge', { locale: global.config.locale }); } await auditLog(req, 'translations/accepted', { locale: global.config.locale, id: req.body.id, }); return res.json('OK'); })); router.post('/translations/unapprove-proposal', handleErrorAsync(async (req, res) => { if (!req.isGranted('translations')) { return res.status(401).json({ error: 'Unauthorised' }); } await req.db.get(SQL`UPDATE translations SET status = ${TRANSLATION_STATUS.AWAITING} WHERE id = ${req.body.id}`); await auditLog(req, 'translations/unapproved', { locale: global.config.locale, id: req.body.id, }); return res.json('OK'); })); router.post('/translations/proposals-done', handleErrorAsync(async (req, res) => { if (!req.isGranted('code')) { return res.status(401).json({ error: 'Unauthorised' }); } await req.db.get(SQL`UPDATE translations SET status = ${TRANSLATION_STATUS.MERGED} WHERE locale = ${global.config.locale} AND status = ${TRANSLATION_STATUS.APPROVED}`); await auditLog(req, 'translations/merged', { locale: global.config.locale, id: req.body.id, }); return res.json('OK'); })); router.get('/translations/contributors', handleErrorAsync(async (req, res) => { if (!req.isGranted('translations') && !req.isGranted('code')) { return res.status(401).json({ error: 'Unauthorised' }); } const contributors = []; const translationCountByContributor = req.db.all<{ author_id: string; c: number }>(SQL` SELECT author_id, count(*) AS c FROM translations WHERE locale = ${global.config.locale} AND status >= ${TRANSLATION_STATUS.APPROVED} GROUP BY author_id `); for (const { author_id: authorId, c } of await translationCountByContributor) { const authorInfo = await req.db.get<{ username: string; roles: string }>(SQL` SELECT username, roles FROM users WHERE id=${authorId} `); if (!authorInfo) { continue; } contributors.push({ username: authorInfo.username, isMember: !!authorInfo.roles, count: c, }); } return res.json(contributors); })); export default router;