diff --git a/configs/main.js.example b/configs/main.js.example index 5ab02c0a..0cdef370 100644 --- a/configs/main.js.example +++ b/configs/main.js.example @@ -246,7 +246,9 @@ module.exports = { tphTrigger: 0, //numebr of threads in an hour before trigger action is activated pphTrigger: 0, //number of posts in an hour before ^ triggerAction: 0, //0=nothing, 1=captcha enable for threads, 2=captcha enable for all posts, 3=lock board + resetTrigger: false, //reset captcha/lock settings back to original at the end of hour forceAnon: false, //disable name and subject, only allow sage email + sageOnlyEmail: false, //only allow sage email early404: true, //delete threads beyond the first 1/3 of pages with less than 5 replies ids: false, //show per thread poster ID based on ip flags: false, //show geo flags, requires nginx setup diff --git a/configs/webring.json.example b/configs/webring.json.example index d71899af..b565da55 100644 --- a/configs/webring.json.example +++ b/configs/webring.json.example @@ -3,7 +3,7 @@ "https://example.com/webring.json" ], "blacklist": [ - "badwebsite.com", + "badwebsite.com" ], "logo": [ "https://yourdomain.com/favicon.ico" diff --git a/controllers/forms.js b/controllers/forms.js index 8e3c2d4b..6ad8484a 100644 --- a/controllers/forms.js +++ b/controllers/forms.js @@ -89,7 +89,7 @@ router.post('/board/:board/modpost', processIp, sessionRefresh, Boards.exists, c //post actions router.post('/board/:board/actions', processIp, sessionRefresh, Boards.exists, calcPerms, banCheck, paramConverter, verifyCaptcha, actionController); //public, with captcha router.post('/board/:board/modactions', processIp, sessionRefresh, csrf, Boards.exists, calcPerms, banCheck, isLoggedIn, hasPerms(3), paramConverter, actionController); //board manage page -router.post('/global/actions', sessionRefresh, csrf, calcPerms, isLoggedIn, hasPerms(1), paramConverter, globalActionController); //global manage page +router.post('/global/actions', processIp, sessionRefresh, csrf, calcPerms, isLoggedIn, hasPerms(1), paramConverter, globalActionController); //global manage page //appeal ban router.post('/appeal', processIp, sessionRefresh, paramConverter, verifyCaptcha, appealController); //edit post @@ -105,6 +105,7 @@ router.post('/board/:board/deleteboard', processIp, sessionRefresh, csrf, Boards //global management forms router.post('/global/editbans', sessionRefresh, csrf, calcPerms, isLoggedIn, hasPerms(1), paramConverter, editBansController); //remove bans +router.post('/global/deleteboard', sessionRefresh, csrf, paramConverter, calcPerms, isLoggedIn, hasPerms(1), deleteBoardController); //delete board 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 diff --git a/controllers/forms/deleteboard.js b/controllers/forms/deleteboard.js index cceadf2d..28598a78 100644 --- a/controllers/forms/deleteboard.js +++ b/controllers/forms/deleteboard.js @@ -22,6 +22,7 @@ module.exports = async (req, res, next) => { //no need to check these if the board name is completely invalid if (req.params.board != null && req.params.board !== req.body.uri) { //board manage page to not be able to delete other boards; + //req.params.board will be null on global delete, so this wont happen errors.push('URI does not match current board'); } try { @@ -39,7 +40,7 @@ module.exports = async (req, res, next) => { return dynamicResponse(req, res, 400, 'message', { 'title': 'Bad request', 'errors': errors, - 'redirect': req.params.board ? `/${req.params.board}/manage/settings.html` : '/globalmanage/recent.html' + 'redirect': req.params.board ? `/${req.params.board}/manage/settings.html` : '/globalmanage/settings.html' }); } @@ -52,7 +53,7 @@ module.exports = async (req, res, next) => { return dynamicResponse(req, res, 200, 'message', { 'title': 'Success', 'message': 'Board deleted', - 'redirect': req.params.board ? '/' : '/globalmanage/recent.html' + 'redirect': req.params.board ? '/' : '/globalmanage/settings.html' }); } diff --git a/db/bans.js b/db/bans.js index 087e6f26..62b4f937 100644 --- a/db/bans.js +++ b/db/bans.js @@ -12,13 +12,13 @@ module.exports = { let ipQuery; if (typeof ip === 'object') { //object with hash and ranges in bancheck ipQuery = { - '$in': Object.values(ip) //gets values of ip object for single and range bans in 1 query + '$in': [ip.single, ip.qrange, ip.hrange] //gets single and range ban in 1 query } } else { ipQuery = ip; } return db.find({ - 'ip': ipQuery, + 'ip.single': ipQuery, 'board': { '$in': [board, null] } @@ -42,7 +42,7 @@ module.exports = { '_id': { '$in': ids }, - 'ip': ip, + 'ip.single': ip, 'allowAppeal': true, 'appeal': null }, { diff --git a/db/boards.js b/db/boards.js index 053d1936..c61faff9 100644 --- a/db/boards.js +++ b/db/boards.js @@ -2,6 +2,7 @@ const Mongo = require(__dirname+'/db.js') , cache = require(__dirname+'/../redis.js') + , dynamicResponse = require(__dirname+'/../helpers/dynamic.js') , db = Mongo.client.db('jschan').collection('boards'); module.exports = { @@ -56,6 +57,7 @@ module.exports = { deleteOne: (board) => { cache.del(`board:${board}`); cache.del(`banners:${board}`); + cache.srem('triggered', board); return db.deleteOne({ '_id': board }); }, @@ -200,12 +202,40 @@ module.exports = { bodyExists: async (req, res, next) => { const board = await module.exports.findOne(req.body.board); if (!board) { - return res.status(404).render('404'); + return dynamicResponse(req, res, 404, '404', { + 'title': 'Bad request', + 'message': 'Board does not exist', + }); } res.locals.board = board; next(); }, + triggerModes: (boards) => { + return db.aggregate([ + { + '$match': { + '_id': { + '$in': boards + } + } + }, { + '$project': { + '_id': 1, + 'lockMode': { + 'new': '$settings.lockMode', + 'old': '$preTriggerMode.lockMode' + }, + 'captchaMode': { + 'new': '$settings.captchaMode', + 'old': '$preTriggerMode.captchaMode' + }, + 'threadLimit': '$settings.threadLimit' + } + } + ]).toArray(); + }, + getNextId: async (board, saged) => { const update = { '$inc': { diff --git a/db/posts.js b/db/posts.js index f4c20625..59d44cfb 100644 --- a/db/posts.js +++ b/db/posts.js @@ -26,8 +26,10 @@ module.exports = { getGlobalRecent: (offset=0, limit=20, ip) => { //global recent posts for recent section of global manage page const query = {}; - if (ip !== null) { + if (ip instanceof RegExp) { query['ip.single'] = ip; + } else if (typeof ip === 'string') { + query['ip.raw'] = ip; } return db.find(query).sort({ '_id': -1 @@ -38,8 +40,10 @@ module.exports = { const query = { board }; - if (ip !== null) { + if (ip instanceof RegExp) { query['ip.single'] = ip; + } else if (typeof ip === 'string') { + query['ip.raw'] = ip; } return db.find(query).sort({ '_id': -1 @@ -405,14 +409,19 @@ module.exports = { getGlobalReports: (offset=0, limit, ip) => { const query = { - 'globalreports.0': { - '$exists': true - } - } - if (ip !== null) { + 'globalreports.0': { + '$exists': true + } + } + if (ip instanceof RegExp) { query['$or'] = [ { 'ip.single': ip }, - { 'globalreports.ip': ip } + { 'globalreports.ip.single': ip } + ]; + } else if (typeof ip === 'string') { + query['$or'] = [ + { 'ip.raw': ip }, + { 'globalreports.ip.raw': ip } ]; } return db.find(query, { diff --git a/db/webring.js b/db/webring.js index b4b9c72c..27e35033 100644 --- a/db/webring.js +++ b/db/webring.js @@ -46,7 +46,7 @@ module.exports = { return db.countDocuments(addedFilter); }, - deleteAll: (board) => { + deleteAll: () => { return db.deleteMany({}); }, diff --git a/gulp/res/css/style.css b/gulp/res/css/style.css index 3c9c5954..e09787b5 100644 --- a/gulp/res/css/style.css +++ b/gulp/res/css/style.css @@ -905,10 +905,6 @@ input:invalid, textarea:invalid { text-align: center; } -.nav-item:nth-of-type(3) { - line-height:1.5em; -} - .left { float: left; } diff --git a/gulpfile.js b/gulpfile.js index 50772418..21c98a60 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -59,7 +59,8 @@ async function wipe() { await db.createCollection('webring'); await db.createCollection('bypass'); - const { Webring, Boards, Posts, Captchas, Ratelimits, Accounts, Files, Stats, Modlogs, Bans, Bypass } = require(__dirname+'/db/'); + const { Webring, Boards, Posts, Captchas, Ratelimits, News, + Accounts, Files, Stats, Modlogs, Bans, Bypass } = require(__dirname+'/db/'); //wipe db shit await Promise.all([ @@ -75,6 +76,7 @@ async function wipe() { Stats.deleteAll(), Modlogs.deleteAll(), Bypass.deleteAll(), + News.deleteAll(), ]); //add indexes - should profiled and changed at some point if necessary @@ -92,7 +94,7 @@ async function wipe() { await Modlogs.db.dropIndexes() await Modlogs.db.createIndex({ 'board': 1 }) await Files.db.createIndex({ 'count': 1 }) - await Bans.db.createIndex({ 'ip': 1 , 'board': 1 }) + await Bans.db.createIndex({ 'ip.single': 1 , 'board': 1 }) await Bans.db.createIndex({ 'expireAt': 1 }, { expireAfterSeconds: 0 }) //custom expiry, i.e. it will expire when current date > than this date await Bypass.db.createIndex({ 'expireAt': 1 }, { expireAfterSeconds: 0 }) await Captchas.db.createIndex({ 'expireAt': 1 }, { expireAfterSeconds: 300 }) //captchas valid for 5 minutes @@ -174,6 +176,8 @@ function custompages() { return gulp.src([`${paths.pug.src}/custompages/*.pug`, `${paths.pug.src}/pages/404.pug`, `${paths.pug.src}/pages/502.pug`]) .pipe(gulppug({ locals: { + meta: configs.meta, + enableWebring: configs.enableWebring, globalLimits: configs.globalLimits, codeLanguages: configs.highlightOptions.languageSubset, defaultTheme: configs.boardDefaults.theme, diff --git a/helpers/decodequeryip.js b/helpers/decodequeryip.js index d8793dc9..c13d4c02 100644 --- a/helpers/decodequeryip.js +++ b/helpers/decodequeryip.js @@ -1,15 +1,17 @@ 'use strict'; const escapeRegExp = require(__dirname+'/escaperegexp.js') + , { isIP } = require('net') , { ipHashPermLevel } = require(__dirname+'/../configs/main.js') module.exports = (query, permLevel) => { if (query.ip && typeof query.ip === 'string') { const decoded = decodeURIComponent(query.ip); - const hashed = permLevel > ipHashPermLevel; - if (!hashed || decoded.length === 10) { + if (permLevel <= ipHashPermLevel && isIP(decoded)) { //if perms to view raw ip, allow querying + return decoded; + } else if (decoded.length === 10) { //otherwise, only allow last 10 char substring return new RegExp(`${escapeRegExp(decoded)}$`); } } - return null; + return null; //else, no ip filter } diff --git a/helpers/processip.js b/helpers/processip.js index 693e7653..97194739 100644 --- a/helpers/processip.js +++ b/helpers/processip.js @@ -13,9 +13,10 @@ module.exports = (req, res, next) => { const qrange = split.slice(0,Math.floor(split.length*0.75)).join(delimiter); const hrange = split.slice(0,Math.floor(split.length*0.5)).join(delimiter); res.locals.ip = { - single: ipHashPermLevel === -1 ? hashIp(ip) : ip, - qrange: ipHashPermLevel === -1 ? hashIp(qrange) : qrange, - hrange: ipHashPermLevel === -1 ? hashIp(hrange) : hrange, + raw: ipHashPermLevel === -1 ? hashIp(ip) : ip, + single: hashIp(ip), + qrange: hashIp(qrange), + hrange: hashIp(hrange), } next(); } else { diff --git a/helpers/render.js b/helpers/render.js index d9e22899..b567f4f0 100644 --- a/helpers/render.js +++ b/helpers/render.js @@ -1,6 +1,8 @@ 'use strict'; -const { enableAccountCreation, enableUserBoards, lockWait, globalLimits, boardDefaults, cacheTemplates, meta } = require(__dirname+'/../configs/main.js') +const { enableUserBoardCreation, enableUserAccountCreation, + lockWait, globalLimits, boardDefaults, cacheTemplates, + meta, enableWebring } = require(__dirname+'/../configs/main.js') , { outputFile } = require('fs-extra') , formatSize = require(__dirname+'/files/formatsize.js') , pug = require('pug') @@ -19,9 +21,10 @@ module.exports = async (htmlName, templateName, options, json=null) => { defaultTheme: boardDefaults.theme, defaultCodeTheme: boardDefaults.codeTheme, postFilesSize: formatSize(globalLimits.postFilesSize.max), - enableAccountCreation, - enableUserBoards, + enableUserAccountCreation, + enableUserBoardCreation, globalLimits, + enableWebring, }); const lock = await redlock.lock(`locks:${htmlName}`, lockWait); const htmlPromise = outputFile(`${uploadDirectory}/html/${htmlName}`, html); diff --git a/helpers/tasks.js b/helpers/tasks.js index 86940f5d..318c6bb7 100644 --- a/helpers/tasks.js +++ b/helpers/tasks.js @@ -6,7 +6,9 @@ const Mongo = require(__dirname+'/../db/db.js') , { remove } = require('fs-extra') , { debugLogs, pruneModlogs, pruneAfterDays, enableWebring, maxRecentNews } = require(__dirname+'/../configs/main.js') , { Stats, Posts, Files, Boards, News, Modlogs } = require(__dirname+'/../db/') + , cache = require(__dirname+'/../redis.js') , render = require(__dirname+'/render.js') + , buildQueue = require(__dirname+'/../queue.js') , timeDiffString = require(__dirname+'/timediffstring.js'); module.exports = { @@ -86,6 +88,9 @@ module.exports = { //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 @@ -216,9 +221,68 @@ module.exports = { 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': { + 'settings.lockMode': p.lockMode.old, + 'settings.captchaMode': p.captchaMode.old + } + } + } + } + }) + await Boards.db.bulkWrite(bulkWrites); + const promises = []; + triggerModes.forEach(async (p) => { + await cache.del(`board:${p._id}`); + if (p.captchaMode.old < p.captchaMode.new) { + if (p.captchaMode.old === 2) { + promises.push(remove(`${uploadDirectory}/html/${p._id}/thread/`)); + } + if (p.captchaMode.old === 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)); - module.exports.buildHomepage(); + }, buildChangePassword: () => { diff --git a/migrations/index.js b/migrations/index.js index 62748d27..f05e35af 100644 --- a/migrations/index.js +++ b/migrations/index.js @@ -7,4 +7,7 @@ module.exports = { '0.0.4': require(__dirname+'/migration-0.0.4.js'), //rename some fields for board lock mode and unlisting '0.0.5': require(__dirname+'/migration-0.0.5.js'), //add bumplimit to board settings '0.0.6': require(__dirname+'/migration-0.0.6.js'), //add blocked countries to board settings + '0.0.7': require(__dirname+'/migration-0.0.7.js'), //sage only email without force anon for some reason + '0.0.8': require(__dirname+'/migration-0.0.8.js'), //option to auto reset triggers after hour is over + '0.0.9': require(__dirname+'/migration-0.0.9.js'), //ip changes } diff --git a/migrations/migration-0.0.7.js b/migrations/migration-0.0.7.js new file mode 100644 index 00000000..98a1d1db --- /dev/null +++ b/migrations/migration-0.0.7.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = async(db, redis) => { + console.log('add sageOnlyEmail option to boards'); + await db.collection('boards').updateMany({}, { + '$set': { + 'settings.sageOnlyEmail': false, + } + }); + console.log('Cleared boards cache'); + await redis.deletePattern('board:*'); +}; diff --git a/migrations/migration-0.0.8.js b/migrations/migration-0.0.8.js new file mode 100644 index 00000000..66d79f11 --- /dev/null +++ b/migrations/migration-0.0.8.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = async(db, redis) => { + console.log('add resetTrigger option to boards'); + await db.collection('boards').updateMany({}, { + '$set': { + 'settings.resetTrigger': false, + } + }); + console.log('Cleared boards cache'); + await redis.deletePattern('board:*'); +}; diff --git a/migrations/migration-0.0.9.js b/migrations/migration-0.0.9.js new file mode 100644 index 00000000..f24ea071 --- /dev/null +++ b/migrations/migration-0.0.9.js @@ -0,0 +1,56 @@ +'use strict'; + +const hashIp = require(__dirname+'/../helpers/haship.js'); + +module.exports = async(db, redis) => { + console.log('change bans index'); + await db.collection('bans').dropIndex("ip_1_board_1"); + await db.collection('bans').createIndex({ 'ip.single': 1 , 'board': 1 }); + console.log('adjusting ip on posts and clearing reports'); + const promises = [] + await db.collection('posts').find().forEach(doc => { + promises.push(db.collection('posts').updateOne({ + '_id':doc._id + }, { + '$set':{ + 'ip.raw': doc.ip.single, + 'ip.single': hashIp(doc.ip.single), + 'ip.qrange': hashIp(doc.ip.qrange), + 'ip.hrange': hashIp(doc.ip.hrange), + 'reports': [], //easier than fixing reports + 'globalreports': [], //easier than fixing reports + } + })) + }); + console.log('adjusting ip in modlogs') + await db.collection('modlog').find().forEach(doc => { + promises.push(db.collection('modlog').updateOne({ + '_id':doc._id + }, { + '$set':{ + 'ip': { + 'raw': doc.ip, + 'single': hashIp(doc.ip) + } + } + })) + }); + console.log('adjust ip in bans, set null type and remove saved posts') + await db.collection('bans').find().forEach(doc => { + promises.push(db.collection('bans').updateOne({ + '_id':doc._id + }, { + '$set':{ + 'ip': { + 'raw': doc.ip, + 'single': hashIp(doc.ip) + }, + 'type': null, + 'posts': null //easier than fixing all saved posts + } + })) + }); + await Promise.all(promises); + console.log('Cleared boards cache'); + await redis.deletePattern('board:*'); +}; diff --git a/models/forms/actionhandler.js b/models/forms/actionhandler.js index b27d1791..d74eb44e 100644 --- a/models/forms/actionhandler.js +++ b/models/forms/actionhandler.js @@ -291,7 +291,10 @@ module.exports = async (req, res, next) => { showUser: !req.body.hide_name || logUser === 'Unregistered User' ? true : false, message: message, user: logUser, - ip: res.locals.ip.single, + ip: { + single: res.locals.ip.single, + raw: res.locals.ip.raw + } }; } //push each post id diff --git a/models/forms/banposter.js b/models/forms/banposter.js index 5b0c2817..1d19a2d3 100644 --- a/models/forms/banposter.js +++ b/models/forms/banposter.js @@ -23,13 +23,20 @@ module.exports = async (req, res, next) => { }, {}); for (let ip in ipPosts) { const thisIpPosts = ipPosts[ip]; - let banIp = ip; + let type = 'single'; + let banIp = { + single: ip, + raw: thisIpPosts[0].ip.raw + }; if (req.body.ban_h) { - banIp = thisIpPosts[0].ip.hrange; + type = 'half'; + banIp.single = thisIpPosts[0].ip.hrange; } else if (req.body.ban_q) { - banIp = thisIpPosts[0].ip.qrange; + type = 'quarter'; + banIp.single = thisIpPosts[0].ip.qrange; } bans.push({ + type, 'ip': banIp, 'reason': banReason, 'board': banBoard, @@ -58,7 +65,7 @@ module.exports = async (req, res, next) => { } if (req.body.global_report_ban) { const matches = post.globalreports.map(r => { - if (req.body.checkedreports.includes(r.id)) { + if (req.body.checkedreports.includes(r.id.toString())) { return r.ip; } }); @@ -67,6 +74,7 @@ module.exports = async (req, res, next) => { [...new Set(ips)].forEach(ip => { bans.push({ 'ip': ip, + 'type': 'single', 'reason': banReason, 'board': banBoard, 'posts': null, diff --git a/models/forms/changeboardsettings.js b/models/forms/changeboardsettings.js index 46bd93cf..e4480438 100644 --- a/models/forms/changeboardsettings.js +++ b/models/forms/changeboardsettings.js @@ -82,6 +82,7 @@ module.exports = async (req, res, next) => { 'ids': booleanSetting(req.body.ids), 'flags': booleanSetting(req.body.flags), 'forceAnon': booleanSetting(req.body.force_anon), + 'sageOnlyEmail': booleanSetting(req.body.sage_only_email), 'userPostDelete': booleanSetting(req.body.user_post_delete), 'userPostSpoiler': booleanSetting(req.body.user_post_spoiler), 'userPostUnlink': booleanSetting(req.body.user_post_unlink), @@ -91,6 +92,7 @@ module.exports = async (req, res, next) => { 'forceReplyFile': booleanSetting(req.body.force_reply_file), 'forceThreadSubject': booleanSetting(req.body.force_thread_subject), 'disableReplySubject': booleanSetting(req.body.disable_reply_subject), + 'resetTrigger': booleanSetting(req.body.reset_trigger), 'captchaMode': numberSetting(req.body.captcha_mode, oldSettings.captchaMode), 'tphTrigger': numberSetting(req.body.tph_trigger, oldSettings.tphTrigger), 'pphTrigger': numberSetting(req.body.pph_trigger, oldSettings.pphTrigger), @@ -127,7 +129,11 @@ module.exports = async (req, res, next) => { //settings changed in the db await Boards.updateOne(req.params.board, { '$set': { - 'settings': newSettings + 'settings': newSettings, + 'preTriggerMode': { + 'lockMode': newSettings.lockMode, + 'captchaMode': newSettings.captchaMode + } } }); diff --git a/models/forms/editpost.js b/models/forms/editpost.js index 0e534272..2e46ce3c 100644 --- a/models/forms/editpost.js +++ b/models/forms/editpost.js @@ -48,7 +48,11 @@ todo: handle some more situations const banDate = new Date(); const banExpiry = new Date(globalSettings.filterBanDuration + banDate.getTime()); const ban = { - 'ip': res.locals.ip.single, + 'ip': { + 'single': res.locals.ip.single, + 'raw': res.locals.ip.raw, + }, + 'type': 'single', 'reason': 'global word filter auto ban', 'board': null, 'posts': null, @@ -58,10 +62,10 @@ todo: handle some more situations 'allowAppeal': true, //should i make this configurable if appealable? 'seen': false }; - await Bans.insertOne(ban); - const bans = await Bans.find(res.locals.ip.single, banBoard); //need to query db so it has _id field for appeal checkmark + const insertedResult = await Bans.insertOne(ban); + ban._id = insertedResult.insertedId; return res.status(403).render('ban', { - bans: bans + bans: [ban] }); } } @@ -141,7 +145,10 @@ todo: handle some more situations showUser: req.body.hide_name ? false : true, message: req.body.log_message || null, user: req.session.user.username, - ip: res.locals.ip.single, + ip: { + single: res.locals.ip.single, + raw: ras.locals.ip.raw, + } }); const buildOptions = { diff --git a/models/forms/makepost.js b/models/forms/makepost.js index ec60072c..413f5a58 100644 --- a/models/forms/makepost.js +++ b/models/forms/makepost.js @@ -44,8 +44,8 @@ module.exports = async (req, res, next) => { let redirect = `/${req.params.board}/` let salt = null; let thread = null; - const { filterBanDuration, filterMode, filters, blockedCountries, - maxFiles, forceAnon, replyLimit, disableReplySubject, + const { filterBanDuration, filterMode, filters, blockedCountries, resetTrigger, + maxFiles, sageOnlyEmail, forceAnon, replyLimit, disableReplySubject, threadLimit, ids, userPostSpoiler, pphTrigger, tphTrigger, triggerAction, captchaMode, lockMode, allowedFileTypes, flags } = res.locals.board.settings; if (flags === true @@ -133,7 +133,11 @@ module.exports = async (req, res, next) => { const banDate = new Date(); const banExpiry = new Date(useFilterBanDuration + banDate.getTime()); const ban = { - 'ip': res.locals.ip.single, + 'ip': { + 'single': res.locals.ip.single, + 'raw': res.locals.ip.raw, + }, + 'type': 'single', 'reason': `${hitGlobalFilter ? 'global ' :''}word filter auto ban`, 'board': banBoard, 'posts': null, @@ -143,10 +147,10 @@ module.exports = async (req, res, next) => { 'allowAppeal': true, //should i make this configurable if appealable? 'seen': false }; - await Bans.insertOne(ban); - const bans = await Bans.find(res.locals.ip.single, banBoard); //need to query db so it has _id field for appeal checkmark + const insertedResult = await Bans.insertOne(ban); + ban._id = insertedResult.insertedId; return res.status(403).render('ban', { - bans: bans + bans: [ban] }); } } @@ -322,9 +326,9 @@ module.exports = async (req, res, next) => { const spoiler = userPostSpoiler && req.body.spoiler ? true : false; //forceanon hide reply subjects so cant be used as name for replies - //forceanon only allow sage email + //forceanon and sageonlyemail only allow sage email let subject = (res.locals.permLevel >= 4 && req.body.thread && (disableReplySubject || forceAnon)) ? null : req.body.subject; - let email = (res.locals.permLevel < 4 || !forceAnon || req.body.email === 'sage') ? req.body.email : null; + let email = (res.locals.permLevel < 4 || (!forceAnon && !sageOnlyEmail) || req.body.email === 'sage') ? req.body.email : null; //get name, trip and cap const { name, tripcode, capcode } = await nameHandler(req.body.name, res.locals.permLevel, res.locals.board.settings); @@ -386,7 +390,12 @@ module.exports = async (req, res, next) => { || (pphTrigger > 0 && hourPosts.pph > pphTrigger)) { //update in memory for other stuff done e.g. rebuilds const update = { - '$set': {} + '$set': { + 'preTriggerMode': { + lockMode, + captchaMode + } + } }; if (triggerAction < 3) { res.locals.board.settings.captchaMode = triggerAction; @@ -401,6 +410,10 @@ module.exports = async (req, res, next) => { } //set it in the db await Boards.updateOne(res.locals.board._id, update); + if (resetTrigger) { + //mark the board as being triggered so we can return it to old mode after on schedule + await cache.sadd('triggered', res.locals.board._id); + } } } diff --git a/models/forms/reportpost.js b/models/forms/reportpost.js index f9d1140a..696eb2d6 100644 --- a/models/forms/reportpost.js +++ b/models/forms/reportpost.js @@ -8,7 +8,10 @@ module.exports = (req, res) => { 'id': ObjectId(), 'reason': req.body.report_reason, 'date': new Date(), - 'ip': res.locals.ip.single + 'ip': { + 'single': res.locals.ip.single, + 'raw': res.locals.ip.raw + } } const ret = { diff --git a/models/pages/globalmanage/logs.js b/models/pages/globalmanage/logs.js index 12aa8bbc..7852a0fe 100644 --- a/models/pages/globalmanage/logs.js +++ b/models/pages/globalmanage/logs.js @@ -19,8 +19,10 @@ module.exports = async (req, res, next) => { filter.board = uri; } const ipMatch = decodeQueryIP(req.query, res.locals.permLevel); - if (ipMatch) { - filter.ip = ipMatch; + if (ipMatch instanceof RegExp) { + filter['ip.single'] = ipMatch; + } else if (typeof ipMatch === 'string') { + filter['ip.raw'] = ipMatch; } let logs, maxPage; diff --git a/models/pages/globalmanage/recent.js b/models/pages/globalmanage/recent.js index 413b05dd..fe1ff828 100644 --- a/models/pages/globalmanage/recent.js +++ b/models/pages/globalmanage/recent.js @@ -1,10 +1,8 @@ 'use strict'; const { Posts } = require(__dirname+'/../../../db/') - , { ipHashPermLevel } = require(__dirname+'/../../../configs/main.js') , pageQueryConverter = require(__dirname+'/../../../helpers/pagequeryconverter.js') , decodeQueryIP = require(__dirname+'/../../../helpers/decodequeryip.js') - , hashIp = require(__dirname+'/../../../helpers/haship.js') , limit = 20; module.exports = async (req, res, next) => { @@ -18,12 +16,6 @@ module.exports = async (req, res, next) => { } catch (err) { return next(err) } - if (ipHashPermLevel !== -1 - && res.locals.permLevel > ipHashPermLevel) { - for (let i = 0; i < posts.length; i++) { - posts[i].ip.single = hashIp(posts[i].ip.single); - } - } res .set('Cache-Control', 'private, max-age=5') diff --git a/models/pages/manage/bans.js b/models/pages/manage/bans.js index db0cd513..59c15cdf 100644 --- a/models/pages/manage/bans.js +++ b/models/pages/manage/bans.js @@ -1,8 +1,6 @@ 'use strict'; -const Bans = require(__dirname+'/../../../db/bans.js') - , { ipHashPermLevel } = require(__dirname+'/../../../configs/main.js') - , hashIp = require(__dirname+'/../../../helpers/haship.js'); +const Bans = require(__dirname+'/../../../db/bans.js'); module.exports = async (req, res, next) => { @@ -12,12 +10,6 @@ module.exports = async (req, res, next) => { } catch (err) { return next(err) } - if (ipHashPermLevel !== -1 - && res.locals.permLevel > ipHashPermLevel) { - for (let i = 0; i < bans.length; i++) { - bans[i].ip = hashIp(bans[i].ip); - } - } res .set('Cache-Control', 'private, max-age=5') diff --git a/models/pages/manage/logs.js b/models/pages/manage/logs.js index b92b0931..0bfd8610 100644 --- a/models/pages/manage/logs.js +++ b/models/pages/manage/logs.js @@ -1,10 +1,8 @@ 'use strict'; const { Modlogs } = require(__dirname+'/../../../db/') - , { ipHashPermLevel } = require(__dirname+'/../../../configs/main.js') , pageQueryConverter = require(__dirname+'/../../../helpers/pagequeryconverter.js') - , decodeQueryIP = require(__dirname+'/../../../helpers/decodequeryip.js') - , hashIp = require(__dirname+'/../../../helpers/haship.js') +// , decodeQueryIP = require(__dirname+'/../../../helpers/decodequeryip.js') , limit = 50; module.exports = async (req, res, next) => { @@ -34,12 +32,6 @@ module.exports = async (req, res, next) => { } catch (err) { return next(err) } - if (ipHashPermLevel !== -1 - && res.locals.permLevel > ipHashPermLevel) { - for (let i = 0; i < logs.length; i++) { - logs[i].ip = hashIp(logs[i].ip); - } - } res .set('Cache-Control', 'private, max-age=5') diff --git a/models/pages/manage/recent.js b/models/pages/manage/recent.js index f0896809..e09e90a9 100644 --- a/models/pages/manage/recent.js +++ b/models/pages/manage/recent.js @@ -1,8 +1,6 @@ 'use strict'; const { Posts } = require(__dirname+'/../../../db/') - , { ipHashPermLevel } = require(__dirname+'/../../../configs/main.js') - , hashIp = require(__dirname+'/../../../helpers/haship.js') , decodeQueryIP = require(__dirname+'/../../../helpers/decodequeryip.js') , pageQueryConverter = require(__dirname+'/../../../helpers/pagequeryconverter.js') , limit = 20; @@ -11,12 +9,11 @@ module.exports = async (req, res, next) => { const { page, offset, queryString } = pageQueryConverter(req.query, limit); let ip = decodeQueryIP(req.query, res.locals.permLevel); - const postId = typeof req.query.postid === 'string' ? req.query.postid : null; if (postId && +postId === parseInt(postId) && Number.isSafeInteger(+postId)) { const fetchedPost = await Posts.getPost(req.params.board, +postId, true); if (fetchedPost) { - ip = fetchedPost.ip.single; + ip = decodeQueryIP({ ip: fetchedPost.ip.single.slice(-10) }, res.locals.permlevel); } } @@ -26,12 +23,6 @@ module.exports = async (req, res, next) => { } catch (err) { return next(err) } - if (ipHashPermLevel !== -1 - && res.locals.permLevel > ipHashPermLevel) { - for (let i = 0; i < posts.length; i++) { - posts[i].ip.single = hashIp(posts[i].ip.single); - } - } res .set('Cache-Control', 'private, max-age=5') diff --git a/models/pages/manage/reports.js b/models/pages/manage/reports.js index 6e24b4dc..61889347 100644 --- a/models/pages/manage/reports.js +++ b/models/pages/manage/reports.js @@ -1,8 +1,6 @@ 'use strict'; -const Posts = require(__dirname+'/../../../db/posts.js') - , { ipHashPermLevel } = require(__dirname+'/../../../configs/main.js') - , hashIp = require(__dirname+'/../../../helpers/haship.js'); +const Posts = require(__dirname+'/../../../db/posts.js'); module.exports = async (req, res, next) => { @@ -13,13 +11,6 @@ module.exports = async (req, res, next) => { return next(err) } - if (ipHashPermLevel !== -1 - && res.locals.permLevel > ipHashPermLevel) { - for (let i = 0; i < reports.length; i++) { - reports[i].ip.single = hashIp(reports[i].ip.single); - } - } - res .set('Cache-Control', 'private, max-age=5') .render('managereports', { diff --git a/package-lock.json b/package-lock.json index b18b50a7..fe13a7d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,21 +5,21 @@ "requires": true, "dependencies": { "@babel/helper-validator-identifier": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", - "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==" + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz", + "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==" }, "@babel/parser": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.6.tgz", - "integrity": "sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==" + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.1.tgz", + "integrity": "sha512-AUTksaz3FqugBkbTZ1i+lDLG5qy8hIzCaAxEtttU6C0BtZZU9pkNZtWSVAht4EW9kl46YBiyTGMp9xTTGqViNg==" }, "@babel/types": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.6.tgz", - "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.1.tgz", + "integrity": "sha512-L2yqUOpf3tzlW9GVuipgLEcZxnO+96SzR6fjXMuxxNkIgFJ5+07mHCZ+HkHqaeZu8+3LKnNJJ1bKbjBETQAsrA==", "requires": { - "@babel/helper-validator-identifier": "^7.9.5", + "@babel/helper-validator-identifier": "^7.10.1", "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" } @@ -8487,9 +8487,9 @@ "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" }, "with": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/with/-/with-7.0.0.tgz", - "integrity": "sha512-XS51xsYITl5V0q0AcZG/lMPFBbCETHLjL7Tr6ZOmPGP3UkKBmwOKqLgAmenC5v8MHDcOudpNkO1CW0kpe7Oyag==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", "requires": { "@babel/parser": "^7.9.6", "@babel/types": "^7.9.6", diff --git a/package.json b/package.json index be9b5968..6b97dfa0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jschan", "version": "0.0.1", - "migrateVersion": "0.0.6", + "migrateVersion": "0.0.9", "description": "", "main": "server.js", "dependencies": { diff --git a/queue.js b/queue.js index fa19544a..38300445 100644 --- a/queue.js +++ b/queue.js @@ -1,8 +1,8 @@ 'use strict'; const Queue = require('bull') - , configs = require(__dirname+'/configs/main.js') - , taskQueue = new Queue('task', { 'redis': configs.redis }); + , { redis } = require(__dirname+'/configs/main.js') + , taskQueue = new Queue('task', { redis }); module.exports = { diff --git a/redis.js b/redis.js index 350d3666..f203848f 100644 --- a/redis.js +++ b/redis.js @@ -27,6 +27,16 @@ module.exports = { return client.sadd(key, value); }, + //get all members of a set + sgetall: (key) => { + return client.smembers(key); + }, + + //remove an item from a set + srem: (key, value) => { + return client.srem(key, value); + }, + //get random item from set srand: (key) => { return client.srandmember(key); diff --git a/schedules/webring.js b/schedules/webring.js index 175ffb09..2f08a62c 100644 --- a/schedules/webring.js +++ b/schedules/webring.js @@ -16,14 +16,14 @@ module.exports = async () => { const label = `updating webring`; const start = process.hrtime(); - const visited = new Set(); + const visited = new Map(); let known = new Set(following); let webringBoards = []; //list of webring boards while (known.size > visited.size) { //get sites we havent visited yet const toVisit = [...known].filter(url => !visited.has(url)); let rings = await Promise.all(toVisit.map(url => { - visited.add(url); + visited.set(url, (visited.get(url)||0)+1); return fetch(url, { agent, headers: { @@ -33,9 +33,12 @@ module.exports = async () => { })); for (let i = 0; i < rings.length; i++) { const ring = rings[i]; - if (!ring || !ring.name || !ring.endpoint || !ring.url || ring.endpoint.includes(meta.url)) { + if (!ring || !ring.name || !ring.endpoint || !ring.url //malformed + || ring.endpoint.includes(meta.url) //own site + || visited.get(ring.endpoint) > 1) { //already seen endpoint (for multiple domain sites) continue; } + visited.set(ring.endpoint, visited.get(ring.endpoint)+1); if (ring.following && ring.following.length > 0) { //filter their folowing by blacklist/self and add to known sites ring.following @@ -46,7 +49,7 @@ module.exports = async () => { //add some stuff for the boardlist and then add their boards ring.boards.forEach(board => { board.siteName = ring.name; - //convert to numbers because old infinity webring plugin returns strings + //convert to numbers because old infinity webring plugin returns string board.totalPosts = parseInt(board.totalPosts); board.postsPerHour = parseInt(board.postsPerHour); board.uniqueUsers = parseInt(board.uniqueUsers); @@ -56,13 +59,18 @@ module.exports = async () => { } } - //$out from temp collection to replace webring boards - const tempCollection = Mongo.client.db('jschan').collection('tempwebring'); - await tempCollection.insertMany(webringBoards); - await tempCollection.aggregate([ - { $out : 'webring' } - ]); - await tempCollection.drop(); + if (webringBoards.length > 0) { + //$out from temp collection to replace webring boards + const tempCollection = Mongo.client.db('jschan').collection('tempwebring'); + await tempCollection.insertMany(webringBoards); + await tempCollection.aggregate([ + { $out : 'webring' } + ]).toArray(); + await tempCollection.drop(); + } else { + //otherwise none found, so delete them all + await Webring.deleteAll(); + } //update webring.json const boards = await Boards.webringBoards(); diff --git a/server.js b/server.js index e0a57fb7..b4529652 100644 --- a/server.js +++ b/server.js @@ -12,7 +12,8 @@ const express = require('express') , server = require('http').createServer(app) , cookieParser = require('cookie-parser') , { cacheTemplates, boardDefaults, sessionSecret, globalLimits, - secureCookies, debugLogs, ipHashPermLevel, meta, port } = require(__dirname+'/configs/main.js') + enableUserBoardCreation, enableUserAccountCreation, secureCookies, + debugLogs, ipHashPermLevel, meta, port, enableWebring } = require(__dirname+'/configs/main.js') , referrerCheck = require(__dirname+'/helpers/referrercheck.js') , { themes, codeThemes } = require(__dirname+'/helpers/themes.js') , Mongo = require(__dirname+'/db/db.js') @@ -80,10 +81,13 @@ const express = require('express') } //default settings + app.locals.enableUserAccountCreation = enableUserAccountCreation; + app.locals.enableUserBoardCreation = enableUserBoardCreation; app.locals.defaultTheme = boardDefaults.theme; app.locals.defaultCodeTheme = boardDefaults.codeTheme; app.locals.globalLimits = globalLimits; app.locals.ipHashPermLevel = ipHashPermLevel; + app.locals.enableWebring = enableWebring; app.locals.commit = commit; app.locals.meta = meta; diff --git a/views/includes/bantable.pug b/views/includes/bantable.pug index f0cfd15e..2aebf3c5 100644 --- a/views/includes/bantable.pug +++ b/views/includes/bantable.pug @@ -5,6 +5,7 @@ th Board th Reason th IP + th Type th Issuer th Issue Date th Expiry diff --git a/views/includes/navbar.pug b/views/includes/navbar.pug index f38c9dfa..9c124e6e 100644 --- a/views/includes/navbar.pug +++ b/views/includes/navbar.pug @@ -2,9 +2,10 @@ unless minimal nav.navbar a.nav-item(href='/index.html') Home a.nav-item(href='/news.html') News - a.nav-item(href='/boards.html') + a.nav-item(href='/boards.html' style=(enableWebring ? 'line-height: 1.5em' : null)) | Boards - .rainbow +Webring + if enableWebring + .rainbow +Webring a.nav-item(href='/account.html') Account if board a.nav-item(href=`/${board._id}/manage/reports.html`) Manage diff --git a/views/includes/postform.pug b/views/includes/postform.pug index 2229a695..d50125e4 100644 --- a/views/includes/postform.pug +++ b/views/includes/postform.pug @@ -21,9 +21,15 @@ section.form-wrapper.flex-center .label Name input.mr-1(type='text', name='name', placeholder=board.settings.defaultName maxlength=globalLimits.fieldLength.name) a.close.postform-style(href='#!') X - section.row - .label Email - input(type='text', name='email', autocomplete='off' maxlength=globalLimits.fieldLength.email) + if board.settings.sageOnlyEmail + section.row + .label Sage + label.postform-style.ph-5 + input(type='checkbox', name='email', value='sage') + else + section.row + .label Email + input(type='text', name='email', autocomplete='off' maxlength=globalLimits.fieldLength.email) include ./subjectfield.pug section.row .label diff --git a/views/mixins/ban.pug b/views/mixins/ban.pug index 25ba077a..235691d5 100644 --- a/views/mixins/ban.pug +++ b/views/mixins/ban.pug @@ -10,9 +10,10 @@ mixin ban(ban, banpage) else | Global td= ban.reason - - const ip = permLevel > ipHashPermLevel ? ban.ip.slice(-10) : ban.ip; + - const ip = permLevel > ipHashPermLevel ? ban.ip.single.slice(-10) : ban.ip.raw; td #{ip} - td= ban.issuer + td #{ban.type} + td #{ban.issuer} - const banDate = new Date(ban.date); td: time.right.reltime(datetime=banDate.toISOString()) #{banDate.toLocaleString(undefined, {hour12:false})} - const expireDate = new Date(ban.expireAt); diff --git a/views/mixins/catalogtile.pug b/views/mixins/catalogtile.pug index 915fb7ad..7c038453 100644 --- a/views/mixins/catalogtile.pug +++ b/views/mixins/catalogtile.pug @@ -5,7 +5,7 @@ mixin catalogtile(board, post, index) data-date=post.date data-replies=post.replyposts data-bump=post.bumped) - - const postURL = `/${board._id}/thread/${post.postId}.html#${post.postId}` + - const postURL = `/${board._id}/${modview ? 'manage/' : ''}thread/${post.postId}.html#${post.postId}` .post-info input.left.post-check(type='checkbox', name='checkedposts' value=post.postId) if modview diff --git a/views/mixins/post.pug b/views/mixins/post.pug index cd64b047..a8b7e732 100644 --- a/views/mixins/post.pug +++ b/views/mixins/post.pug @@ -2,7 +2,7 @@ include ./report.pug mixin post(post, truncate, manage=false, globalmanage=false, ban=false) .anchor(id=post.postId) div(class=`post-container ${post.thread || ban === true ? '' : 'op'}` data-board=post.board data-post-id=post.postId data-user-id=post.userId) - - const postURL = `/${post.board}/${modview ? 'manage/' : ''}thread/${post.thread || post.postId}.html`; + - const postURL = `/${post.board}/${(modview || manage || globalmanage) ? 'manage/' : ''}thread/${post.thread || post.postId}.html`; .post-info span label @@ -12,12 +12,12 @@ mixin post(post, truncate, manage=false, globalmanage=false, ban=false) input.post-check(type='checkbox', name='checkedposts' value=post.postId) | if manage - - const ip = permLevel > ipHashPermLevel ? post.ip.single.slice(-10) : post.ip.single; + - const ip = permLevel > ipHashPermLevel ? post.ip.single.slice(-10) : post.ip.raw; a.bold(href=`${upLevel ? '../' : ''}recent.html?ip=${encodeURIComponent(ip)}`) [#{ip}] else if modview a.bold(href=`${upLevel ? '../' : ''}recent.html?postid=${post.postId}`) [+] else if globalmanage - - const ip = permLevel > ipHashPermLevel ? post.ip.single.slice(-10) : post.ip.single; + - const ip = permLevel > ipHashPermLevel ? post.ip.single.slice(-10) : post.ip.raw; a.bold(href=`?ip=${encodeURIComponent(ip)}`) [#{ip}] | if !post.thread diff --git a/views/mixins/report.pug b/views/mixins/report.pug index 778b1753..14c6c025 100644 --- a/views/mixins/report.pug +++ b/views/mixins/report.pug @@ -2,7 +2,7 @@ mixin report(r, manage=false) .reports.post-container input.post-check(type='checkbox', name='checkedreports' value=r.id) | - - const ip = permLevel > ipHashPermLevel ? r.ip.slice(-10) : r.ip; + - const ip = permLevel > ipHashPermLevel ? r.ip.single.slice(-10) : r.ip.raw; a.bold(href=`${manage ? 'recent.html' : ''}?ip=${encodeURIComponent(ip)}`) [#{ip}] | - const reportDate = new Date(r.date); diff --git a/views/pages/account.pug b/views/pages/account.pug index d13eb5e8..f8be50ac 100644 --- a/views/pages/account.pug +++ b/views/pages/account.pug @@ -13,9 +13,9 @@ block content ul if user.authLevel <= 1 li: a(href='/globalmanage/recent.html') Global management - if enableUserBoards || user.authLevel <= 1 + if enableUserBoardCreation || user.authLevel <= 1 li: a(href='/create.html') Create a board - if !userAccountCreation && user.authLevel <= 1 + if !enableUserAccountCreation && user.authLevel <= 1 li: a(href='/register.html') Register an account li: a(href='/changepassword.html') Change password form(action='/forms/logout' method='post') diff --git a/views/pages/captcha.pug b/views/pages/captcha.pug index 68f85ad9..64de269c 100644 --- a/views/pages/captcha.pug +++ b/views/pages/captcha.pug @@ -1,6 +1,7 @@ doctype html html head + meta(charset='utf-8') link(rel='stylesheet', href='/css/nscaptcha.css') body img(src='/captcha') diff --git a/views/pages/changepassword.pug b/views/pages/changepassword.pug index 5393ec8d..99e5331c 100644 --- a/views/pages/changepassword.pug +++ b/views/pages/changepassword.pug @@ -25,5 +25,5 @@ block content include ../includes/captcha.pug input(type='submit', value='Change Password') p: a(href='/login.html') Login - if enableAccountCreation + if enableUserAccountCreation p: a(href='/register.html') Register diff --git a/views/pages/globalmanagelogs.pug b/views/pages/globalmanagelogs.pug index e19869b5..01da1432 100644 --- a/views/pages/globalmanagelogs.pug +++ b/views/pages/globalmanagelogs.pug @@ -52,7 +52,7 @@ block content | a(href=`?username=${log.user}`) [+] td - - const logIp = permLevel > ipHashPermLevel ? log.ip.slice(-10) : log.ip; + - const logIp = permLevel > ipHashPermLevel ? log.ip.single.slice(-10) : log.ip.raw; a(href=`recent.html?ip=${encodeURIComponent(logIp)}`) #{logIp} | a(href=`?ip=${encodeURIComponent(logIp)}`) [+] diff --git a/views/pages/globalmanagereports.pug b/views/pages/globalmanagereports.pug index 3ea10bb5..15a7c371 100644 --- a/views/pages/globalmanagereports.pug +++ b/views/pages/globalmanagereports.pug @@ -17,7 +17,7 @@ block content input(type='hidden' name='_csrf' value=csrf) hr(size=1) if ip - h4.no-m-p Reports against or by ...#{ip} + h4.no-m-p Reports against or by #{ip} hr(size=1) for report in reports .thread diff --git a/views/pages/globalmanagesettings.pug b/views/pages/globalmanagesettings.pug index ef072fde..1e3b2850 100644 --- a/views/pages/globalmanagesettings.pug +++ b/views/pages/globalmanagesettings.pug @@ -9,8 +9,21 @@ block content br +globalmanagenav('settings') hr(size=1) + h4.no-m-p Delete board: + .form-wrapper.flexleft.mt-10 + form.form-post(action=`/forms/global/deleteboard`, enctype='application/x-www-form-urlencoded', method='POST') + input(type='hidden' name='_csrf' value=csrf) + .row + .label Board URI + input(type='text' name='uri' required) + .row + .label I'm sure + label.postform-style.ph-5 + input(type='checkbox', name='confirm', value='true' required) + input(type='submit', value='submit') + hr(size=1) h4.no-m-p Settings: - .form-wrapper.flexleft + .form-wrapper.flexleft.mt-10 form.form-post(action=`/forms/global/settings`, enctype='application/x-www-form-urlencoded', method='POST') input(type='hidden' name='_csrf' value=csrf) .row diff --git a/views/pages/login.pug b/views/pages/login.pug index 6fca992e..1766d9c2 100644 --- a/views/pages/login.pug +++ b/views/pages/login.pug @@ -15,7 +15,7 @@ block content .label Password input(type='password', name='password', maxlength='100' required) input(type='submit', value='submit') - if enableAccountCreation + if enableUserAccountCreation p: a(href='/register.html') Register p: a(href='/changepassword.html') Change Password diff --git a/views/pages/managelogs.pug b/views/pages/managelogs.pug index 401b4942..a85c0651 100644 --- a/views/pages/managelogs.pug +++ b/views/pages/managelogs.pug @@ -40,7 +40,7 @@ block content | a(href=`?username=${log.user}`) [+] td - - const logIp = permLevel > ipHashPermLevel ? log.ip.slice(-10) : log.ip; + - const logIp = permLevel > ipHashPermLevel ? log.ip.single.slice(-10) : log.ip.raw; | #{logIp} td #{log.actions} td #{log.postIds} diff --git a/views/pages/managerecent.pug b/views/pages/managerecent.pug index 79bacf51..fd19ebb6 100644 --- a/views/pages/managerecent.pug +++ b/views/pages/managerecent.pug @@ -17,9 +17,8 @@ block content if posts.length === 0 p No posts. else - if postId || queryIp - - const which = (queryIp || posts[0].ip.single) - - const ip = permLevel > ipHashPermLevel ? which.slice(-10) : which; + - const ip = permLevel > ipHashPermLevel ? posts[0].ip.single.slice(-10) : posts[0].ip.raw; + if postId || (queryIp && queryIp === ip) h4.no-m-p Post history for #{ip} hr(size=1) for p in posts diff --git a/views/pages/managesettings.pug b/views/pages/managesettings.pug index 9f1ea755..2d76895e 100644 --- a/views/pages/managesettings.pug +++ b/views/pages/managesettings.pug @@ -25,13 +25,13 @@ block content .form-wrapper.flexleft.mt-10 form.form-post(action=`/forms/board/${board._id}/deleteboard`, enctype='application/x-www-form-urlencoded', method='POST') input(type='hidden' name='_csrf' value=csrf) + .row + .label Board URI + input(type='text' name='uri' required) .row .label I'm sure label.postform-style.ph-5 input(type='checkbox', name='confirm', value='true' required) - .row - .label Board URI - input(type='text' name='uri' required) input(type='submit', value='submit') hr(size=1) h4.no-m-p Settings: @@ -97,6 +97,10 @@ block content .label Force Anon label.postform-style.ph-5 input(type='checkbox', name='force_anon', value='true' checked=board.settings.forceAnon) + .row + .label Sage Only Email + label.postform-style.ph-5 + input(type='checkbox', name='sage_only_email', value='true' checked=board.settings.sageOnlyEmail) .col.mr-5 .row .label Force Thread Subject @@ -204,6 +208,10 @@ block content option(value='2', selected=board.settings.triggerAction === 2) Enable captcha for all posts option(value='3', selected=board.settings.triggerAction === 3) Lock thread creation option(value='4', selected=board.settings.triggerAction === 4) Lock board + .row + .label Auto Reset Trigger + label.postform-style.ph-5 + input(type='checkbox', name='reset_trigger', value='true' checked=board.settings.resetTrigger) .row .label Early 404 label.postform-style.ph-5 diff --git a/worker.js b/worker.js index 9f5cdcf4..5ee7fa2e 100644 --- a/worker.js +++ b/worker.js @@ -4,8 +4,7 @@ process .on('uncaughtException', console.error) .on('unhandledRejection', console.error); -const Queue = require('bull') - , { redis, debugLogs } = require(__dirname+'/configs/main.js') +const { debugLogs } = require(__dirname+'/configs/main.js') , Mongo = require(__dirname+'/db/db.js'); (async () => { @@ -14,13 +13,13 @@ const Queue = require('bull') await Mongo.connect(); const tasks = require(__dirname+'/helpers/tasks.js') - , taskQueue = new Queue('task', { redis }); + , { queue } = require(__dirname+'/queue.js') - taskQueue + queue .on('error', console.error) .on('failed', console.warn); - taskQueue.process(async job => { + queue.process(async job => { await tasks[job.data.task](job.data.options); return null; });