@ -43,7 +43,6 @@ module.exports = async (req, res, next) => {
passwordPosts = res . locals . posts ;
passwordPosts = res . locals . posts ;
passwordPostMongoIds = postMongoIds ;
passwordPostMongoIds = postMongoIds ;
}
}
const messages = [ ] ;
const messages = [ ] ;
const combinedQuery = { } ;
const combinedQuery = { } ;
const passwordCombinedQuery = { } ;
const passwordCombinedQuery = { } ;
@ -76,14 +75,25 @@ module.exports = async (req, res, next) => {
const deleteIpPosts = await Posts . db . find ( query ) . toArray ( ) ;
const deleteIpPosts = await Posts . db . find ( query ) . toArray ( ) ;
res . locals . posts = res . locals . 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 { action , message } = await deletePosts ( req , res , next , deleteIpPosts , req . params . board ) ;
messages . push ( message ) ;
messages . push ( message ) ;
aggregateNeeded = true ;
if ( action ) {
aggregateNeeded = true ;
} else {
req . body . delete _uiip _board = false ;
req . body . delete _ip _global = false ;
res . locals . actions . anyValid -- ;
}
}
}
} else if ( req . body . delete ) {
} else if ( req . body . delete ) {
const { message } = await deletePosts ( req , res , next , passwordPosts , req . params . board ) ;
const { action , message } = await deletePosts ( req , res , next , passwordPosts , req . params . board ) ;
messages . push ( message ) ;
messages . push ( message ) ;
aggregateNeeded = true ;
if ( action ) {
aggregateNeeded = true ;
} else {
req . body . delete = false ;
res . locals . actions . anyValid -- ;
}
} else {
} else {
// if it was getting deleted, we cant do any of these
// if it was getting deleted, we cant do any of these
if ( req . body . delete _file || req . body . unlink _file ) {
if ( req . body . delete _file || req . body . unlink _file ) {
@ -91,12 +101,19 @@ module.exports = async (req, res, next) => {
if ( action ) {
if ( action ) {
aggregateNeeded = true ;
aggregateNeeded = true ;
passwordCombinedQuery [ action ] = { ... passwordCombinedQuery [ action ] , ... query }
passwordCombinedQuery [ action ] = { ... passwordCombinedQuery [ action ] , ... query }
} else {
req . body . delete _file = false ;
req . body . unlink _file = false ;
res . locals . actions . anyValid -- ;
}
}
messages . push ( message ) ;
messages . push ( message ) ;
} else if ( req . body . spoiler ) {
} else if ( req . body . spoiler ) {
const { message , action , query } = spoilerPosts ( passwordPosts ) ;
const { message , action , query } = spoilerPosts ( passwordPosts ) ;
if ( action ) {
if ( action ) {
passwordCombinedQuery [ action ] = { ... passwordCombinedQuery [ action ] , ... query }
passwordCombinedQuery [ action ] = { ... passwordCombinedQuery [ action ] , ... query }
} else {
req . body . spoiler = false ;
res . locals . actions . anyValid -- ;
}
}
messages . push ( message ) ;
messages . push ( message ) ;
}
}
@ -105,6 +122,9 @@ module.exports = async (req, res, next) => {
const { message , action , query } = sagePosts ( res . locals . posts ) ;
const { message , action , query } = sagePosts ( res . locals . posts ) ;
if ( action ) {
if ( action ) {
combinedQuery [ action ] = { ... combinedQuery [ action ] , ... query }
combinedQuery [ action ] = { ... combinedQuery [ action ] , ... query }
} else {
req . body . sage = false ;
res . locals . actions . anyValid -- ;
}
}
messages . push ( message ) ;
messages . push ( message ) ;
}
}
@ -112,14 +132,20 @@ module.exports = async (req, res, next) => {
const { message , action , query } = lockPosts ( res . locals . posts ) ;
const { message , action , query } = lockPosts ( res . locals . posts ) ;
if ( action ) {
if ( action ) {
combinedQuery [ action ] = { ... combinedQuery [ action ] , ... query }
combinedQuery [ action ] = { ... combinedQuery [ action ] , ... query }
}
} else {
req . body . lock = false ;
res . locals . actions . anyValid -- ;
}
messages . push ( message ) ;
messages . push ( message ) ;
}
}
if ( req . body . sticky ) {
if ( req . body . sticky ) {
const { message , action , query } = stickyPosts ( res . locals . posts ) ;
const { message , action , query } = stickyPosts ( res . locals . posts ) ;
if ( action ) {
if ( action ) {
combinedQuery [ action ] = { ... combinedQuery [ action ] , ... query }
combinedQuery [ action ] = { ... combinedQuery [ action ] , ... query }
}
} else {
req . body . sticky = false ;
res . locals . actions . anyValid -- ;
}
messages . push ( message ) ;
messages . push ( message ) ;
}
}
// cannot report and dismiss at same time
// cannot report and dismiss at same time
@ -127,13 +153,16 @@ module.exports = async (req, res, next) => {
const { message , action , query } = reportPosts ( req , res . locals . 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 ( req . body . dismiss ) {
} else if ( req . body . dismiss ) {
const { message , action , query } = dismissReports ( res . locals . posts ) ;
const { message , action , query } = dismissReports ( res . locals . posts ) ;
if ( action ) {
if ( action ) {
combinedQuery [ action ] = { ... combinedQuery [ action ] , ... query }
combinedQuery [ action ] = { ... combinedQuery [ action ] , ... query }
}
} else {
req . body . dismiss = false ;
res . locals . actions . anyValid -- ;
}
messages . push ( message ) ;
messages . push ( message ) ;
}
}
// cannot report and dismiss at same time
// cannot report and dismiss at same time
@ -147,158 +176,177 @@ module.exports = async (req, res, next) => {
const { message , action , query } = dismissGlobalReports ( res . locals . posts ) ;
const { message , action , query } = dismissGlobalReports ( res . locals . posts ) ;
if ( action ) {
if ( action ) {
combinedQuery [ action ] = { ... combinedQuery [ action ] , ... query }
combinedQuery [ action ] = { ... combinedQuery [ action ] , ... query }
}
} else {
req . body . global _dismiss = false ;
res . locals . actions . anyValid -- ;
}
messages . push ( message ) ;
messages . push ( message ) ;
}
}
}
}
const bulkWrites = [ ]
if ( res . locals . actions . anyValid > 0 ) {
if ( Object . keys ( combinedQuery ) . length > 0 ) {
const bulkWrites = [ ] ;
bulkWrites . push ( {
if ( Object . keys ( combinedQuery ) . length > 0 ) {
'updateMany' : {
bulkWrites . push ( {
'filter' : {
'updateMany' : {
'_id' : {
'filter' : {
'$in' : postMongoIds
'_id' : {
}
'$in' : postMongoIds
} ,
}
'update' : combinedQuery
} ,
'update' : combinedQuery
}
} ) ;
}
if ( Object . keys ( passwordCombinedQuery ) . length > 0 ) {
bulkWrites . push ( {
'updateMany' : {
'filter' : {
'_id' : {
'$in' : passwordPostMongoIds
}
} ,
'update' : passwordCombinedQuery
}
} ) ;
}
//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
if ( Object . keys ( passwordCombinedQuery ) . length > 0 ) {
boardThreadMap [ post . board ] [ 'selectedThreads' ] = true ;
bulkWrites . push ( {
'updateMany' : {
'filter' : {
'_id' : {
'$in' : passwordPostMongoIds
}
} ,
'update' : passwordCombinedQuery
}
}
} ) ;
boardThreadMap [ post . board ] . push ( post . thread || post . postId ) ;
}
}
//get a map of boards to threads affected
const beforePages = { } ;
const boardThreadMap = { } ;
const threadBoards = Object . keys ( boardThreadMap ) ;
const queryOrs = [ ] ;
//get how many pages each board is to know whether we should rebuild all pages (because of page nav changes)
for ( let i = 0 ; i < res . locals . posts . length ; i ++ ) {
//only if deletes actions selected because this could result in number of pages to change
const post = res . locals . posts [ i ] ;
if ( req . body . delete || req . body . delete _ip _board || req . body . delete _ip _global ) {
if ( ! boardThreadMap [ post . board ] ) {
await Promise . all ( threadBoards . map ( async board => {
boardThreadMap [ post . board ] = [ ] ;
beforePages [ board ] = Math . ceil ( ( await Posts . getPages ( board ) ) / 10 ) ;
} ) ) ;
}
}
boardThreadMap [ post . board ] . push ( post . thread || post . postId ) ;
}
const beforePages = { } ;
//execute actions now
const threadBoards = Object . keys ( boardThreadMap ) ;
if ( bulkWrites . length > 0 ) {
//get how many pages each board is to know whether we should rebuild all pages (because of page nav changes)
await Posts . db . bulkWrite ( bulkWrites ) ;
//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
//get only posts (so we can use them for thread ids
if ( bulkWrites . length > 0 ) {
const selectedPosts = res . locals . posts . filter ( post => post . thread !== null ) ;
await Posts . db . bulkWrite ( bulkWrites ) ;
if ( aggregateNeeded ) {
}
//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 ) ;
} ) ) ;
}
//get only posts (so we can use them for thread ids
//make it into an OR query for the db
const postThreadsToUpdate = res . locals . posts . filter ( post => post . thread !== null ) ;
for ( let i = 0 ; i < threadBoards . length ; i ++ ) {
if ( aggregateNeeded ) {
const threadBoard = threadBoards [ i ] ;
//recalculate replies and image counts
boardThreadMap [ threadBoard ] = [ ... new Set ( boardThreadMap [ threadBoard ] ) ]
await Promise . all ( postThreadsToUpdate . map ( async ( post ) => {
queryOrs . push ( {
const replyCounts = await Posts . getReplyCounts ( post . board , post . thread ) ;
'board' : threadBoard ,
let replyposts = 0 ;
'postId' : {
let replyfiles = 0 ;
'$in' : boardThreadMap [ threadBoard ]
if ( replyCounts [ 0 ] ) {
}
replyposts = replyCounts [ 0 ] . replyposts ;
} )
replyfiles = replyCounts [ 0 ] . replyfiles ;
}
}
Posts . setReplyCounts ( post . board , post . thread , replyposts , replyfiles ) ;
//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 )
//make it into an OR query for the db
//get the oldest and newest thread for each board to determine how to delete
for ( let i = 0 ; i < threadBoards . length ; i ++ ) {
const threadBounds = threadsEachBoard . reduce ( ( acc , curr ) => {
const threadBoard = threadBoards [ i ] ;
if ( ! acc [ curr . board ] || curr . bumped < acc [ curr . board ] . bumped ) {
boardThreadMap [ threadBoard ] = [ ... new Set ( boardThreadMap [ threadBoard ] ) ]
acc [ curr . board ] = { oldest : null , newest : null } ;
queryOrs . push ( {
'board' : threadBoard ,
'postId' : {
'$in' : boardThreadMap [ threadBoard ]
}
}
} )
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 ;
} , { } ) ;
//fetch threads per board that we only checked posts for
//if there are actions that can cause some rebuilding
let threadsEachBoard = await Posts . db . find ( {
//TODO: move this check earlier and move the db builkwrite earlier if possible
'thread' : null ,
if ( res . locals . actions . anyBuild > 0 ) {
'$or' : queryOrs
} ) . toArray ( ) ;
//combine it with what we already had
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
const parallelPromises = [ ]
const threadBounds = threadsEachBoard . reduce ( ( acc , curr ) => {
const boardNames = Object . keys ( threadBounds ) ;
if ( ! acc [ curr . board ] || curr . bumped < acc [ curr . board ] . bumped ) {
const buildBoards = { } ;
acc [ curr . board ] = { oldest : null , newest : null } ;
const multiBoards = await Boards . db . find ( {
}
'_id' : {
if ( ! acc [ curr . board ] . oldest || curr . bumped < acc [ curr . board ] . oldest . bumped ) {
'$in' : boardNames
acc [ curr . board ] . oldest = curr ;
}
}
} ) . toArray ( ) ;
if ( ! acc [ curr . board ] . newest || curr . bumped > acc [ curr . board ] . newest . bumped ) {
multiBoards . forEach ( board => {
acc [ curr . board ] . newest = curr ;
buildBoards [ board . _id ] = board ;
}
} ) ;
return acc ;
} , { } ) ;
//now we need to delete outdated html
for ( let i = 0 ; i < boardNames . length ; i ++ ) {
//TODO: not do this for reports
const boardName = boardNames [ i ] ;
const parallelPromises = [ ]
const bounds = threadBounds [ boardName ] ;
const boardNames = Object . keys ( threadBounds ) ;
//always need to refresh catalog
const buildBoards = { } ;
parallelPromises . push ( buildCatalog ( buildBoards [ boardName ] ) ) ;
const multiBoards = await Boards . db . find ( {
//rebuild impacted threads
'_id' : {
for ( let j = 0 ; j < boardThreadMap [ boardName ] . length ; j ++ ) {
'$in' : boardNames
parallelPromises . push ( buildThread ( boardThreadMap [ boardName ] [ j ] , buildBoards [ boardName ] ) ) ;
}
}
} ) . toArray ( ) ;
//refersh any pages affected
multiBoards . forEach ( board => {
const afterPages = Math . ceil ( ( await Posts . getPages ( boardName ) ) / 10 ) ;
buildBoards [ board . _id ] = board ;
if ( beforePages [ boardName ] && beforePages [ boardName ] !== afterPages ) {
} )
//amount of pages changed, rebuild all pages
for ( let i = 0 ; i < boardNames . length ; i ++ ) {
parallelPromises . push ( buildBoardMultiple ( buildBoards [ boardName ] , 1 , afterPages ) ) ;
const boardName = boardNames [ i ] ;
} else {
const bounds = threadBounds [ boardName ] ;
const threadPageOldest = await Posts . getThreadPage ( boardName , bounds . oldest ) ;
//always need to refresh catalog
const threadPageNewest = bounds . oldest . postId === bounds . newest . postId ? threadPageOldest : await Posts . getThreadPage ( boardName , bounds . newest ) ;
parallelPromises . push ( buildCatalog ( buildBoards [ boardName ] ) ) ;
if ( req . body . delete || req . body . delete _ip _board || req . body . delete _ip _global ) {
//rebuild impacted threads
if ( ! boardThreadMap [ boardName ] . selectedThreads ) {
for ( let j = 0 ; j < boardThreadMap [ boardName ] . length ; j ++ ) {
//onyl deleting posts from threads, so thread order wont change, thus we dont delete all pages after
parallelPromises . push ( buildThread ( boardThreadMap [ boardName ] [ j ] , buildBoards [ boardName ] ) ) ;
parallelPromises . push ( buildBoardMultiple ( buildBoards [ boardName ] , threadPageNewest , threadPageOldest ) ) ;
}
} else {
//refersh any pages affected
//deleting threads, so we delete all pages after
const afterPages = Math . ceil ( ( await Posts . getPages ( boardName ) ) / 10 ) ;
parallelPromises . push ( buildBoardMultiple ( buildBoards [ boardName ] , threadPageNewest , afterPages ) ) ;
if ( beforePages [ boardName ] && beforePages [ boardName ] !== afterPages ) {
}
//amount of pages changed, rebuild all pages
} else if ( req . body . sticky ) { //else if -- if deleting, other actions are not executed/irrelevant
parallelPromises . push ( buildBoardMultiple ( buildBoards [ boardName ] , 1 , afterPages ) ) ;
//rebuild current and newer pages
} else {
parallelPromises . push ( buildBoardMultiple ( buildBoards [ boardName ] , 1 , threadPageOldest ) ) ;
const threadPageOldest = await Posts . getThreadPage ( boardName , bounds . oldest ) ;
} else if ( req . body . lock || req . body . sage || req . body . spoiler || req . body . ban || req . body . global _ban || req . body . unlink _file ) {
const threadPageNewest = await Posts . getThreadPage ( boardName , bounds . newest ) ;
//rebuild inbewteen pages for things that dont cause page/thread movement
if ( req . body . delete || req . body . delete _ip _board || req . body . delete _ip _global ) {
//should rebuild only affected pages, but finding the page of all affected
//rebuild current and older pages for deletes
//threads could end up being slower/more resource intensive. this is simpler.
parallelPromises . push ( buildBoardMultiple ( buildBoards [ boardName ] , threadPageNewest , afterPages ) ) ;
//it avoids rebuilding _some_ but not all pages unnecessarily
} else if ( req . body . sticky ) { //else if -- if deleting, other actions are not executed/irrelevant
parallelPromises . push ( buildBoardMultiple ( buildBoards [ boardName ] , threadPageNewest , threadPageOldest ) ) ;
//rebuild current and newer pages
}
parallelPromises . push ( buildBoardMultiple ( buildBoards [ boardName ] , 1 , threadPageOldest ) ) ;
}
} else if ( req . body . lock || req . body . sage || req . body . spoiler || req . body . ban || req . body . global _ban || req . body . unlink _file ) {
//rebuild inbewteen pages for things that dont cause page/thread movement
//should rebuild only affected pages, but finding the page of all affected
//threads could end up being slower/more resource intensive. this is simpler.
//it avoids rebuilding _some_ but not all pages unnecessarily
parallelPromises . push ( buildBoardMultiple ( buildBoards [ boardName ] , threadPageNewest , threadPageOldest ) ) ;
}
}
await Promise . all ( parallelPromises ) ;
}
}
}
}
await Promise . all ( parallelPromises ) ;
} catch ( err ) {
} catch ( err ) {
return next ( err ) ;
return next ( err ) ;