reference #55 working on global settings page/config editor, start with global filters and options to force change sitewide captcha

merge-requests/208/head
fatchan 5 years ago
parent 05667c52a4
commit a5aaaa29a5
  1. 3
      controllers/forms.js
  2. 43
      controllers/forms/globalsettings.js
  3. 7
      controllers/pages.js
  4. 2
      gulp/res/js/hover.js
  5. 2
      helpers/tasks.js
  6. 74
      models/forms/changeglobalsettings.js
  7. 36
      models/forms/makepost.js
  8. 1
      models/pages/globalmanage/index.js
  9. 23
      models/pages/globalmanage/settings.js
  10. 8
      schedules/index.js
  11. 7
      views/custompages/rules.pug
  12. 2
      views/mixins/globalmanagenav.pug
  13. 35
      views/pages/globalmanagesettings.pug

@ -55,6 +55,7 @@ const express = require('express')
, registerController = require(__dirname+'/forms/register.js')
, changePasswordController = require(__dirname+'/forms/changepassword.js')
, editAccountsController = require(__dirname+'/forms/editaccounts.js')
, globalSetttingsController = require(__dirname+'/forms/globalsettings.js')
, createBoardController = require(__dirname+'/forms/create.js')
, makePostController = require(__dirname+'/forms/makepost.js')
, newcaptcha = require(__dirname+'/../models/forms/newcaptcha.js')
@ -80,7 +81,7 @@ router.post('/global/editbans', sessionRefresh, csrf, calcPerms, isLoggedIn, has
router.post('/global/addnews', sessionRefresh, csrf, calcPerms, isLoggedIn, hasPerms(0), addNewsController); //add new newspost
router.post('/global/deletenews', sessionRefresh, csrf, calcPerms, isLoggedIn, hasPerms(0), paramConverter, deleteNewsController); //delete news
router.post('/global/editaccounts', sessionRefresh, csrf, calcPerms, isLoggedIn, hasPerms(0), paramConverter, editAccountsController); //account editing
//router.post('/global/deleteboard', sessionRefresh, csrf, calcPerms, isLoggedIn, hasPerms(1), deleteBoardController); //delete board. removed and going to change when i add a board page to globalmanage with unlisted boards and searching
router.post('/global/setings', sessionRefresh, csrf, calcPerms, isLoggedIn, hasPerms(0), paramConverter, globalSettingsController); //global settings
//accounts
router.post('/login', loginController);

@ -0,0 +1,43 @@
'use strict';
const changeGlobalSettings = require(__dirname+'/../../models/forms/changeglobalsettings.js')
module.exports = async (req, res, next) => {
const errors = [];
if (req.body.filters && req.body.filters.length > 2000) {
errors.push('Filters length must be less than 2000 characters');
}
if (typeof req.body.captcha_mode === 'number' && (req.body.captcha_mode < 0 || req.body.captcha_mode > 2)) {
errors.push('Invalid captcha mode.');
}
if (typeof req.body.filter_mode === 'number' && (req.body.filter_mode < 0 || req.body.filter_mode > 2)) {
errors.push('Invalid filter mode.');
}
if (typeof req.body.ban_duration === 'number' && req.body.ban_duration <= 0) {
errors.push('Invalid filter auto ban duration.');
}
if (errors.length > 0) {
return res.status(400).render('message', {
'title': 'Bad request',
'errors': errors,
'redirect': '/globalmanage/settings.html'
});
}
//todo: implement this
return res.status(400).render('message', {
'title': 'Bad request',
'error': 'Not implemented',
'redirect': '/globalmanage/settings.html'
});
try {
await changeGlobalSettings(req, res, next);
} catch (err) {
return next(err);
}
}

@ -13,7 +13,7 @@ const express = require('express')
, csrf = require(__dirname+'/../helpers/checks/csrfmiddleware.js')
//page models
, { manageReports, manageBanners, manageSettings, manageBans } = require(__dirname+'/../models/pages/manage/')
, { globalManageReports, globalManageBans, globalManageRecent, globalManageAccounts, globalManageNews } = require(__dirname+'/../models/pages/globalmanage/')
, { globalManageSettings, globalManageReports, globalManageBans, globalManageRecent, globalManageAccounts, globalManageNews } = require(__dirname+'/../models/pages/globalmanage/')
, { changePassword, home, register, login, logout, create,
board, catalog, banners, randombanner, news, captchaPage,
captcha, thread, modlog, modloglist, account, boardlist } = require(__dirname+'/../models/pages/');
@ -51,9 +51,10 @@ router.get('/:board/manage/thread/:id(\\d+).html', sessionRefresh, isLoggedIn, B
//global manage pages
router.get('/globalmanage/reports.html', sessionRefresh, isLoggedIn, calcPerms, hasPerms(1), csrf, globalManageReports);
router.get('/globalmanage/bans.html', sessionRefresh, isLoggedIn, calcPerms, hasPerms(1), csrf, globalManageBans);
router.get('/globalmanage/news.html', sessionRefresh, isLoggedIn, calcPerms, hasPerms(0), csrf, globalManageNews);
router.get('/globalmanage/accounts.html', sessionRefresh, isLoggedIn, calcPerms, hasPerms(1), csrf, globalManageAccounts);
router.get('/globalmanage/recent.html', sessionRefresh, isLoggedIn, calcPerms, hasPerms(1), csrf, globalManageRecent);
router.get('/globalmanage/news.html', sessionRefresh, isLoggedIn, calcPerms, hasPerms(0), csrf, globalManageNews);
router.get('/globalmanage/accounts.html', sessionRefresh, isLoggedIn, calcPerms, hasPerms(0), csrf, globalManageAccounts);
router.get('/globalmanage/settings.html', sessionRefresh, isLoggedIn, calcPerms, hasPerms(0), csrf, globalManageSettings);
//captcha
router.get('/captcha', captcha); //get captcha image and cookie

@ -107,7 +107,7 @@ window.addEventListener('DOMContentLoaded', (event) => {
this.style.cursor = '';
}
if (json) {
setLocalStorage(`hovercache-${jsonPath}`, JSON.stringify(jsonson));
setLocalStorage(`hovercache-${jsonPath}`, JSON.stringify(json));
if (json.postId == hash) {
postJson = json;
} else {

@ -91,7 +91,7 @@ module.exports = {
if (options.endpage === 0) {
//deleted only/all posts, so only 1 page will remain
options.endpage = 1;
} else if (maxPage < options.endpage) {
} else if (maxPage < options.endpage || !options.endpage) {
//else just build up to the max page if it is greater than input page number
options.endpage = maxPage
}

@ -0,0 +1,74 @@
'use strict';
const { Boards, Posts, Accounts } = require(__dirname+'/../../db/')
, uploadDirectory = require(__dirname+'/../../helpers/files/uploadDirectory.js')
, buildQueue = require(__dirname+'/../../queue.js')
, cache = require(__dirname+'/../../redis.js')
, { remove } = require('fs-extra');
module.exports = async (req, res, next) => {
const promises = [];
const oldSettings = await cache.get('globalsettings');
const newSettings = {
'captchaMode': typeof req.body.captcha_mode === 'number' && req.body.captcha_mode !== oldSettings.captchaMode ? req.body.captcha_mode : oldSettings.captchaMode,
'filters': req.body.filters !== null ? req.body.filters.split('\r\n').filter(n => n).slice(0,50) : oldSettings.filters,
'filterMode': typeof req.body.filter_mode === 'number' && req.body.filter_mode !== oldSettings.filterMode ? req.body.filter_mode : oldSettings.filterMode,
'filterBanDuration': typeof req.body.ban_duration === 'number' && req.body.ban_duration !== oldSettings.filterBanDuration ? req.body.ban_duration : oldSettings.filterBanDuration,
};
cache.set('globalsettings', newSettings);
let rebuildThreads = false
, rebuildBoard = false
, rebuildCatalog = false;
if (newSettings.captchaMode > oldSettings.captchaMode) {
rebuildBoard = true;
rebuildCatalog = true;
if (newSettings.captchaMode == 2) {
rebuildThreads = true; //thread captcha enabled, removes threads
}
//todo: implement removing pages/rebuilding for all affected boards
//i.e. query for ones with settings.catchaMode < newSettings.captchaMode
/*
const affectedBoards = //////
for (let i = 0; i < affectedBoards.length; i++) {
const board = affectedBoards[i];
if (rebuildThreads) {
promises.push(remove(`${uploadDirectory}/html/${board._id}/thread/`));
}
if (rebuildBoard) {
buildQueue.push({
'task': 'buildBoardMultiple',
'options': {
board,
'startpage': 1,
'endpage': null //no endpage will use whatver maxpage of board is
}
});
}
if (rebuildCatalog) {
buildQueue.push({
'task': 'buildCatalog',
'options': {
board,
}
});
}
}
*/
}
//finish the promises in parallel e.g. removing files
if (promises.length > 0) {
await Promise.all(promises);
}
return res.render('message', {
'title': 'Success',
'message': 'Updated settings.',
'redirect': '/globalmanage/settings.html'
});
}

@ -7,6 +7,7 @@ const path = require('path')
, Mongo = require(__dirname+'/../../db/db.js')
, Socketio = require(__dirname+'/../../socketio.js')
, { Stats, Posts, Boards, Files, Bans } = require(__dirname+'/../../db/')
, cache = require(__dirname+'/../../redis.js')
, getTripCode = require(__dirname+'/../../helpers/posting/tripcode.js')
, linkQuotes = require(__dirname+'/../../helpers/posting/quotes.js')
, { markdown } = require(__dirname+'/../../helpers/posting/markdown.js')
@ -98,24 +99,38 @@ module.exports = async (req, res, next) => {
});
}
//filters
if (res.locals.permLevel >= 4 && filterMode > 0 && filters && filters.length > 0) {
const allContents = req.body.name+req.body.message+req.body.subject+req.body.email;
const containsFilter = filters.some(filter => { return allContents.includes(filter) });
if (containsFilter === true) {
if (res.locals.permLevel > 1) { //global staff bypass filters
const allContents = req.body.name+req.body.message+req.body.subject+req.body.email
, globalSettings = await cache.get('globalsettings');
let hitGlobalFilter = false
, hitLocalFilter = false
, ban;
//global filters
if (globalSettings && globalSettings.filters.length > 0 && globalSettings.filterMode > 0) {
hitGlobalfilter = globalSettings.filters.some(filter => { return allContents.includes(filter) });
}
//board-specific filters
if (!hitGlobalFilter && res.locals.permLevel >= 4 && filterMode > 0 && filters && filters.length > 0) {
hitLocalFilter = filters.some(filter => { return allContents.includes(filter) });
}
if (hitGlobalFilter || hitLocalFilter) {
await deleteTempFiles(req).catch(e => console.error);
if (filterMode === 1) {
const useFilterMode = hitGlobalFilter ? globalSettings.filterMode : filterMode; //global override local filter
if (useFilterMode === 1) {
return dynamicResponse(req, res, 400, 'message', {
'title': 'Bad request',
'message': 'Your post was blocked by a word filter',
'redirect': redirect
});
} else if (filterMode === 2) {
} else { //otherwise filter mode must be 2
const useFilterBanDuration = hitGlobalFilter ? globalSettings.filterBanDuration : filterBanDuration;
const banBoard = hitGlobalFilter ? null : res.locals.board._id;
const banDate = new Date();
const banExpiry = new Date(filterBanDuration + banDate.getTime());
const banExpiry = new Date(useFilterBanDuration + banDate.getTime());
const ban = {
'ip': res.locals.ip.hash,
'reason': 'post word filter auto ban',
'board': res.locals.board._id,
'board': banBoard,
'posts': null,
'issuer': 'system', //what should i call this
'date': banDate,
@ -124,12 +139,13 @@ module.exports = async (req, res, next) => {
'seen': false
};
await Bans.insertOne(ban);
const bans = await Bans.find(res.locals.ip.hash, res.locals.board._id); //need to query db so it has _id field for unban checkmark
const bans = await Bans.find(res.locals.ip.hash, banBoard); //need to query db so it has _id field for appeal checkmark
return res.status(403).render('ban', {
bans: bans
});
}
}
}
let files = [];
// if we got a file
@ -140,7 +156,7 @@ module.exports = async (req, res, next) => {
await deleteTempFiles(req).catch(e => console.error);
return dynamicResponse(req, res, 400, 'message', {
'title': 'Bad request',
'message': `Mime type ${req.files.file[i].mimetype} for "${req.files.file[i].name}" not allowed.`,
'message': `Mime type "${req.files.file[i].mimetype}" for "${req.files.file[i].name}" not allowed.`,
'redirect': redirect
});
}

@ -6,4 +6,5 @@ module.exports = {
globalManageRecent: require(__dirname+'/recent.js'),
globalManageNews: require(__dirname+'/news.js'),
globalManageAccounts: require(__dirname+'/accounts.js'),
globalManageSettings: require(__dirname+'/settings.js'),
}

@ -0,0 +1,23 @@
'use strict';
const cache = require(__dirname+'/../../../redis.js');
module.exports = async (req, res, next) => {
let settings = await cache.get('globalsettings');
if (!settings) {
settings = {
captchaMode: 0,
filters: [],
filterMode: 0,
filterBanDuration: 0,
}
cache.set('globalsettings', settings);
}
res.render('globalmanagesettings', {
csrf: req.csrfToken(),
settings,
});
}

@ -21,10 +21,10 @@ const timeUtils = require(__dirname+'/../helpers/timeutils.js')
'task': 'updateStats',
'options': {}
}, {
'repeat': {
'cron': '0 * * * *'
}
});
'repeat': {
'cron': '0 * * * *'
}
});
//delete files for expired captchas
const deleteCaptchas = require(__dirname+'/deletecaptchas.js');

@ -1,3 +1,4 @@
extends ../layout.pug
block head
@ -10,6 +11,6 @@ block content
table.table-body
tr.table-row
td
p Do not post, link or promote any content that violates laws of the United States of America.
p Do not spam, flood or perform any actions to an extent that negatively impacts the usual function of the website/server.
p SFW marked boards should have primarily SFW content and ensure that any NSFW content is spoilered.
p Do not post, link or promote any content illegal under United States law.
p Do not spam, flood, (D)DoS, etc. which negatively impacts site performance.
p SFW marked boards must be primarily SFW and spoiler all NSFW content.

@ -10,4 +10,6 @@ mixin globalmanagenav(selected)
a(href='accounts.html' class=(selected === 'accounts' ? 'bold' : '')) [Accounts]
|
a(href='news.html' class=(selected === 'news' ? 'bold' : '')) [News]
|
a(href='settings.html' class=(selected === 'settings' ? 'bold' : '')) [Settings]

@ -0,0 +1,35 @@
extends ../layout.pug
include ../mixins/globalmanagenav.pug
block head
script(src='/js/all.js')
title Manage
block content
h1.board-title Global Management
br
+globalmanagenav('settings')
hr(size=1)
h4.no-m-p Settings:
.form-wrapper.flexleft
form.form-post(action=`/forms/global/settings`, enctype='application/x-www-form-urlencoded', method='POST')
input(type='hidden' name='_csrf' value=csrf)
.row
.label Captcha Mode
select(name='captcha_mode')
option(value='0', selected=settings.captchaMode === 0) No Captcha
option(value='1', selected=settings.captchaMode === 1) Captcha for new thread
option(value='2', selected=settings.captchaMode === 2) Captcha for all posts
.row
.label Filters
textarea(name='filters' placeholder='newline separated, max 50') #{settings.filters.join('\n')}
.row
.label Filter Mode
select(name='filter_mode')
option(value='0', selected=settings.filterMode === 0) Do nothing
option(value='1', selected=settings.filterMode === 1) Block post
option(value='2', selected=settings.filterMode === 2) Ban
.row
.label Filter Auto Ban Duration
input(type='text' name='ban_duration' placeholder='e.g. 1w' value=settings.filterBanDuration)
input(type='submit', value='save settings')
Loading…
Cancel
Save