diff --git a/migrations/053-admin-notifications.sql b/migrations/053-admin-notifications.sql new file mode 100644 index 00000000..9ca49f03 --- /dev/null +++ b/migrations/053-admin-notifications.sql @@ -0,0 +1,6 @@ +-- Up + +ALTER TABLE users ADD COLUMN adminNotifications INTEGER NOT NULL DEFAULT 7; + +-- Down + diff --git a/routes/admin.vue b/routes/admin.vue index 00b4be7e..3beea607 100644 --- a/routes/admin.vue +++ b/routes/admin.vue @@ -10,6 +10,16 @@

Moderation rules

+

+ Email notifications when there's items to moderate: +

+

+ + + + +

+
@@ -192,6 +202,7 @@ localeFilter: true, adminsFilter: false, usersShown: false, + adminNotifications: this.$user().adminNotifications ?? 7, } }, async asyncData({ app, store }) { @@ -241,6 +252,10 @@ this.userFilterDelayed = this.userFilter; }, 500); }, + async adminNotifications() { + const res = await this.$axios.$post(`/admin/set-notification-frequency`, {frequency: parseInt(this.adminNotifications)}); + this.$store.commit('setToken', res.token); + }, }, head() { return head({ diff --git a/server/notify.js b/server/notify.js index d363f038..916ca72f 100644 --- a/server/notify.js +++ b/server/notify.js @@ -22,6 +22,18 @@ const isGranted = (user, locale, area) => { return false; } +const shouldNotify = (frequency) => { + if (frequency === 0) { + return false; + } + + if (frequency === 7) { + return (new Date()).getDay() === 6; // Saturdays + } + + return true; +} + async function notify() { const db = await dbConnection(); @@ -38,13 +50,13 @@ async function notify() { return; } - const admins = await db.all(`SELECT email, roles FROM users WHERE roles != ''`); + const admins = await db.all(`SELECT email, roles, adminNotifications FROM users WHERE roles != ''`); const awaitingModerationGrouped = {} let count = 0; for (let m of awaitingModeration) { for (let admin of admins) { - if (isGranted(admin, m.locale, m.type)) { + if (isGranted(admin, m.locale, m.type) && shouldNotify(admin.adminNotifications)) { if (awaitingModerationGrouped[admin.email] === undefined) { awaitingModerationGrouped[admin.email] = {}; } diff --git a/server/routes/admin.js b/server/routes/admin.js index 4c255bfc..9f7affad 100644 --- a/server/routes/admin.js +++ b/server/routes/admin.js @@ -11,6 +11,7 @@ import {profilesSnapshot} from "./profile"; import buildLocaleList from "../../src/buildLocaleList"; import {archiveBan, liftBan} from "../ban"; import marked from 'marked'; +import {loadCurrentUser} from "./user"; const router = Router(); @@ -247,4 +248,18 @@ router.get('/admin/moderation', handleErrorAsync(async (req, res) => { }) })); +router.post('/admin/set-notification-frequency', handleErrorAsync(async (req, res) => { + if (!req.isGranted()) { + return res.status(401).json({error: 'Unauthorised'}); + } + + if (![0, 1, 7].includes(req.body.frequency)) { + return res.status(400).json({error: 'Bad request'}); + } + + await req.db.get(SQL`UPDATE users SET adminNotifications = ${req.body.frequency} WHERE id = ${req.user.id}`); + + return await loadCurrentUser(req, res); +})); + export default router; diff --git a/server/routes/user.js b/server/routes/user.js index d2c63379..ba1eb9ad 100644 --- a/server/routes/user.js +++ b/server/routes/user.js @@ -250,7 +250,7 @@ const router = Router(); router.use(handleErrorAsync(reloadUser)); -router.get('/user/current', handleErrorAsync(async (req, res) => { +export const loadCurrentUser = async (req, res) => { if (!req.user) { res.clearCookie('token'); return res.json(null); @@ -269,7 +269,9 @@ router.get('/user/current', handleErrorAsync(async (req, res) => { req.user = req.rawUser; return res.json({...req.user, token}); -})); +}; + +router.get('/user/current', handleErrorAsync(loadCurrentUser)); router.post('/user/init', handleErrorAsync(async (req, res) => { if (req.body.usernameOrEmail && isSpam(req.body.usernameOrEmail || '')) {