multi board rebuilds and rebuilds work for global actions untested

merge-requests/208/head
fatchan 5 years ago
parent 6255a45037
commit c54c9e7892
  1. 6
      build.js
  2. 197
      controllers/forms.js
  3. 2
      db/boards.js
  4. 30
      helpers/actionchecker.js
  5. 3
      helpers/haspermsmiddleware.js
  6. 154
      models/forms/actionhandler.js
  7. 1
      server.js

@ -8,6 +8,10 @@ const Posts = require(__dirname+'/db/posts.js')
module.exports = { module.exports = {
buildCatalog: async (board) => { buildCatalog: async (board) => {
//console.log('building catalog', `${board._id}/catalog.html`);
if (!board._id) {
board = await Boards.findOne(board);
}
const threads = await Posts.getCatalog(board._id); const threads = await Posts.getCatalog(board._id);
return render(`${board._id}/catalog.html`, 'catalog.pug', { return render(`${board._id}/catalog.html`, 'catalog.pug', {
board, board,
@ -22,7 +26,7 @@ module.exports = {
} }
const thread = await Posts.getThread(board._id, threadId) const thread = await Posts.getThread(board._id, threadId)
if (!thread) { if (!thread) {
return; //this thread may have been an OP that was deleted during a rebuild return; //this thread may have been an OP that was deleted
} }
return render(`${board._id}/thread/${threadId}.html`, 'thread.pug', { return render(`${board._id}/thread/${threadId}.html`, 'thread.pug', {
board, board,

@ -4,15 +4,8 @@ const express = require('express')
, router = express.Router() , router = express.Router()
, Boards = require(__dirname+'/../db/boards.js') , Boards = require(__dirname+'/../db/boards.js')
, Posts = require(__dirname+'/../db/posts.js') , Posts = require(__dirname+'/../db/posts.js')
, Captchas = require(__dirname+'/../db/captchas.js')
, Trips = require(__dirname+'/../db/trips.js')
, Bans = require(__dirname+'/../db/bans.js')
, Mongo = require(__dirname+'/../db/db.js') , Mongo = require(__dirname+'/../db/db.js')
, remove = require('fs-extra').remove , remove = require('fs-extra').remove
, deletePosts = require(__dirname+'/../models/forms/delete-post.js')
, spoilerPosts = require(__dirname+'/../models/forms/spoiler-post.js')
, dismissGlobalReports = require(__dirname+'/../models/forms/dismissglobalreport.js')
, banPoster = require(__dirname+'/../models/forms/ban-poster.js')
, removeBans = require(__dirname+'/../models/forms/removebans.js') , removeBans = require(__dirname+'/../models/forms/removebans.js')
, makePost = require(__dirname+'/../models/forms/make-post.js') , makePost = require(__dirname+'/../models/forms/make-post.js')
, uploadBanners = require(__dirname+'/../models/forms/uploadbanners.js') , uploadBanners = require(__dirname+'/../models/forms/uploadbanners.js')
@ -21,6 +14,7 @@ const express = require('express')
, changePassword = require(__dirname+'/../models/forms/changepassword.js') , changePassword = require(__dirname+'/../models/forms/changepassword.js')
, registerAccount = require(__dirname+'/../models/forms/register.js') , registerAccount = require(__dirname+'/../models/forms/register.js')
, checkPermsMiddleware = require(__dirname+'/../helpers/haspermsmiddleware.js') , checkPermsMiddleware = require(__dirname+'/../helpers/haspermsmiddleware.js')
, checkPerms = require(__dirname+'/../helpers/hasperms.js')
, paramConverter = require(__dirname+'/../helpers/paramconverter.js') , paramConverter = require(__dirname+'/../helpers/paramconverter.js')
, banCheck = require(__dirname+'/../helpers/bancheck.js') , banCheck = require(__dirname+'/../helpers/bancheck.js')
, deletePostFiles = require(__dirname+'/../helpers/files/deletepostfiles.js') , deletePostFiles = require(__dirname+'/../helpers/files/deletepostfiles.js')
@ -346,44 +340,73 @@ router.post('/board/:board/deletebanners', csrf, Boards.exists, checkPermsMiddle
}); });
//report/delete/spoiler/ban //actions for a specific board
router.post('/board/:board/actions', Boards.exists, banCheck, paramConverter, verifyCaptcha, actionHandler); //Captcha on regular actions router.post('/board/:board/actions', Boards.exists, banCheck, paramConverter, verifyCaptcha, boardActionController); //Captcha on regular actions
router.post('/board/:board/modactions', csrf, Boards.exists, checkPermsMiddleware, paramConverter, actionHandler); //CSRF for mod actions router.post('/board/:board/modactions', csrf, Boards.exists, checkPermsMiddleware, paramConverter, boardActionController); //CSRF for mod actions
async function boardActionController(req, res, next) {
//unban
router.post('/board/:board/unban', csrf, Boards.exists, checkPermsMiddleware, paramConverter, async (req, res, next) => {
//keep this for later in case i add other options to unbans
const errors = []; const errors = [];
if (!req.body.checkedbans || req.body.checkedbans.length === 0 || req.body.checkedbans.length > 10) { //make sure they checked 1-10 posts
errors.push('Must select 1-10 bans') if (!req.body.checkedposts || req.body.checkedposts.length === 0 || req.body.checkedposts.length > 10) {
errors.push('Must select 1-10 posts');
}
res.locals.actions = actionChecker(req);
//make sure they selected at least 1 action
if (!res.locals.actions.anyValid) {
errors.push('No actions selected');
}
//check if they have permission to perform the actions
res.locals.hasPerms = checkPerms(req, res);
if(!res.locals.hasPerms && res.locals.actions.anyAuthed) {
errors.push('No permission');
}
//check that actions are valid
if (req.body.password && req.body.password.length > 50) {
errors.push('Password must be 50 characters or less');
}
if (req.body.report_reason && req.body.report_reason.length > 50) {
errors.push('Report must be 50 characters or less');
}
if (req.body.ban_reason && req.body.ban_reason.length > 50) {
errors.push('Ban reason must be 50 characters or less');
}
if ((req.body.report || req.body.global_report) && (!req.body.report_reason || req.body.report_reason.length === 0)) {
errors.push('Reports must have a reason');
} }
if (errors.length > 0) { if (errors.length > 0) {
return res.status(400).render('message', { return res.status(400).render('message', {
'title': 'Bad request', 'title': 'Bad request',
'errors': errors, 'errors': errors,
'redirect': `/${req.params.board}/manage.html` 'redirect': `/${req.params.board}/`
}); })
}
res.locals.posts = await Posts.getPosts(req.params.board, req.body.checkedposts, true);
if (!res.locals.posts || res.locals.posts.length === 0) {
return res.status(404).render('message', {
'title': 'Not found',
'error': 'Selected posts not found',
'redirect': `/${req.params.board}/`
})
} }
const messages = [];
try { try {
messages.push((await removeBans(req, res, next))); await actionHandler(req, res, next);
} catch (err) { } catch (err) {
console.error(err);
return next(err); return next(err);
} }
return res.render('message', { }
'title': 'Success',
'messages': messages,
'redirect': `/${req.params.board}/manage.html`
});
});
router.post('/global/actions', csrf, checkPermsMiddleware, paramConverter, async(req, res, next) => { //global actions (global manage page)
router.post('/global/actions', csrf, checkPermsMiddleware, paramConverter, globalActionController);
async function globalActionController(req, res, next) {
const errors = []; const errors = [];
@ -392,10 +415,10 @@ router.post('/global/actions', csrf, checkPermsMiddleware, paramConverter, async
errors.push('Must select 1-10 posts') errors.push('Must select 1-10 posts')
} }
const { anyGlobal } = actionChecker(req); res.locals.actions = actionChecker(req);
//make sure they selected at least 1 global action //make sure they have any global actions, and that they only selected global actions
if (!anyGlobal) { if (!res.locals.actions.anyGlobal || res.locals.actions.anyValid > res.locals.actions.anyGlobal) {
errors.push('Invalid actions selected'); errors.push('Invalid actions selected');
} }
@ -403,15 +426,9 @@ router.post('/global/actions', csrf, checkPermsMiddleware, paramConverter, async
if (req.body.password && req.body.password.length > 50) { if (req.body.password && req.body.password.length > 50) {
errors.push('Password must be 50 characters or less'); errors.push('Password must be 50 characters or less');
} }
if (req.body.report_reason && req.body.report_reason.length > 50) {
errors.push('Report must be 50 characters or less');
}
if (req.body.ban_reason && req.body.ban_reason.length > 50) { if (req.body.ban_reason && req.body.ban_reason.length > 50) {
errors.push('Ban reason must be 50 characters or less'); errors.push('Ban reason must be 50 characters or less');
} }
if (req.body.report && (!req.body.report_reason || req.body.report_reason.length === 0)) {
errors.push('Reports must have a reason')
}
//return the errors //return the errors
if (errors.length > 0) { if (errors.length > 0) {
@ -423,8 +440,8 @@ router.post('/global/actions', csrf, checkPermsMiddleware, paramConverter, async
} }
//get posts with global ids only //get posts with global ids only
const posts = await Posts.globalGetPosts(req.body.globalcheckedposts, true); res.locals.posts = await Posts.globalGetPosts(req.body.globalcheckedposts, true);
if (!posts || posts.length === 0) { if (!res.locals.posts || res.locals.posts.length === 0) {
return res.status(404).render('message', { return res.status(404).render('message', {
'title': 'Not found', 'title': 'Not found',
'errors': 'Selected posts not found', 'errors': 'Selected posts not found',
@ -432,80 +449,36 @@ router.post('/global/actions', csrf, checkPermsMiddleware, paramConverter, async
}) })
} }
//get the ids
const postMongoIds = posts.map(post => Mongo.ObjectId(post._id));
const messages = [];
const combinedQuery = {};
let aggregateNeeded = false;
try { try {
if (req.body.global_ban) { await actionHandler(req, res, next);
const { message, action, query } = await banPoster(req, res, next, null, posts); } catch (err) {
if (action) { console.error(err);
combinedQuery[action] = { ...combinedQuery[action], ...query} return next(err);
} }
messages.push(message);
} }
if (req.body.delete_ip_global) {
const deletePostIps = posts.map(x => x.ip); //unban
const deleteIpPosts = await Posts.db.find({ router.post('/board/:board/unban', csrf, Boards.exists, checkPermsMiddleware, paramConverter, async (req, res, next) => {
'ip': {
'$in': deletePostIps //keep this for later in case i add other options to unbans
} const errors = [];
}).toArray();
if (deleteIpPosts && deleteIpPosts.length > 0) { if (!req.body.checkedbans || req.body.checkedbans.length === 0 || req.body.checkedbans.length > 10) {
const { message } = await deletePosts(req, res, next, deleteIpPosts, null); errors.push('Must select 1-10 bans')
messages.push(message);
aggregateNeeded = true;
}
} else if (req.body.delete) {
const { message } = await deletePosts(req, res, next, posts);
messages.push(message);
aggregateNeeded = true;
} else {
// if it was getting deleted, we cant do any of these
if (req.body.delete_file) {
const { message, action, query } = await deletePostsFiles(posts);
if (action) {
aggregateNeeded = true;
combinedQuery[action] = { ...combinedQuery[action], ...query}
}
messages.push(message);
} else if (req.body.spoiler) {
const { message, action, query } = spoilerPosts(posts);
if (action) {
combinedQuery[action] = { ...combinedQuery[action], ...query}
}
messages.push(message);
}
if (req.body.global_dismiss) {
const { message, action, query } = dismissGlobalReports(posts);
if (action) {
combinedQuery[action] = { ...combinedQuery[action], ...query}
}
messages.push(message);
}
}
if (Object.keys(combinedQuery).length > 0) {
await Posts.db.updateMany({
'_id': {
'$in': postMongoIds
}
}, combinedQuery);
}
if (aggregateNeeded) {
const threadsToUpdate = [...new Set(posts.filter(post => post.thread !== null))];
//recalculate and set correct aggregation numbers again
await Promise.all(threadsToUpdate.map(async (post) => {
const replyCounts = await Posts.getReplyCounts(post.board, post.thread);
let replyposts = 0;
let replyfiles = 0;
if (replyCounts[0]) {
replyposts = replyCounts[0].replyposts;
replyfiles = replyCounts[0].replyfiles;
}
Posts.setReplyCounts(post.board, post.thread, replyposts, replyfiles);
}));
} }
if (errors.length > 0) {
return res.status(400).render('message', {
'title': 'Bad request',
'errors': errors,
'redirect': `/${req.params.board}/manage.html`
});
}
const messages = [];
try {
messages.push((await removeBans(req, res, next)));
} catch (err) { } catch (err) {
return next(err); return next(err);
} }
@ -513,7 +486,7 @@ router.post('/global/actions', csrf, checkPermsMiddleware, paramConverter, async
return res.render('message', { return res.render('message', {
'title': 'Success', 'title': 'Success',
'messages': messages, 'messages': messages,
'redirect': '/globalmanage.html' 'redirect': `/${req.params.board}/manage.html`
}); });
}); });

@ -5,7 +5,7 @@ const Mongo = require(__dirname+'/db.js')
module.exports = { module.exports = {
db, db: db.collection('boards'),
findOne: (name) => { findOne: (name) => {
return db.collection('boards').findOne({ '_id': name }); return db.collection('boards').findOne({ '_id': name });

@ -1,16 +1,16 @@
'use strict'; 'use strict';
const actions = [ const actions = [
{name:'delete_file', global:true, auth:false, passwords:true},
{name:'spoiler', global:true, auth:false, passwords:true},
{name:'delete', global:true, auth:false, passwords:true},
{name:'lock', global:false, auth:true, passwords:false}, {name:'lock', global:false, auth:true, passwords:false},
{name:'sticky', global:false, auth:true, passwords:false}, {name:'sticky', global:false, auth:true, passwords:false},
{name:'sage', global:false, auth:true, passwords:false}, {name:'sage', global:false, auth:true, passwords:false},
{name:'report', global:false, auth:false, passwords:false}, {name:'report', global:false, auth:false, passwords:false},
{name:'global_report', global:false, auth:false, passwords:false}, {name:'global_report', global:false, auth:false, passwords:false},
{name:'spoiler', global:true, auth:false, passwords:true},
{name:'delete', global:true, auth:false, passwords:true},
{name:'delete_ip_board', global:false, auth:true, passwords:false}, {name:'delete_ip_board', global:false, auth:true, passwords:false},
{name:'delete_ip_global', global:true, auth:true, passwords:false}, {name:'delete_ip_global', global:true, auth:true, passwords:false},
{name:'delete_file', global:true, auth:false, passwords:true},
{name:'dismiss', global:false, auth:true, passwords:false}, {name:'dismiss', global:false, auth:true, passwords:false},
{name:'global_dismiss', global:true, auth:true, passwords:false}, {name:'global_dismiss', global:true, auth:true, passwords:false},
{name:'ban', global:false, auth:true, passwords:false}, {name:'ban', global:false, auth:true, passwords:false},
@ -19,26 +19,24 @@ const actions = [
module.exports = (req, res) => { module.exports = (req, res) => {
let anyGlobal = false let anyGlobal = 0
, anyAuthed = false , anyAuthed = 0
, anyPasswords = false , anyPasswords = 0
, anyValid = false; , anyValid = 0;
for (let i = 0; i < actions.length; i++) { for (let i = 0; i < actions.length; i++) {
const action = actions[i]; const action = actions[i];
const bodyHasAction = req.body[action.name]; const bodyHasAction = req.body[action.name];
if (bodyHasAction) { if (bodyHasAction) {
if (!anyGlobal && action.global) { anyValid++;
anyGlobal = true; if (action.global) {
} anyGlobal++;
if (!anyAuthed && action.auth) {
anyAuthed = true;
} }
if (!anyPasswords && action.passwords) { if (action.auth) {
anyPasswords = true; anyAuthed++;
} }
if (!anyValid) { if (action.passwords) {
anyValid = true; anyPasswords++;
} }
} }
if (anyGlobal && anyAuthed && anyValid) { if (anyGlobal && anyAuthed && anyValid) {

@ -4,7 +4,8 @@ const hasPerms = require(__dirname+'/hasperms.js');
module.exports = async (req, res, next) => { module.exports = async (req, res, next) => {
if (!hasPerms(req, res)) { res.locals.hasPerms = hasPerms(req, res);
if (!res.locals.hasPerms) {
return res.status(403).render('message', { return res.status(403).render('message', {
'title': 'Forbidden', 'title': 'Forbidden',
'message': 'You do not have permission to access this page', 'message': 'You do not have permission to access this page',

@ -1,6 +1,7 @@
'use strict'; 'use strict';
const Posts = require(__dirname+'/../../db/posts.js') const Posts = require(__dirname+'/../../db/posts.js')
, Boards = require(__dirname+'/../../db/boards.js')
, Mongo = require(__dirname+'/../../db/db.js') , Mongo = require(__dirname+'/../../db/db.js')
, banPoster = require(__dirname+'/ban-poster.js') , banPoster = require(__dirname+'/ban-poster.js')
, deletePosts = require(__dirname+'/delete-post.js') , deletePosts = require(__dirname+'/delete-post.js')
@ -13,73 +14,17 @@ const Posts = require(__dirname+'/../../db/posts.js')
, globalReportPosts = require(__dirname+'/globalreportpost.js') , globalReportPosts = require(__dirname+'/globalreportpost.js')
, dismissReports = require(__dirname+'/dismiss-report.js') , dismissReports = require(__dirname+'/dismiss-report.js')
, dismissGlobalReports = require(__dirname+'/dismissglobalreport.js') , dismissGlobalReports = require(__dirname+'/dismissglobalreport.js')
, actionChecker = require(__dirname+'/../../helpers/actionchecker.js')
, checkPerms = require(__dirname+'/../../helpers/hasperms.js')
, remove = require('fs-extra').remove
, uploadDirectory = require(__dirname+'/../../helpers/uploadDirectory.js')
, { buildCatalog, buildThread, buildBoardMultiple } = require(__dirname+'/../../build.js'); , { buildCatalog, buildThread, buildBoardMultiple } = require(__dirname+'/../../build.js');
module.exports = async (req, res, next) => { module.exports = async (req, res, next) => {
const errors = [];
//make sure they checked 1-10 posts
if (!req.body.checkedposts || req.body.checkedposts.length === 0 || req.body.checkedposts.length > 10) {
errors.push('Must select 1-10 posts');
}
//get what type of actions
const { anyPasswords, anyAuthed, anyValid } = actionChecker(req);
//make sure they selected at least 1 action
if (!anyValid) {
errors.push('No actions selected');
}
//check if they have permission to perform the actions
const hasPerms = checkPerms(req, res);
if(!hasPerms && anyAuthed) {
errors.push('No permission');
}
//check that actions are valid
if (req.body.password && req.body.password.length > 50) {
errors.push('Password must be 50 characters or less');
}
if (req.body.report_reason && req.body.report_reason.length > 50) {
errors.push('Report must be 50 characters or less');
}
if (req.body.ban_reason && req.body.ban_reason.length > 50) {
errors.push('Ban reason must be 50 characters or less');
}
if ((req.body.report || req.body.global_report) && (!req.body.report_reason || req.body.report_reason.length === 0)) {
errors.push('Reports must have a reason');
}
if (errors.length > 0) {
return res.status(400).render('message', {
'title': 'Bad request',
'errors': errors,
'redirect': `/${req.params.board}/`
})
}
let posts = await Posts.getPosts(req.params.board, req.body.checkedposts, true);
if (!posts || posts.length === 0) {
return res.status(404).render('message', {
'title': 'Not found',
'error': 'Selected posts not found',
'redirect': `/${req.params.board}/`
})
}
//get the ids //get the ids
const postMongoIds = posts.map(post => Mongo.ObjectId(post._id)); const postMongoIds = res.locals.posts.map(post => Mongo.ObjectId(post._id));
let passwordPostMongoIds = []; let passwordPostMongoIds = [];
let passwordPosts = []; let passwordPosts = [];
if (!hasPerms && anyPasswords) { if (!res.locals.hasPerms && res.locals.actions.anyPasswords) {
//just to avoid multiple filters and mapping, do it all here //just to avoid multiple filters and mapping, do it all here
passwordPosts = posts.filter(post => { passwordPosts = res.locals.posts.filter(post => {
if (post.password != null if (post.password != null
&& post.password.length > 0 && post.password.length > 0
&& post.password == req.body.password) { && post.password == req.body.password) {
@ -91,11 +36,11 @@ module.exports = async (req, res, next) => {
return res.status(403).render('message', { return res.status(403).render('message', {
'title': 'Forbidden', 'title': 'Forbidden',
'error': 'Password did not match any selected posts', 'error': 'Password did not match any selected posts',
'redirect': `/${req.params.board}/` 'redirect': `/${req.params.board ? req.params.board+'/' : 'globalmanage.html'}`
}); });
} }
} else { } else {
passwordPosts = posts; passwordPosts = res.locals.posts;
passwordPostMongoIds = postMongoIds; passwordPostMongoIds = postMongoIds;
} }
@ -104,24 +49,24 @@ module.exports = async (req, res, next) => {
const passwordCombinedQuery = {}; const passwordCombinedQuery = {};
let aggregateNeeded = false; let aggregateNeeded = false;
try { try {
if (hasPerms) { if (res.locals.hasPerms) {
// if getting global banned, board ban doesnt matter // if getting global banned, board ban doesnt matter
if (req.body.global_ban) { if (req.body.global_ban) {
const { message, action, query } = await banPoster(req, res, next, null, posts); const { message, action, query } = await banPoster(req, res, next, null, res.locals.posts);
if (action) { if (action) {
combinedQuery[action] = { ...combinedQuery[action], ...query} combinedQuery[action] = { ...combinedQuery[action], ...query}
} }
messages.push(message); messages.push(message);
} else if (req.body.ban) { } else if (req.body.ban) {
const { message, action, query } = await banPoster(req, res, next, req.params.board, posts); const { message, action, query } = await banPoster(req, res, next, req.params.board, res.locals.posts);
if (action) { if (action) {
combinedQuery[action] = { ...combinedQuery[action], ...query} combinedQuery[action] = { ...combinedQuery[action], ...query}
} }
messages.push(message); messages.push(message);
} }
} }
if (hasPerms && (req.body.delete_ip_board || req.body.delete_ip_global)) { if (res.locals.hasPerms && (req.body.delete_ip_board || req.body.delete_ip_global)) {
const deletePostIps = posts.map(x => x.ip); const deletePostIps = res.locals.posts.map(x => x.ip);
let query = { let query = {
'ip': { 'ip': {
'$in': deletePostIps '$in': deletePostIps
@ -131,7 +76,7 @@ module.exports = async (req, res, next) => {
query['board'] = req.params.board; query['board'] = req.params.board;
} }
const deleteIpPosts = await Posts.db.find(query).toArray(); const deleteIpPosts = await Posts.db.find(query).toArray();
posts = posts.concat(deleteIpPosts); res.locals.posts = res.locals.posts.concat(deleteIpPosts);
if (deleteIpPosts && deleteIpPosts.length > 0) { if (deleteIpPosts && deleteIpPosts.length > 0) {
const { message } = await deletePosts(req, res, next, deleteIpPosts, req.params.board); const { message } = await deletePosts(req, res, next, deleteIpPosts, req.params.board);
messages.push(message); messages.push(message);
@ -157,24 +102,24 @@ module.exports = async (req, res, next) => {
} }
messages.push(message); messages.push(message);
} }
if (hasPerms) { if (res.locals.hasPerms) {
//lock, sticky, sage //lock, sticky, sage
if (req.body.sage) { if (req.body.sage) {
const { message, action, query } = sagePosts(posts); const { message, action, query } = sagePosts(res.locals.posts);
if (action) { if (action) {
combinedQuery[action] = { ...combinedQuery[action], ...query} combinedQuery[action] = { ...combinedQuery[action], ...query}
} }
messages.push(message); messages.push(message);
} }
if (req.body.lock) { if (req.body.lock) {
const { message, action, query } = lockPosts(posts); const { message, action, query } = lockPosts(res.locals.posts);
if (action) { if (action) {
combinedQuery[action] = { ...combinedQuery[action], ...query} combinedQuery[action] = { ...combinedQuery[action], ...query}
} }
messages.push(message); messages.push(message);
} }
if (req.body.sticky) { if (req.body.sticky) {
const { message, action, query } = stickyPosts(posts); const { message, action, query } = stickyPosts(res.locals.posts);
if (action) { if (action) {
combinedQuery[action] = { ...combinedQuery[action], ...query} combinedQuery[action] = { ...combinedQuery[action], ...query}
} }
@ -183,13 +128,13 @@ module.exports = async (req, res, next) => {
} }
// cannot report and dismiss at same time // cannot report and dismiss at same time
if (req.body.report) { if (req.body.report) {
const { message, action, query } = reportPosts(req, posts); const { message, action, query } = reportPosts(req, res.locals.posts);
if (action) { if (action) {
combinedQuery[action] = { ...combinedQuery[action], ...query} combinedQuery[action] = { ...combinedQuery[action], ...query}
} }
messages.push(message); messages.push(message);
} else if (hasPerms && req.body.dismiss) { } else if (res.locals.hasPerms && req.body.dismiss) {
const { message, action, query } = dismissReports(posts); const { message, action, query } = dismissReports(res.locals.posts);
if (action) { if (action) {
combinedQuery[action] = { ...combinedQuery[action], ...query} combinedQuery[action] = { ...combinedQuery[action], ...query}
} }
@ -197,13 +142,13 @@ module.exports = async (req, res, next) => {
} }
// cannot report and dismiss at same time // cannot report and dismiss at same time
if (req.body.global_report) { if (req.body.global_report) {
const { message, action, query } = globalReportPosts(req, posts); const { message, action, query } = globalReportPosts(req, res.locals.posts);
if (action) { if (action) {
combinedQuery[action] = { ...combinedQuery[action], ...query} combinedQuery[action] = { ...combinedQuery[action], ...query}
} }
messages.push(message); messages.push(message);
} else if (hasPerms && req.body.global_dismiss) { } else if (res.locals.hasPerms && req.body.global_dismiss) {
const { message, action, query } = dismissGlobalReports(posts); const { message, action, query } = dismissGlobalReports(res.locals.posts);
if (action) { if (action) {
combinedQuery[action] = { ...combinedQuery[action], ...query} combinedQuery[action] = { ...combinedQuery[action], ...query}
} }
@ -239,8 +184,8 @@ module.exports = async (req, res, next) => {
//get a map of boards to threads affected //get a map of boards to threads affected
const boardThreadMap = {}; const boardThreadMap = {};
const queryOrs = []; const queryOrs = [];
for (let i = 0; i < posts.length; i++) { for (let i = 0; i < res.locals.posts.length; i++) {
const post = posts[i]; const post = res.locals.posts[i];
if (!boardThreadMap[post.board]) { if (!boardThreadMap[post.board]) {
boardThreadMap[post.board] = []; boardThreadMap[post.board] = [];
} }
@ -263,7 +208,7 @@ module.exports = async (req, res, next) => {
} }
//get only posts (so we can use them for thread ids //get only posts (so we can use them for thread ids
const postThreadsToUpdate = posts.filter(post => post.thread !== null); const postThreadsToUpdate = res.locals.posts.filter(post => post.thread !== null);
if (aggregateNeeded) { if (aggregateNeeded) {
//recalculate replies and image counts //recalculate replies and image counts
await Promise.all(postThreadsToUpdate.map(async (post) => { await Promise.all(postThreadsToUpdate.map(async (post) => {
@ -296,7 +241,7 @@ module.exports = async (req, res, next) => {
'$or': queryOrs '$or': queryOrs
}).toArray(); }).toArray();
//combine it with what we already had //combine it with what we already had
threadsEachBoard = threadsEachBoard.concat(posts.filter(post => post.thread === null)) threadsEachBoard = threadsEachBoard.concat(res.locals.posts.filter(post => post.thread === null))
//get the oldest and newest thread for each board to determine how to delete //get the oldest and newest thread for each board to determine how to delete
const threadBounds = threadsEachBoard.reduce((acc, curr) => { const threadBounds = threadsEachBoard.reduce((acc, curr) => {
@ -315,36 +260,45 @@ module.exports = async (req, res, next) => {
//now we need to delete outdated html //now we need to delete outdated html
//TODO: not do this for reports, handle global actions & move to separate handler + optimize and test //TODO: not do this for reports, handle global actions & move to separate handler + optimize and test
const parallelPromises = [] const parallelPromises = []
const boardsWithChanges = Object.keys(threadBounds); const boardNames = Object.keys(threadBounds);
for (let i = 0; i < boardsWithChanges.length; i++) { const buildBoards = {};
const changeBoard = boardsWithChanges[i]; const multiBoards = await Boards.db.find({
const bounds = threadBounds[changeBoard]; '_id': {
'$in': boardNames
}
}).toArray();
multiBoards.forEach(board => {
buildBoards[board._id] = board;
})
for (let i = 0; i < boardNames.length; i++) {
const boardName = boardNames[i];
const bounds = threadBounds[boardName];
//always need to refresh catalog //always need to refresh catalog
parallelPromises.push(buildCatalog(res.locals.board)); parallelPromises.push(buildCatalog(buildBoards[boardName]));
//rebuild impacted threads //rebuild impacted threads
for (let j = 0; j < boardThreadMap[changeBoard].length; j++) { for (let j = 0; j < boardThreadMap[boardName].length; j++) {
parallelPromises.push(buildThread(boardThreadMap[changeBoard][j], changeBoard)); parallelPromises.push(buildThread(boardThreadMap[boardName][j], buildBoards[boardName]));
} }
//refersh any pages affected //refersh any pages affected
const afterPages = Math.ceil((await Posts.getPages(changeBoard)) / 10); const afterPages = Math.ceil((await Posts.getPages(boardName)) / 10);
if (beforePages[changeBoard] && beforePages[changeBoard] !== afterPages) { if (beforePages[boardName] && beforePages[boardName] !== afterPages) {
//amount of pages changed, rebuild all pages //amount of pages changed, rebuild all pages
parallelPromises.push(buildBoardMultiple(res.locals.board, 1, afterPages)); parallelPromises.push(buildBoardMultiple(buildBoards[boardName], 1, afterPages));
} else { } else {
const threadPageOldest = await Posts.getThreadPage(req.params.board, bounds.oldest); const threadPageOldest = await Posts.getThreadPage(boardName, bounds.oldest);
const threadPageNewest = await Posts.getThreadPage(req.params.board, bounds.newest); const threadPageNewest = await Posts.getThreadPage(boardName, bounds.newest);
if (req.body.delete || req.body.delete_ip_board || req.body.delete_ip_global) { if (req.body.delete || req.body.delete_ip_board || req.body.delete_ip_global) {
//rebuild current and older pages for deletes //rebuild current and older pages for deletes
parallelPromises.push(buildBoardMultiple(res.locals.board, threadPageNewest, afterPages)); parallelPromises.push(buildBoardMultiple(buildBoards[boardName], threadPageNewest, afterPages));
} else if (req.body.sticky) { //else if -- if deleting, other actions are not executed/irrelevant } else if (req.body.sticky) { //else if -- if deleting, other actions are not executed/irrelevant
//rebuild current and newer pages for stickies //rebuild current and newer pages for stickies
parallelPromises.push(buildBoardMultiple(res.locals.board, 1, threadPageOldest)); parallelPromises.push(buildBoardMultiple(buildBoards[boardName], 1, threadPageOldest));
} else if ((hasPerms && (req.body.lock || req.body.sage)) || req.body.spoiler) { } else if ((res.locals.hasPerms && (req.body.lock || req.body.sage)) || req.body.spoiler) {
//rebuild inbewteen pages for things that dont cause page/thread movement //rebuild inbewteen pages for things that dont cause page/thread movement
//should rebuild only affected pages, but finding the page of all affected //should rebuild only affected pages, but finding the page of all affected
//threads could end up being slower/more resource intensive. this is simpler //threads could end up being slower/more resource intensive. this is simpler.
//but still avoids rebuilding _some_ pages unnecessarily //it avoids rebuilding _some_ but not all pages unnecessarily
parallelPromises.push(buildBoardMultiple(res.locals.board, threadPageNewest, threadPageOldest)); parallelPromises.push(buildBoardMultiple(buildBoards[boardName], threadPageNewest, threadPageOldest));
} }
} }
} }
@ -357,7 +311,7 @@ module.exports = async (req, res, next) => {
return res.render('message', { return res.render('message', {
'title': 'Success', 'title': 'Success',
'messages': messages, 'messages': messages,
'redirect': `/${req.params.board}/` 'redirect': `/${req.params.board ? req.params.board+'/' : 'globalmanage.html'}`
}); });
} }

@ -115,6 +115,7 @@ const express = require('express')
server.close((err) => { server.close((err) => {
// if error, log and exit with error (1 code) // if error, log and exit with error (1 code)
console.info('closing http server')
if (err) { if (err) {
console.error(err); console.error(err);
process.exit(1); process.exit(1);

Loading…
Cancel
Save