Merge branch 'dev'

merge-requests/208/head
fatchan 4 years ago
commit 137e3a8809
  1. 2
      configs/main.js.example
  2. 2
      configs/webring.json.example
  3. 3
      controllers/forms.js
  4. 5
      controllers/forms/deleteboard.js
  5. 6
      db/bans.js
  6. 32
      db/boards.js
  7. 25
      db/posts.js
  8. 2
      db/webring.js
  9. 4
      gulp/res/css/style.css
  10. 8
      gulpfile.js
  11. 8
      helpers/decodequeryip.js
  12. 7
      helpers/processip.js
  13. 9
      helpers/render.js
  14. 66
      helpers/tasks.js
  15. 3
      migrations/index.js
  16. 12
      migrations/migration-0.0.7.js
  17. 12
      migrations/migration-0.0.8.js
  18. 56
      migrations/migration-0.0.9.js
  19. 5
      models/forms/actionhandler.js
  20. 16
      models/forms/banposter.js
  21. 8
      models/forms/changeboardsettings.js
  22. 17
      models/forms/editpost.js
  23. 31
      models/forms/makepost.js
  24. 5
      models/forms/reportpost.js
  25. 6
      models/pages/globalmanage/logs.js
  26. 8
      models/pages/globalmanage/recent.js
  27. 10
      models/pages/manage/bans.js
  28. 10
      models/pages/manage/logs.js
  29. 11
      models/pages/manage/recent.js
  30. 11
      models/pages/manage/reports.js
  31. 26
      package-lock.json
  32. 2
      package.json
  33. 4
      queue.js
  34. 10
      redis.js
  35. 30
      schedules/webring.js
  36. 6
      server.js
  37. 1
      views/includes/bantable.pug
  38. 5
      views/includes/navbar.pug
  39. 12
      views/includes/postform.pug
  40. 5
      views/mixins/ban.pug
  41. 2
      views/mixins/catalogtile.pug
  42. 6
      views/mixins/post.pug
  43. 2
      views/mixins/report.pug
  44. 4
      views/pages/account.pug
  45. 1
      views/pages/captcha.pug
  46. 2
      views/pages/changepassword.pug
  47. 2
      views/pages/globalmanagelogs.pug
  48. 2
      views/pages/globalmanagereports.pug
  49. 15
      views/pages/globalmanagesettings.pug
  50. 2
      views/pages/login.pug
  51. 2
      views/pages/managelogs.pug
  52. 5
      views/pages/managerecent.pug
  53. 14
      views/pages/managesettings.pug
  54. 9
      worker.js

@ -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

@ -3,7 +3,7 @@
"https://example.com/webring.json"
],
"blacklist": [
"badwebsite.com",
"badwebsite.com"
],
"logo": [
"https://yourdomain.com/favicon.ico"

@ -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

@ -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'
});
}

@ -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
}, {

@ -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': {

@ -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, {

@ -46,7 +46,7 @@ module.exports = {
return db.countDocuments(addedFilter);
},
deleteAll: (board) => {
deleteAll: () => {
return db.deleteMany({});
},

@ -905,10 +905,6 @@ input:invalid, textarea:invalid {
text-align: center;
}
.nav-item:nth-of-type(3) {
line-height:1.5em;
}
.left {
float: left;
}

@ -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,

@ -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
}

@ -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 {

@ -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);

@ -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: () => {

@ -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
}

@ -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:*');
};

@ -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:*');
};

@ -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:*');
};

@ -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

@ -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,

@ -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
}
}
});

@ -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 = {

@ -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);
}
}
}

@ -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 = {

@ -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;

@ -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')

@ -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')

@ -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')

@ -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')

@ -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', {

26
package-lock.json generated

@ -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",

@ -1,7 +1,7 @@
{
"name": "jschan",
"version": "0.0.1",
"migrateVersion": "0.0.6",
"migrateVersion": "0.0.9",
"description": "",
"main": "server.js",
"dependencies": {

@ -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 = {

@ -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);

@ -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();

@ -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;

@ -5,6 +5,7 @@
th Board
th Reason
th IP
th Type
th Issuer
th Issue Date
th Expiry

@ -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

@ -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

@ -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);

@ -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

@ -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

@ -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);

@ -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')

@ -1,6 +1,7 @@
doctype html
html
head
meta(charset='utf-8')
link(rel='stylesheet', href='/css/nscaptcha.css')
body
img(src='/captcha')

@ -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

@ -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)}`) [+]

@ -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

@ -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

@ -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

@ -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}

@ -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

@ -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

@ -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;
});

Loading…
Cancel
Save