'use strict' ;
const Mongo = require ( _ _dirname + '/../../db/db.js' )
, timeUtils = require ( _ _dirname + '/../converter/timeutils.js' )
, uploadDirectory = require ( _ _dirname + '/../file/uploaddirectory.js' )
, { remove } = require ( 'fs-extra' )
, config = require ( _ _dirname + '/../misc/config.js' )
, { debugLogs } = require ( _ _dirname + '/../../configs/secrets.js' )
, { CustomPages , Stats , Posts , Files , Boards , News , Modlogs } = require ( _ _dirname + '/../../db/' )
, cache = require ( _ _dirname + '/../redis/redis.js' )
, render = require ( _ _dirname + '/../build/render.js' )
, buildQueue = require ( _ _dirname + '/../build/queue.js' )
, gulp = require ( 'gulp' )
, { buildTasks } = require ( _ _dirname + '/../../gulpfile.js' )
, { timeDiffString } = require ( _ _dirname + '/../converter/timeutils.js' ) ;
module . exports = {
gulp : async ( options ) => {
/ * T O D O : c a l c u l a t e d i f f e r e n c e s i n o l d s e t t i n g s v s n e w s e t t i n g s i n g l o b a l m a n a g e s e t t i n g s m o d e l
and send task options with list of tasks instead of always doing all * /
const label = ` running gulp tasks [ ${ options . tasks . join ( ', ' ) } ] after global config change ` ;
const start = process . hrtime ( ) ;
gulp . series ( options . tasks . map ( t => buildTasks [ t ] ) , ( ) => {
const end = process . hrtime ( start ) ;
debugLogs && console . log ( timeDiffString ( label , end ) ) ;
} ) ( ) ;
} ,
buildBanners : async ( options ) => {
const label = ` / ${ options . board . _id } /banners.html ` ;
const start = process . hrtime ( ) ;
options . managePage = 'assets.html' ;
const { html } = await render ( label , 'banners.pug' , options , {
'name' : ` / ${ options . board . _id } /banners.json ` ,
'data' : options . board . banners
} ) ;
const end = process . hrtime ( start ) ;
debugLogs && console . log ( timeDiffString ( label , end ) ) ;
return html ;
} ,
buildCatalog : async ( options ) => {
const label = ` / ${ options . board . _id || options . board } /catalog.html ` ;
const start = process . hrtime ( ) ;
if ( ! options . board . _id ) {
options . board = await Boards . findOne ( options . board ) ;
}
options . managePage = 'catalog.html' ;
const threads = await Posts . getCatalog ( options . board . _id ) ;
const { html , json } = await render ( label , 'catalog.pug' , {
... options ,
threads ,
} , {
'name' : ` / ${ options . board . _id } /catalog.json ` ,
'data' : threads
} ) ;
const end = process . hrtime ( start ) ;
debugLogs && console . log ( timeDiffString ( label , end ) ) ;
return { html , json } ;
} ,
buildThread : async ( options ) => {
const label = ` / ${ options . board . _id || options . board } /thread/ ${ options . threadId } .html ` ;
const start = process . hrtime ( ) ;
if ( ! options . board . _id ) {
options . board = await Boards . findOne ( options . board ) ;
}
options . managePage = ` thread/ ${ options . threadId } .html ` ;
const thread = await Posts . getThread ( options . board . _id , options . threadId ) ;
if ( ! thread ) {
return ; //this thread may have been an OP that was deleted
}
const { html , json } = await render ( label , 'thread.pug' , {
... options ,
thread ,
} , {
'name' : ` / ${ options . board . _id } /thread/ ${ options . threadId } .json ` ,
'data' : thread
} ) ;
const end = process . hrtime ( start ) ;
debugLogs && console . log ( timeDiffString ( label , end ) ) ;
return { html , json } ;
} ,
buildBoard : async ( options ) => {
const label = ` / ${ options . board . _id } / ${ options . page === 1 ? 'index' : options . page } .html ` ;
const start = process . hrtime ( ) ;
const threads = await Posts . getRecent ( options . board . _id , options . page ) ;
if ( ! options . maxPage ) {
options . maxPage = Math . min ( Math . ceil ( ( await Posts . getPages ( options . board . _id ) ) / 10 ) , Math . ceil ( options . board . settings . threadLimit / 10 ) ) ;
}
const pageText = options . page === 1 ? 'index' : options . page ;
options . managePage = ` ${ pageText } .html ` ;
const { html , json } = await render ( label , 'board.pug' , {
... options ,
threads ,
} , {
'name' : ` / ${ options . board . _id } / ${ pageText } .json ` ,
'data' : threads
} ) ;
const end = process . hrtime ( start ) ;
debugLogs && console . log ( timeDiffString ( label , end ) ) ;
return { html , json } ;
} ,
//building multiple pages (for rebuilds)
buildBoardMultiple : async ( options ) => {
const start = process . hrtime ( ) ;
if ( ! options . board . _id ) {
options . board = await Boards . findOne ( options . board ) ;
}
const maxPage = Math . min ( Math . ceil ( ( await Posts . getPages ( options . board . _id ) ) / 10 ) , Math . ceil ( options . board . settings . threadLimit / 10 ) ) || 1 ;
if ( options . endpage === 0 ) {
//deleted only/all posts, so only 1 page will remain
options . endpage = 1 ;
} 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
}
const difference = options . endpage - options . startpage + 1 ; //+1 because for single pagemust be > 0
const threads = await Posts . getRecent ( options . board . _id , options . startpage , difference * 10 ) ;
const label = ` / ${ options . board . _id } / ${ options . startpage === 1 ? 'index' : options . startpage } ${ options . endpage === options . startpage ? '' : '->' + ( options . endpage === 1 ? 'index' : options . endpage ) } .html ` ;
const buildArray = [ ] ;
for ( let i = options . startpage ; i <= options . endpage ; i ++ ) {
let spliceStart = ( i - 1 ) * 10 ;
if ( spliceStart > 0 ) {
spliceStart = spliceStart - 1 ;
}
const pageThreads = threads . splice ( 0 , 10 ) ;
const pageText = i === 1 ? 'index' : i ;
buildArray . push (
render ( ` ${ options . board . _id } / ${ pageText } .html ` , 'board.pug' , {
board : options . board ,
threads : pageThreads ,
maxPage ,
page : i ,
managePage : ` ${ pageText } .html ` ,
} , {
'name' : ` / ${ options . board . _id } / ${ i === 1 ? 'index' : i } .json ` ,
'data' : pageThreads
} )
) ;
}
await Promise . all ( buildArray ) ;
const end = process . hrtime ( start ) ;
debugLogs && console . log ( timeDiffString ( label , end ) ) ;
} ,
buildNews : async ( ) => {
const label = '/news.html' ;
const start = process . hrtime ( ) ;
const news = await News . find ( ) ;
const { html } = await render ( 'news.html' , 'news.pug' , {
news
} ) ;
const end = process . hrtime ( start ) ;
debugLogs && console . log ( timeDiffString ( label , end ) ) ;
return html ;
} ,
buildCustomPage : async ( options ) => {
const label = ` / ${ options . board . _id || options . board } /custompage/ ${ options . page } .html ` ;
const start = process . hrtime ( ) ;
if ( ! options . customPage ) {
const customPage = await CustomPages . findOne ( options . board . _id || options . board , options . page ) ;
if ( ! customPage ) {
return ;
}
options . customPage = customPage ;
}
if ( ! options . board . _id ) {
options . board = await Boards . findOne ( options . board ) ;
}
options . managePage = 'custompages.html' ;
const { html } = await render ( label , 'custompage.pug' , {
... options ,
} ) ;
const end = process . hrtime ( start ) ;
debugLogs && console . log ( timeDiffString ( label , end ) ) ;
return html ;
} ,
buildModLog : async ( options ) => {
if ( ! options . startDate || ! options . endDate ) {
const d = new Date ( ) ;
const month = d . getUTCMonth ( )
, day = d . getUTCDate ( )
, year = d . getUTCFullYear ( ) ;
options . startDate = new Date ( Date . UTC ( year , month , day , 0 , 0 , 0 , 0 ) ) ;
options . endDate = new Date ( Date . UTC ( year , month , day , 23 , 59 , 59 , 999 ) ) ;
}
const day = ( '0' + options . startDate . getDate ( ) ) . slice ( - 2 ) ;
const month = ( '0' + ( options . startDate . getMonth ( ) + 1 ) ) . slice ( - 2 ) ;
const year = options . startDate . getFullYear ( ) ;
const label = ` / ${ options . board . _id } /logs/ ${ month } - ${ day } - ${ year } .html ` ;
const start = process . hrtime ( ) ;
if ( ! options . logs ) {
options . logs = await Modlogs . findBetweenDate ( options . board , options . startDate , options . endDate ) ;
}
options . managePage = 'logs.html' ;
const { html } = await render ( label , 'modlog.pug' , {
... options
} ) ;
const end = process . hrtime ( start ) ;
debugLogs && console . log ( timeDiffString ( label , end ) ) ;
return html ;
} ,
buildModLogList : async ( options ) => {
const label = ` / ${ options . board . _id } /logs.html ` ;
const start = process . hrtime ( ) ;
let dates = await Modlogs . getDates ( options . board ) ;
const { pruneModlogs } = config . get ;
if ( pruneModlogs ) {
const pruneLogs = [ ] ;
const pruneAfter = new Date ( Date . now ( ) - timeUtils . DAY * pruneModlogs ) ;
dates = dates . filter ( date => {
const { year , month , day } = date . date ;
if ( new Date ( year , month - 1 , day ) > pruneAfter ) { //-1 for 0-index months
return true ;
}
const logName = ` ${ month } - ${ day } - ${ year } ` ;
debugLogs && console . log ( 'Pruning log' , ` ${ options . board . _id } / ${ logName } ` )
pruneLogs . push ( logName ) ;
return false ;
} ) ;
if ( pruneLogs . length > 0 ) {
await Promise . all ( pruneLogs . map ( log => {
remove ( ` ${ uploadDirectory } /html/ ${ options . board . _id } /logs/ ${ log } .html ` )
} ) ) ;
await Modlogs . deleteOld ( options . board , pruneAfter ) ;
}
}
const { html } = await render ( label , 'modloglist.pug' , {
board : options . board ,
dates ,
managePage : 'logs.html' ,
} ) ;
const end = process . hrtime ( start ) ;
debugLogs && console . log ( timeDiffString ( label , end ) ) ;
return html ;
} ,
buildHomepage : async ( ) => {
const { maxRecentNews } = config . get ;
const label = '/index.html' ;
const start = process . hrtime ( ) ;
let [ totalStats , boards , fileStats , recentNews , hotThreads ] = await Promise . all ( [
Boards . totalStats ( ) , //overall total posts ever made
Boards . boardSort ( 0 , 20 ) , //top 20 boards sorted by users, pph, total posts
Files . activeContent ( ) , //size and number of files
News . find ( maxRecentNews ) , //some recent newsposts
Posts . db . find ( { 'board' : { $in : listedBoards } , 'thread' : null , 'date' : { $gte : ( new Date ( Date . now ( ) - ( 7 * 24 * 60 * 60 * 1000 ) ) ) } } ) . sort ( { 'replyposts' : - 1 } ) . limit ( 5 ) . toArray ( ) , //top 5 threads last 7 days
] ) ;
const [ localStats , webringStats , hotThreads ] = totalStats ;
const { html } = await render ( 'index.html' , 'home.pug' , {
localStats ,
webringStats ,
boards ,
fileStats ,
recentNews ,
} ) ;
const end = process . hrtime ( start ) ;
debugLogs && console . log ( timeDiffString ( label , end ) ) ;
return html ;
} ,
updateStats : async ( ) => {
const label = 'Hourly stats rollover' ;
const start = process . hrtime ( ) ;
await Stats . updateBoards ( ) ;
await Stats . resetStats ( ) ;
buildQueue . push ( {
'task' : 'buildHomepage' ,
} ) ;
const end = process . hrtime ( start ) ;
debugLogs && console . log ( timeDiffString ( label , end ) ) ;
module . exports . resetTriggers ( ) ;
} ,
resetTriggers : async ( ) => {
const label = 'Resetting pph/tph triggers' ;
const start = process . hrtime ( ) ;
const triggeredBoards = await cache . sgetall ( 'triggered' ) ; //boards triggered pph/tph mode
if ( triggeredBoards . length === 0 ) {
return ; //no label is no triggers
}
await cache . del ( 'triggered' ) ;
const triggerModes = await Boards . triggerModes ( triggeredBoards ) ;
const bulkWrites = triggerModes . map ( p => {
return {
'updateOne' : {
'filter' : {
'_id' : p . _id
} ,
'update' : {
'$set' : {
/ * r e s e t = 0 i s " n o c h a n g e " , t h e o p t i o n s g o f r o m 0 - 2 , a n d g e t r e s e t t o 0 o r 1 ,
so if > 0 , we subtract 1 otherwise no change * /
'settings.lockMode' : ( p . lockReset > 0 ? Math . min ( p . lockReset - 1 , p . lockMode ) : p . lockMode ) ,
'settings.captchaMode' : ( p . captchaReset > 0 ? Math . min ( p . captchaReset - 1 , p . captchaMode ) : p . captchaMode ) ,
}
}
}
}
} ) ;
await Boards . db . bulkWrite ( bulkWrites ) ;
const promises = [ ] ;
triggerModes . forEach ( async ( p ) => {
await cache . del ( ` board: ${ p . _id } ` ) ;
if ( p . captchaReset > 0 && p . captchaReset - 1 < p . captchaMode ) {
if ( p . captchaReset - 1 <= 1 ) {
promises . push ( remove ( ` ${ uploadDirectory } /html/ ${ p . _id } /thread/ ` ) ) ;
}
if ( p . captchaReset - 1 === 0 ) {
buildQueue . push ( {
'task' : 'buildBoardMultiple' ,
'options' : {
'board' : p . _id ,
'startpage' : 1 ,
'endpage' : Math . ceil ( p . threadLimit / 10 )
}
} )
buildQueue . push ( {
'task' : 'buildCatalog' ,
'options' : {
'board' : p . _id
}
} ) ;
}
}
} )
await Promise . all ( promises ) ;
const end = process . hrtime ( start ) ;
debugLogs && console . log ( timeDiffString ( label , end ) ) ;
} ,
buildChangePassword : async ( ) => {
const { html } = await render ( 'changepassword.html' , 'changepassword.pug' ) ;
return html ;
} ,
buildRegister : async ( ) => {
const { html } = await render ( 'register.html' , 'register.pug' ) ;
return html ;
} ,
buildBypass : async ( minimal = false ) => {
const { html } = await render ( ` bypass ${ minimal ? '_minimal' : '' } .html ` , 'bypass.pug' , {
minimal ,
} ) ;
return html ;
} ,
buildCreate : async ( ) => {
const { html } = await render ( 'create.html' , 'create.pug' ) ;
return html ;
} ,
buildCaptcha : async ( ) => {
const { html } = await render ( 'captcha.html' , 'captcha.pug' ) ;
return html ;
} ,
}