From 13ed2b2b9153a1e4ea3b9f2779a895da7ec1043d Mon Sep 17 00:00:00 2001 From: fatchan Date: Wed, 3 Jul 2019 10:21:19 +0000 Subject: [PATCH] improve actionhandler. not correctly handles case of page numbers decreasing on a delete action, removing further pages html correctly --- models/forms/actionhandler.js | 167 ++++++++++++++++--------------- models/forms/deletepostsfiles.js | 18 ++-- 2 files changed, 96 insertions(+), 89 deletions(-) diff --git a/models/forms/actionhandler.js b/models/forms/actionhandler.js index 3d385b59..180b5e69 100644 --- a/models/forms/actionhandler.js +++ b/models/forms/actionhandler.js @@ -14,6 +14,8 @@ const Posts = require(__dirname+'/../../db/posts.js') , globalReportPosts = require(__dirname+'/globalreportpost.js') , dismissReports = require(__dirname+'/dismissreport.js') , dismissGlobalReports = require(__dirname+'/dismissglobalreport.js') + , { remove } = require('fs-extra') + , uploadDirectory = require(__dirname+'/../../helpers/files/uploadDirectory.js') , { buildCatalog, buildThread, buildBoardMultiple } = require(__dirname+'/../../helpers/build.js'); module.exports = async (req, res, next) => { @@ -43,6 +45,30 @@ module.exports = async (req, res, next) => { passwordPosts = res.locals.posts; passwordPostMongoIds = postMongoIds; } + + //get a map of boards to threads affected + const boardThreadMap = {}; + for (let i = 0; i < res.locals.posts.length; i++) { + const post = res.locals.posts[i]; + if (!boardThreadMap[post.board]) { + boardThreadMap[post.board] = []; + } + if (!post.thread) { + //a thread was directly selected on this board, not just posts. so we handle deletes differently + boardThreadMap[post.board]['selectedThreads'] = true; + } + boardThreadMap[post.board].push(post.thread || post.postId); + } + + const beforePages = {}; + const threadBoards = Object.keys(boardThreadMap); + //get number of pages for each before actions for deleting old pages and changing page nav numbers incase number of pages changes + if (req.body.delete || req.body.delete_ip_board || req.body.delete_ip_global) { + await Promise.all(threadBoards.map(async board => { + beforePages[board] = Math.ceil((await Posts.getPages(board)) / 10); + })); + } + const messages = []; const combinedQuery = {}; const passwordCombinedQuery = {}; @@ -210,90 +236,65 @@ module.exports = async (req, res, next) => { }); } - //get a map of boards to threads affected - const boardThreadMap = {}; - const queryOrs = []; - for (let i = 0; i < res.locals.posts.length; i++) { - const post = res.locals.posts[i]; - if (!boardThreadMap[post.board]) { - boardThreadMap[post.board] = []; - } - if (!post.thread) { - //a thread was directly selected on this board, not just posts. so we handle deletes differently - boardThreadMap[post.board]['selectedThreads'] = true; - } - boardThreadMap[post.board].push(post.thread || post.postId); - } - - const beforePages = {}; - const threadBoards = Object.keys(boardThreadMap); - //get how many pages each board is to know whether we should rebuild all pages (because of page nav changes) - //only if deletes actions selected because this could result in number of pages to change - if (req.body.delete || req.body.delete_ip_board || req.body.delete_ip_global) { - await Promise.all(threadBoards.map(async board => { - beforePages[board] = Math.ceil((await Posts.getPages(board)) / 10); - })); - } - //execute actions now if (bulkWrites.length > 0) { await Posts.db.bulkWrite(bulkWrites); } - //get only posts (so we can use them for thread ids - if (aggregateNeeded) { - const selectedPosts = res.locals.posts.filter(post => post.thread !== null); - //recalculate replies and image counts - await Promise.all(selectedPosts.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); - })); - } - - //make it into an OR query for the db - for (let i = 0; i < threadBoards.length; i++) { - const threadBoard = threadBoards[i]; - boardThreadMap[threadBoard] = [...new Set(boardThreadMap[threadBoard])] - queryOrs.push({ - 'board': threadBoard, - 'postId': { - '$in': boardThreadMap[threadBoard] - } - }) - } - - //fetch threads per board that we only checked posts for - let threadsEachBoard = await Posts.db.find({ - 'thread': null, - '$or': queryOrs - }).toArray(); - //combine it with what we already had - const selectedThreads = res.locals.posts.filter(post => post.thread === null) - threadsEachBoard = threadsEachBoard.concat(selectedThreads) + //if there are actions that can cause some rebuilding + if (res.locals.actions.anyBuild > 0) { - //get the oldest and newest thread for each board to determine how to delete - const threadBounds = threadsEachBoard.reduce((acc, curr) => { - if (!acc[curr.board] || curr.bumped < acc[curr.board].bumped) { - acc[curr.board] = { oldest: null, newest: null}; - } - if (!acc[curr.board].oldest || curr.bumped < acc[curr.board].oldest.bumped) { - acc[curr.board].oldest = curr; + //get only posts (so we can use them for thread ids + if (aggregateNeeded) { + const selectedPosts = res.locals.posts.filter(post => post.thread !== null); + //recalculate replies and image counts + await Promise.all(selectedPosts.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 (!acc[curr.board].newest || curr.bumped > acc[curr.board].newest.bumped) { - acc[curr.board].newest = curr; + + //make it into an OR query for the db + const queryOrs = []; + for (let i = 0; i < threadBoards.length; i++) { + const threadBoard = threadBoards[i]; + boardThreadMap[threadBoard] = [...new Set(boardThreadMap[threadBoard])] + queryOrs.push({ + 'board': threadBoard, + 'postId': { + '$in': boardThreadMap[threadBoard] + } + }) } - return acc; - }, {}); - //if there are actions that can cause some rebuilding - //TODO: move this check earlier and move the db builkwrite earlier if possible - if (res.locals.actions.anyBuild > 0) { + //fetch threads per board that we only checked posts for + let threadsEachBoard = await Posts.db.find({ + 'thread': null, + '$or': queryOrs + }).toArray(); + //combine it with what we already had + const selectedThreads = res.locals.posts.filter(post => post.thread === null) + threadsEachBoard = threadsEachBoard.concat(selectedThreads) + + //get the oldest and newest thread for each board to determine how to delete + const threadBounds = threadsEachBoard.reduce((acc, curr) => { + if (!acc[curr.board] || curr.bumped < acc[curr.board].bumped) { + acc[curr.board] = { oldest: null, newest: null}; + } + if (!acc[curr.board].oldest || curr.bumped < acc[curr.board].oldest.bumped) { + acc[curr.board].oldest = curr; + } + if (!acc[curr.board].newest || curr.bumped > acc[curr.board].newest.bumped) { + acc[curr.board].newest = curr; + } + return acc; + }, {}); const parallelPromises = [] const boardNames = Object.keys(threadBounds); @@ -310,12 +311,6 @@ module.exports = async (req, res, next) => { for (let i = 0; i < boardNames.length; i++) { const boardName = boardNames[i]; const bounds = threadBounds[boardName]; - //always need to refresh catalog - /* - TODO: not rebuild catalog when a building action occurs that only affects thread posts - e.g. spoiler a post images that isnt an OP. This wont affect the catalog at all since - reply and image counts dont change, and the OP isnt changed in the catalog tile. - */ //rebuild impacted threads for (let j = 0; j < boardThreadMap[boardName].length; j++) { parallelPromises.push(buildThread(boardThreadMap[boardName][j], buildBoards[boardName])); @@ -324,9 +319,17 @@ module.exports = async (req, res, next) => { const afterPages = Math.ceil((await Posts.getPages(boardName)) / 10); let catalogRebuild = true; if (beforePages[boardName] && beforePages[boardName] !== afterPages) { - //amount of pages changed, rebuild all pages + //amount of pages changed, rebuild all pages and delete any further pages (if pages amount decreased) + if (afterPages < beforePages[boardName]) { + //amount of pages decreased + for (let k = beforePages[boardName]; k > afterPages; k--) { + //deleting html for pages that no longer should exist + parallelPromises.push(remove(`${uploadDirectory}html/${boardName}/${k}.html`)); + } + } parallelPromises.push(buildBoardMultiple(buildBoards[boardName], 1, afterPages)); } else { + //number of pages did not change, only possibly building existing pages const threadPageOldest = await Posts.getThreadPage(boardName, bounds.oldest); const threadPageNewest = bounds.oldest.postId === bounds.newest.postId ? threadPageOldest : await Posts.getThreadPage(boardName, bounds.newest); if (req.body.delete || req.body.delete_ip_board || req.body.delete_ip_global) { diff --git a/models/forms/deletepostsfiles.js b/models/forms/deletepostsfiles.js index 71593a30..36175554 100644 --- a/models/forms/deletepostsfiles.js +++ b/models/forms/deletepostsfiles.js @@ -7,18 +7,21 @@ module.exports = async (posts, unlinkOnly) => { //get filenames from all the posts let fileNames = []; - posts.forEach(post => { - fileNames = fileNames.concat(post.files.map(x => x.filename)) - }); + for (let i = 0; i < posts.length; i++) { + const post = posts[i]; + if (post.files.length > 0) { + fileNames = fileNames.concat(post.files.map(x => x.filename)); + } + } - if (fileNames.length === 0) { + if (fileNames.length == 0) { return { - message: 'No files found' - } + message: 'No files found' + }; } if (unlinkOnly) { -//TODO: decrement ref counters when implemented +//TODO: decrement ref counters here when implemented return { message:`Unlinked ${fileNames.length} file(s) across ${posts.length} post(s)`, action:'$set', @@ -27,6 +30,7 @@ module.exports = async (posts, unlinkOnly) => { } }; } else { +//TODO: delete ref counters for this file here //delete all the files using the filenames await Promise.all(fileNames.map(async filename => { //dont question it.