This did not go as planned

merge-requests/218/head
Thomas Lynch 3 years ago
parent 60cddef23a
commit 1c5f14e419
  1. 5
      README.md
  2. 14
      config.js
  3. 4
      configs/nginx/nginx.example
  4. 4
      configs/nginx/nginx_no_https.example
  5. 10
      controllers/forms.js
  6. 3
      controllers/forms/actions.js
  7. 3
      controllers/forms/addban.js
  8. 3
      controllers/forms/addcustompage.js
  9. 3
      controllers/forms/appeal.js
  10. 3
      controllers/forms/boardsettings.js
  11. 4
      controllers/forms/create.js
  12. 3
      controllers/forms/editpost.js
  13. 3
      controllers/forms/globalactions.js
  14. 12
      controllers/forms/globalsettings.js
  15. 3
      controllers/forms/makepost.js
  16. 4
      controllers/forms/register.js
  17. 3
      controllers/forms/uploadbanners.js
  18. 5
      controllers/pages.js
  19. 4
      db/bypass.js
  20. 6
      db/db.js
  21. 15
      db/posts.js
  22. 12
      getconfig.js
  23. 81
      gulpfile.js
  24. 9
      helpers/captcha/generators/grid.js
  25. 5
      helpers/captcha/generators/text.js
  26. 3
      helpers/captcha/verify.js
  27. 3
      helpers/checks/blockbypass.js
  28. 10
      helpers/checks/captcha.js
  29. 3
      helpers/checks/dnsbl.js
  30. 4
      helpers/checks/spamcheck.js
  31. 3
      helpers/checks/torprebypass.js
  32. 3
      helpers/decodequeryip.js
  33. 3
      helpers/filemiddlewares.js
  34. 3
      helpers/files/audiothumbnail.js
  35. 1
      helpers/files/ffprobe.js
  36. 1
      helpers/files/imageidentify.js
  37. 3
      helpers/files/imagethumbnail.js
  38. 6
      helpers/files/mimetypes.js
  39. 3
      helpers/files/videothumbnail.js
  40. 3
      helpers/geoip.js
  41. 2
      helpers/haship.js
  42. 3
      helpers/imagehash.js
  43. 7
      helpers/posting/markdown.js
  44. 2
      helpers/posting/tripcode.js
  45. 3
      helpers/processip.js
  46. 2
      helpers/referrercheck.js
  47. 7
      helpers/render.js
  48. 6
      helpers/schema.js
  49. 5
      helpers/tasks.js
  50. 2
      helpers/themes.js
  51. 4
      helpers/usesession.js
  52. 4
      migrations/0.0.5.js
  53. 2
      models/forms/actionhandler.js
  54. 4
      models/forms/addban.js
  55. 3
      models/forms/banposter.js
  56. 3
      models/forms/blockbypass.js
  57. 4
      models/forms/changeboardsettings.js
  58. 4
      models/forms/changeglobalsettings.js
  59. 4
      models/forms/create.js
  60. 4
      models/forms/deletepost.js
  61. 4
      models/forms/deletepostsfiles.js
  62. 3
      models/forms/editpost.js
  63. 7
      models/forms/makepost.js
  64. 5
      models/forms/uploadbanners.js
  65. 3
      models/pages/boardlist.js
  66. 10
      models/pages/captcha.js
  67. 3
      models/pages/overboard.js
  68. 4
      models/pages/overboardcatalog.js
  69. 2
      queue.js
  70. 8
      redis.js
  71. 4
      schedules/Schedule.js
  72. 4
      schedules/index.js
  73. 3
      schedules/tasks/ips.js
  74. 2
      schedules/tasks/prune.js
  75. 6
      schedules/tasks/webring.js
  76. 7
      server.js
  77. 4
      socketio.js
  78. 6
      worker.js

@ -107,7 +107,6 @@ geoip_city /usr/share/GeoIP/GeoIPCity.dat;
If your nginx doesn't have the necessary module by default, or is using v2 instead, find your own guide.
If you use cloudflare, please read [these](https://support.cloudflare.com/hc/en-us/articles/200170786-Restoring-original-visitor-IPs-Logging-visitor-IP-addresses-with-mod-cloudflare-) [articles](https://support.cloudflare.com/hc/en-us/articles/200168236-Configuring-Cloudflare-IP-Geolocation) to setup proper IP forwarding and geolocation headers. Similar steps would apply to other CDNs/reverse proxies.
There are also 2 config entries in configs/main.js, `ipHeader` and `countryCodeHeader` that you can tweak for jschan to use different headers for country code or real visitor IP.
Also included is an "nginx_advanced" config, and a snippets folder for advanced users who want to better organise and more easily customise the nginx configuration. It functions the same as the normal nginx.example, but you need to create the snippets folder in /etc/nginx/snippets, and copy the example snippets.
@ -115,7 +114,7 @@ Also included is an "nginx_advanced" config, and a snippets folder for advanced
```bash
# copy example config file and edit it
$ cp configs/main.js.example configs/main.js && editor configs/main.js
$ cp configs/secrets.js.example configs/secrets.js && editor configs/secrets.js
# copy example custompages for rules and faq and edit
$ cp views/custompages/faq.pug.example views/custompages/faq.pug
@ -172,7 +171,7 @@ $ git pull
#install dependencies again in case any have updated or changed
$ npm install
#diff the config files to see if anything changed and edit accordingly. OR backup your config, replace it with the fresh example, and update it with whatever settings you want to keep from your backup.
$ diff configs/main.js configs/main.js.example
$ diff configs/secrets.js configs/secrets.js.example
#run the migrate task to update your database
$ gulp migrate
#reload jschan backend

@ -0,0 +1,14 @@
'use strict';
const redis = require(__dirname+'/redis.js');
const load = async (message) => {
module.exports.get = message || (await redis.get('globalsettings'));
};
redis.addCallback('config', load);
module.exports = {
get: null,
load,
};

@ -164,7 +164,7 @@ server {
# "Other" Files for custom type uploads, uses content-disposition to prevent rendering
# inline in browser and will present a "save" dialog box. make sure these file
# extensions match for mimes defined in configs/main.js "otherMimeTypes"
# extensions match for mimes defined in global settings
# location ~* \.(txt)$ {
# access_log off;
# expires max;
@ -366,7 +366,7 @@ server {
#
# # "Other" Files for custom type uploads, uses content-disposition to prevent rendering
# # inline in browser and will present a "save" dialog box. make sure these file
# # extensions match for mimes defined in configs/main.js "otherMimeTypes"
# # extensions match for mimes defined in global settings
# location ~* \.(txt)$ {
# access_log off;
# expires max;

@ -164,7 +164,7 @@ server {
# "Other" Files for custom type uploads, uses content-disposition to prevent rendering
# inline in browser and will present a "save" dialog box. make sure these file
# extensions match for mimes defined in configs/main.js "otherMimeTypes"
# extensions match for mimes defined in global settings
# location ~* \.(txt)$ {
# access_log off;
# expires max;
@ -343,7 +343,7 @@ server {
#
# # "Other" Files for custom type uploads, uses content-disposition to prevent rendering
# # inline in browser and will present a "save" dialog box. make sure these file
# # extensions match for mimes defined in configs/main.js "otherMimeTypes"
# # extensions match for mimes defined in global settings
# location ~* \.(txt)$ {
# access_log off;
# expires max;

@ -3,8 +3,7 @@
const express = require('express')
, router = express.Router({ caseSensitive: true })
, Boards = require(__dirname+'/../db/boards.js')
, { deleteBoardPermLevel } = require(__dirname+'/../configs/main.js')
//middlewares
//middlewares
, torPreBypassCheck = require(__dirname+'/../helpers/checks/torprebypass.js')
, geoAndTor = require(__dirname+'/../helpers/geoip.js')
, processIp = require(__dirname+'/../helpers/processip.js')
@ -22,13 +21,12 @@ const express = require('express')
, dnsblCheck = require(__dirname+'/../helpers/checks/dnsbl.js')
, blockBypassCheck = require(__dirname+'/../helpers/checks/blockbypass.js')
, { handleBannerFiles, handlePostFilesEarlyTor, handlePostFiles } = require(__dirname+'/../helpers/filemiddlewares.js')
//controllers
//controllers
, deleteBoardController = require(__dirname+'/forms/deleteboard.js')
, editBansController = require(__dirname+'/forms/editbans.js')
, appealController = require(__dirname+'/forms/appeal.js')
, globalActionController = require(__dirname+'/forms/globalactions.js')
, actionController = require(__dirname+'/forms/actions.js')
// , addBanController = require(__dirname+'/forms/addban.js')
, addCustomPageController = require(__dirname+'/forms/addcustompage.js')
, deleteCustomPageController = require(__dirname+'/forms/deletecustompage.js')
, addNewsController = require(__dirname+'/forms/addnews.js')
@ -76,13 +74,13 @@ router.post('/board/:board/addcustompages', /*geoAndTor, torPreBypassCheck, proc
router.post('/board/:board/deletecustompages', /*geoAndTor, torPreBypassCheck, processIp,*/ useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, deleteCustomPageController); //delete banners
//router.post('/board/:board/addban', geoAndTor, torPreBypassCheck, processIp, useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(3), paramConverter, addBanController); //add ban manually without post
router.post('/board/:board/editbans', /*geoAndTor, torPreBypassCheck, processIp,*/ useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(3), paramConverter, editBansController); //edit bans
router.post('/board/:board/deleteboard', /*geoAndTor, torPreBypassCheck, processIp,*/ useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(deleteBoardPermLevel), deleteBoardController); //delete board
router.post('/board/:board/deleteboard', /*geoAndTor, torPreBypassCheck, processIp,*/ useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(), deleteBoardController); //delete board
//global management forms
router.post('/global/editbans', useSession, sessionRefresh, csrf, calcPerms, isLoggedIn, hasPerms(1), paramConverter, editBansController); //remove bans
//commented out for now, because we cant add a manual ban based on a non existing hash suffix (or fetch the full hash from a non existing post), and the user wouldnt know if it the post didn't exist so its pointless anyway.
//router.post('/global/addban', geoAndTor, torPreBypassCheck, processIp, useSession, sessionRefresh, csrf, calcPerms, isLoggedIn, hasPerms(1), paramConverter, addBanController); //add ban manually without post
router.post('/global/deleteboard', useSession, sessionRefresh, csrf, paramConverter, calcPerms, isLoggedIn, hasPerms(Math.min(deleteBoardPermLevel, 1)), deleteBoardController); //delete board from global management panel
router.post('/global/deleteboard', useSession, sessionRefresh, csrf, paramConverter, calcPerms, isLoggedIn, hasPerms(1), deleteBoardController); //delete board from global management panel
router.post('/global/addnews', useSession, sessionRefresh, csrf, calcPerms, isLoggedIn, hasPerms(0), addNewsController); //add new newspost
router.post('/global/editnews', useSession, sessionRefresh, csrf, calcPerms, isLoggedIn, hasPerms(0), paramConverter, editNewsController); //add new newspost
router.post('/global/deletenews', useSession, sessionRefresh, csrf, calcPerms, isLoggedIn, hasPerms(0), paramConverter, deleteNewsController); //delete news

@ -1,13 +1,14 @@
'use strict';
const { Posts } = require(__dirname+'/../../db/')
, { globalLimits } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, actionHandler = require(__dirname+'/../../models/forms/actionhandler.js')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
, actionChecker = require(__dirname+'/../../helpers/checks/actionchecker.js');
module.exports = async (req, res, next) => {
const { globalLimits } = config.get;
const errors = [];
//make sure they checked 1-10 posts

@ -1,12 +1,13 @@
'use strict';
const { globalLimits, ipHashPermLevel } = require(__dirname+'/../../configs/main.js')
const config = require(__dirname+'/../../config.js')
, addBan = require(__dirname+'/../../models/forms/addban.js')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
, { isIP } = require('net');
module.exports = async (req, res, next) => {
const { globalLimits, ipHashPermLevel } = config.get;
const errors = [];
if (!req.body.ip || req.body.ip.length === 0) {

@ -3,10 +3,11 @@
const addCustomPage = require(__dirname+'/../../models/forms/addcustompage.js')
, { CustomPages } = require(__dirname+'/../../db/')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
, { globalLimits } = require(__dirname+'/../../configs/main.js');
, config = require(__dirname+'/../../config.js');
module.exports = async (req, res, next) => {
const { globalLimits } = config.get;
const errors = [];
if (!req.body.message || res.locals.messageLength === 0) {

@ -1,12 +1,13 @@
'use strict';
const appealBans = require(__dirname+'/../../models/forms/appeal.js')
, { globalLimits } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
, { Bans } = require(__dirname+'/../../db');
module.exports = async (req, res, next) => {
const { globalLimits } = config.get;
const errors = [];
if (!req.body.checkedbans || req.body.checkedbans.length === 0 || req.body.checkedbans.length > 10) {
errors.push('Must select 1-10 bans');

@ -4,10 +4,11 @@ const changeBoardSettings = require(__dirname+'/../../models/forms/changeboardse
, { themes, codeThemes } = require(__dirname+'/../../helpers/themes.js')
, { Ratelimits } = require(__dirname+'/../../db/')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
, { globalLimits, rateLimitCost } = require(__dirname+'/../../configs/main.js');
, config = require(__dirname+'/../../config.js');
module.exports = async (req, res, next) => {
const { globalLimits, rateLimitCost } = config.get;
const errors = [];
//TODO: add helpers for different checks, passing name, min/max and return true with error if hit

@ -2,11 +2,13 @@
const createBoard = require(__dirname+'/../../models/forms/create.js')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
, { enableUserBoardCreation, globalLimits } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, alphaNumericRegex = require(__dirname+'/../../helpers/checks/alphanumregex.js')
module.exports = async (req, res, next) => {
const { enableUserBoardCreation, globalLimits } = config.get;
if (enableUserBoardCreation === false && res.locals.permLevel > 1) {
return dynamicResponse(req, res, 400, 'message', {
'title': 'Bad request',

@ -2,11 +2,12 @@
const editPost = require(__dirname+'/../../models/forms/editpost.js')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
, { rateLimitCost, globalLimits } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, { Ratelimits, Posts, Boards } = require(__dirname+'/../../db/');
module.exports = async (req, res, next) => {
const { rateLimitCost, globalLimits } = config.get;
const errors = [];
if ((!req.body.board || req.body.board.length === 0)

@ -2,12 +2,13 @@
const { Posts } = require(__dirname+'/../../db/')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
, { globalLimits } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, actionHandler = require(__dirname+'/../../models/forms/actionhandler.js')
, actionChecker = require(__dirname+'/../../helpers/checks/actionchecker.js');
module.exports = async (req, res, next) => {
const { globalLimits } = config.get;
const errors = [];
if (!req.body.globalcheckedposts || req.body.globalcheckedposts.length === 0) {

@ -16,13 +16,13 @@ module.exports = async (req, res, next) => {
}
*/
//not declared at topf of file because getConfig() values could change, so maybe a lil slower.
//not declared at topf of file because values could change, so maybe a lil slower.
//at some point in future the schemas can stored in a common place and updated along with config in redis sub
const schema = [
{ result: lengthBody(req.body.filters, 0, 5000), expected: false, error: 'Filter text cannot exceed 5000 characters' },
{ result: numberBody(req.body.filter_mode, 0, 2), expected: false, error: 'Filter mode must be a number from 0-2' },
{ result: numberBody(req.body.ban_duration), expected: false, error: 'Invalid filter auto ban duration' },
{ result: lengthBody(req.body.allowed_hosts, 0, 100), expected: false, error: 'Allowed hosts must not exceed 100 entries' },
{ result: lengthBody(req.body.allowed_hosts, 0, 10000), expected: false, error: 'Allowed hosts must not exceed 100 entries' },
{ result: lengthBody(req.body.country_code_header, 0, 100), expected: false, error: 'Country code header length must not exceed 100 characters' },
{ result: lengthBody(req.body.ip_header, 0, 100), expected: false, error: 'IP header length must not exceed 100 characters' },
{ result: lengthBody(req.body.meta_site_name, 0, 100), expected: false, error: 'Meta site name must not exceed 100 characters' },
@ -34,10 +34,12 @@ module.exports = async (req, res, next) => {
{ result: numberBody(req.body.captcha_options_grid_icon_y_offset, 0, 50), expected: false, error: 'Captcha options icon y offset must be a number from 0-50' },
{ result: numberBody(req.body.captcha_options_num_distorts_min, 0, 10), expected: false, error: 'Captcha options min distorts must be a number from 0-10' },
{ result: numberBody(req.body.captcha_options_num_distorts_max, 0, 10), expected: false, error: 'Captcha options max distorts must be a number from 0-10' },
{ result: minmaxBody(req.body.captcha_options_num_distorts_min, req.body.captcha_options_num_distorts_max), expected: false, error: 'Captcha options distorts min must be less than max' },
{ result: minmaxBody(req.body.captcha_options_num_distorts_min, req.body.captcha_options_num_distorts_max), expected: true, error: 'Captcha options distorts min must be less than max' },
{ result: numberBody(req.body.captcha_options_distortion, 0, 50), expected: false, error: 'Captcha options distortion must be a number from 0-50' },
];
console.log(schema)
/*
captchaOptions: {
numDistorts: {
@ -180,7 +182,9 @@ module.exports = async (req, res, next) => {
};
*/
const errors = checkSchema(schema);
const errors = await checkSchema(schema);
console.log(errors)
if (errors.length > 0) {
return dynamicResponse(req, res, 400, 'message', {

@ -4,11 +4,12 @@ const makePost = require(__dirname+'/../../models/forms/makepost.js')
, deleteTempFiles = require(__dirname+'/../../helpers/files/deletetempfiles.js')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
, { func: pruneFiles } = require(__dirname+'/../../schedules/tasks/prune.js')
, { pruneImmediately, globalLimits, disableAnonymizerFilePosting } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, { Files } = require(__dirname+'/../../db/');
module.exports = async (req, res, next) => {
const { pruneImmediately, globalLimits, disableAnonymizerFilePosting } = config.get;
const errors = [];
// even if force file and message are off, the post must contain one of either.

@ -2,11 +2,13 @@
const alphaNumericRegex = require(__dirname+'/../../helpers/checks/alphanumregex.js')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
, { enableUserAccountCreation } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, registerAccount = require(__dirname+'/../../models/forms/register.js');
module.exports = async (req, res, next) => {
const { enableUserAccountCreation } = config.get;
if (enableUserAccountCreation === false && res.locals.permLevel > 1) {
return dynamicResponse(req, res, 400, 'message', {
'title': 'Bad request',

@ -3,10 +3,11 @@
const uploadBanners = require(__dirname+'/../../models/forms/uploadbanners.js')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
, deleteTempFiles = require(__dirname+'/../../helpers/files/deletetempfiles.js')
, { globalLimits } = require(__dirname+'/../../configs/main.js');
, config = require(__dirname+'/../../config.js');
module.exports = async (req, res, next) => {
const { globalLimits } = config.get;
const errors = [];
if (res.locals.numFiles === 0) {

@ -4,7 +4,6 @@ const express = require('express')
, router = express.Router({ caseSensitive: true })
, Boards = require(__dirname+'/../db/boards.js')
, Posts = require(__dirname+'/../db/posts.js')
, { captchaOptions } = require(__dirname+'/../configs/main.js')
//middlewares
, processIp = require(__dirname+'/../helpers/processip.js')
, geoAndTor = require(__dirname+'/../helpers/geoip.js')
@ -75,9 +74,7 @@ router.get('/editnews/:newsid([a-f0-9]{24}).html', useSession, sessionRefresh, i
//TODO: edit board custom page get endpoint
//captcha
if (captchaOptions.type !== 'google' && captchaOptions.type !== 'hcaptcha') {
router.get('/captcha', geoAndTor, processIp, captcha); //get captcha image and cookie
}
router.get('/captcha', geoAndTor, processIp, captcha); //get captcha image and cookie
router.get('/captcha.html', captchaPage); //iframed for noscript users
router.get('/bypass.html', blockBypass); //block bypass page
router.get('/bypass_minimal.html', setMinimal, blockBypass); //block bypass page

@ -1,7 +1,7 @@
'use strict';
const Mongo = require(__dirname+'/db.js')
, { blockBypass } = require(__dirname+'/../configs/main.js')
, config = require(__dirname+'/../config.js')
, db = Mongo.db.collection('bypass');
module.exports = {
@ -9,6 +9,7 @@ module.exports = {
db,
checkBypass: (id) => {
const { blockBypass } = config.get;
return db.findOneAndUpdate({
'_id': id,
'uses': {
@ -22,6 +23,7 @@ module.exports = {
},
getBypass: () => {
const { blockBypass } = config.get;
return db.insertOne({
'uses': 0,
'expireAt': new Date(Date.now() + blockBypass.expireAfterTime)

@ -1,6 +1,6 @@
'use strict';
const configs = require(__dirname+'/../getconfig.js')()
const secrets = require(__dirname+'/../configs/secrets.js')
, { MongoClient, ObjectId, Int32 } = require('mongodb')
, { migrateVersion } = require(__dirname+'/../package.json')
@ -10,11 +10,11 @@ module.exports = {
if (module.exports.client) {
throw new Error('Mongo already connected');
}
module.exports.client = await MongoClient.connect(configs.dbURL, {
module.exports.client = await MongoClient.connect(secrets.dbURL, {
useNewUrlParser: true,
useUnifiedTopology: true
});
module.exports.db = module.exports.client.db(configs.dbName);
module.exports.db = module.exports.client.db(secrets.dbName);
},
checkVersion: async() => {

@ -4,8 +4,7 @@ const Mongo = require(__dirname+'/db.js')
, Boards = require(__dirname+'/boards.js')
, Stats = require(__dirname+'/stats.js')
, db = Mongo.db.collection('posts')
, { quoteLimit, previewReplies, stickyPreviewReplies, statsCountAnonymizers,
ipHashPermLevel, early404Replies, early404Fraction } = require(__dirname+'/../configs/main.js');
, config = require(__dirname+'/../config.js');
module.exports = {
@ -35,7 +34,7 @@ module.exports = {
} else if (typeof ip === 'string') {
query['ip.raw'] = ip;
}
if (permLevel > ipHashPermLevel) {
if (permLevel > getconfig.ipHashPermLevel) {
projection['ip.raw'] = 0;
}
return db.find(query, {
@ -59,7 +58,7 @@ module.exports = {
} else if (typeof ip === 'string') {
query['ip.raw'] = ip;
}
if (permLevel > ipHashPermLevel) {
if (permLevel > getconfig.ipHashPermLevel) {
projection['ip.raw'] = 0;
}
return db.find(query, {
@ -112,6 +111,7 @@ module.exports = {
// add last n posts in reverse order to preview
await Promise.all(threads.map(async thread => {
const { stickyPreviewReplies, previewReplies } = config.get;
const previewRepliesLimit = thread.sticky ? stickyPreviewReplies : previewReplies;
const replies = previewRepliesLimit === 0 ? [] : await db.find({
'thread': thread.postId,
@ -412,6 +412,7 @@ module.exports = {
// get only thread and post id for use in quotes
getPostsForQuotes: (queryOrs) => {
const { quoteLimit } = config.get;
return db.find({
'$or': queryOrs
}, {
@ -474,7 +475,7 @@ module.exports = {
//insert the post itself
const postMongoId = await db.insertOne(data).then(result => result.insertedId); //_id of post
const statsIp = (statsCountAnonymizers === false && res.locals.anonymizer === true) ? null : data.ip.single;
const statsIp = (getconfig.statsCountAnonymizers === false && res.locals.anonymizer === true) ? null : data.ip.single;
await Stats.updateOne(board._id, statsIp, data.thread == null);
//add backlinks to the posts this post quotes
@ -598,13 +599,13 @@ module.exports = {
}
}, {
//skip the first (board.settings.threadLimit/early404Fraction)
'$skip': Math.ceil(board.settings.threadLimit/early404Fraction)
'$skip': Math.ceil(board.settings.threadLimit/getconfig.early404Fraction)
}, {
//then any that have less than early404Replies replies get matched again
'$match': {
'sticky':0,
'replyposts': {
'$lt': early404Replies
'$lt': getconfig.early404Replies
}
}
}

@ -1,12 +0,0 @@
'use strict';
const redis = require(__dirname+'/redis.js');
const loadConfig = (message) => {
config = message || redis.get('globalsettings');
}
loadConfig();
redis.addCallback('config', loadConfig);
module.exports = () => { return config };

@ -1,12 +1,11 @@
'use strict';
const gulp = require('gulp')
const config = require(__dirname+'/config.js')
, { hcaptcha, google } = require(__dirname+'/configs/secrets.js')
, gulp = require('gulp')
, fs = require('fs-extra')
, semver = require('semver')
, formatSize = require(__dirname+'/helpers/files/formatsize.js')
, uploadDirectory = require(__dirname+'/helpers/files/uploadDirectory.js')
, secrets = require(__dirname+'/configs/secrets.js')
, { themes, codeThemes } = require(__dirname+'/helpers/themes.js')
, commit = require(__dirname+'/helpers/commit.js')
, less = require('gulp-less')
, concat = require('gulp-concat')
@ -17,6 +16,7 @@ const gulp = require('gulp')
, gulppug = require('gulp-pug')
, { migrateVersion } = require(__dirname+'/package.json')
, { randomBytes } = require('crypto')
, Redis = require(__dirname+'/redis.js')
, paths = {
styles: {
src: 'gulp/res/css/',
@ -161,24 +161,25 @@ async function wipe() {
//update the css file
async function css() {
await config.load();
try {
//a little more configurable
let bypassHeight = (configs.captchaOptions.type === 'google' || configs.captchaOptions.type === 'hcaptcha')
let bypassHeight = (config.get.captchaOptions.type === 'google' || config.get.captchaOptions.type === 'hcaptcha')
? 500
: configs.captchaOptions.type === 'grid'
: config.get.captchaOptions.type === 'grid'
? 330
: 235;
let captchaHeight = configs.captchaOptions.type === 'text' ? 80
: configs.captchaOptions.type === 'grid' ? configs.captchaOptions.grid.imageSize+30
let captchaHeight = config.get.captchaOptions.type === 'text' ? 80
: config.get.captchaOptions.type === 'grid' ? config.get.captchaOptions.grid.imageSize+30
: 200; //google/hcaptcha doesnt need this set
let captchaWidth = configs.captchaOptions.type === 'text' ? 210
: configs.captchaOptions.type === 'grid' ? configs.captchaOptions.grid.imageSize+30
let captchaWidth = config.get.captchaOptions.type === 'text' ? 210
: config.get.captchaOptions.type === 'grid' ? config.get.captchaOptions.grid.imageSize+30
: 200; //google/hcaptcha doesnt need this set
const cssLocals = `:root {
--attachment-img: url('/file/attachment.png');
--spoiler-img: url('/file/spoiler.png');
--audio-img: url('/file/audio.png');
--thumbnail-size: ${configs.thumbSize}px;
--thumbnail-size: ${config.get.thumbSize}px;
--captcha-w: ${captchaWidth}px;
--captcha-h: ${captchaHeight}px;
--bypass-height: ${bypassHeight}px;
@ -254,7 +255,9 @@ function deletehtml() {
return del([ 'static/html/*' ]);
}
function custompages() {
async function custompages() {
await config.load();
const formatSize = require(__dirname+'/helpers/files/formatsize.js');
return gulp.src([
`${paths.pug.src}/custompages/*.pug`,
`${paths.pug.src}/pages/404.pug`,
@ -265,34 +268,36 @@ function custompages() {
])
.pipe(gulppug({
locals: {
early404Fraction: configs.early404Fraction,
early404Replies: configs.early404Replies,
meta: configs.meta,
enableWebring: configs.enableWebring,
globalLimits: configs.globalLimits,
codeLanguages: configs.highlightOptions.languageSubset,
defaultTheme: configs.boardDefaults.theme,
defaultCodeTheme: configs.boardDefaults.codeTheme,
postFilesSize: formatSize(configs.globalLimits.postFilesSize.max),
captchaType: configs.captchaOptions.type,
googleRecaptchaSiteKey: configs.captchaOptions.google.siteKey,
hcaptchaSitekey: configs.captchaOptions.hcaptcha.siteKey,
captchaGridSize: configs.captchaOptions.grid.size,
early404Fraction: config.get.early404Fraction,
early404Replies: config.get.early404Replies,
meta: config.get.meta,
enableWebring: config.get.enableWebring,
globalLimits: config.get.globalLimits,
codeLanguages: config.get.highlightOptions.languageSubset,
defaultTheme: config.get.boardDefaults.theme,
defaultCodeTheme: config.get.boardDefaults.codeTheme,
postFilesSize: formatSize(config.get.globalLimits.postFilesSize.max),
captchaType: config.get.captchaOptions.type,
googleRecaptchaSiteKey: google.siteKey,
hcaptchaSitekey: hcaptcha.siteKey,
captchaGridSize: config.get.captchaOptions.grid.size,
commit,
}
}))
.pipe(gulp.dest(paths.pug.dest));
}
function scripts() {
async function scripts() {
await config.load();
const { themes, codeThemes } = require(__dirname+'/helpers/themes.js');
try {
const locals = `const themes = ['${themes.join("', '")}'];
const codeThemes = ['${codeThemes.join("', '")}'];
const captchaType = '${configs.captchaOptions.type}';
const captchaGridSize = ${configs.captchaOptions.grid.size};
const captchaType = '${config.get.captchaOptions.type}';
const captchaGridSize = ${config.get.captchaOptions.grid.size};
const SERVER_TIMEZONE = '${Intl.DateTimeFormat().resolvedOptions().timeZone}';
const ipHashPermLevel = ${configs.ipHashPermLevel};
const settings = ${JSON.stringify(configs.frontendScriptDefault)};
const ipHashPermLevel = ${config.get.ipHashPermLevel};
const settings = ${JSON.stringify(config.get.frontendScriptDefault)};
`;
fs.writeFileSync('gulp/res/js/locals.js', locals);
fs.writeFileSync('gulp/res/js/post.js', pug.compileFileClient(`${paths.pug.src}/includes/post.pug`, { compileDebug: false, debug: false, name: 'post' }));
@ -391,18 +396,22 @@ async function migrate() {
}
const build = gulp.parallel(gulp.series(scripts, css), images, icons, gulp.series(deletehtml, custompages));
const reset = gulp.series(wipe, build);
const html = gulp.series(deletehtml, custompages);
async function closeRedis() {
Redis.close();
}
const build = gulp.series(gulp.parallel(gulp.series(scripts, css), images, icons, gulp.series(deletehtml, custompages)), closeRedis);
const reset = gulp.series(wipe, build, closeRedis);
const html = gulp.series(deletehtml, custompages, closeRedis);
module.exports = {
html,
css,
css: gulp.series(css, closeRedis),
images,
icons,
reset,
custompages,
scripts,
custompages: gulp.series(custompages, closeRedis),
scripts: gulp.series(scripts, closeRedis),
wipe,
cache,
migrate,

@ -4,18 +4,19 @@ const gm = require('gm').subClass({ imageMagick: true })
, { promisify } = require('util')
, randomBytes = promisify(require('crypto').randomBytes)
, { Captchas } = require(__dirname+'/../../../db/')
, { captchaOptions } = require(__dirname+'/../../../configs/main.js')
, config = require(__dirname+'/../../../config.js')
, uploadDirectory = require(__dirname+'/../../files/uploadDirectory.js')
, randomRange = require(__dirname+'/../../randomrange.js')
, padding = 30
, width = captchaOptions.grid.imageSize+padding
, height = captchaOptions.grid.imageSize+padding
, gridSize = captchaOptions.grid.size
, zeros = ['○','□','♘','♢','▽','△','♖','✧','♔','♘','♕','♗','♙','♧']
, ones = ['●','■','♞','♦','▼','▲','♜','✦','♚','♞','♛','♝','♟','♣']
, colors = ['#FF8080', '#80FF80', '#8080FF', '#FF80FF', '#FFFF80', '#80FFFF']
module.exports = async () => {
const { captchaOptions } = config.get;
const gridSize = captchaOptions.grid.size
const width = captchaOptions.grid.imageSize+padding;
const height = captchaOptions.grid.imageSize+padding;
//number of inputs in grid
const numInputs = gridSize**2;
//random buffer to get true/false for grid from

@ -2,7 +2,7 @@
const gm = require('gm').subClass({ imageMagick: true })
, { Captchas } = require(__dirname+'/../../../db/')
, { captchaOptions } = require(__dirname+'/../../../configs/main.js')
, config = require(__dirname+'/../../../config.js')
, uploadDirectory = require(__dirname+'/../../files/uploadDirectory.js')
, characterWidth = (char) => {
switch (char) {
@ -27,7 +27,6 @@ const gm = require('gm').subClass({ imageMagick: true })
}
, width = 210
, height = 80
, distortion = captchaOptions.distortion
, minVal = parseInt('1000000', 36)
, maxVal = parseInt('1zzzzzz', 36)
, randomRange = require(__dirname+'/../../randomrange.js');
@ -35,6 +34,8 @@ const gm = require('gm').subClass({ imageMagick: true })
module.exports = async () => {
// generate between 1000000 and 1zzzzzz and not 0 and zzzzzz, so toString
// will have enough characters
const { captchaOptions } = config.get;
const distortion = captchaOptions.distortion
const textInt = await randomRange(minVal, maxVal);
const text = textInt.toString(36).substr(-6, 6);
const captchaId = await Captchas.insertOne(text).then(r => r.insertedId);

@ -3,7 +3,7 @@
const { Ratelimits } = require(__dirname+'/../../db/')
, { ObjectId } = require(__dirname+'/../../db/db.js')
, checkCaptcha = require(__dirname+'/../checks/captcha.js')
, { captchaOptions } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, remove = require('fs-extra').remove
, dynamicResponse = require(__dirname+'/../dynamic.js')
, deleteTempFiles = require(__dirname+'/../files/deletetempfiles.js')
@ -45,6 +45,7 @@ module.exports = async (req, res, next) => {
//it was correct, so mark as solved for other middleware
res.locals.solvedCaptcha = true;
const { captchaOptions } = config.get;
if (captchaOptions.type !== 'google' && captchaOptions.type !== 'hcaptcha') {
//for builtin captchas, clear captchaid cookie, delete file and reset quota
res.clearCookie('captchaid');

@ -2,13 +2,14 @@
const { Bypass } = require(__dirname+'/../../db/')
, { ObjectId } = require(__dirname+'/../../db/db.js')
, { secureCookies, blockBypass } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, deleteTempFiles = require(__dirname+'/../files/deletetempfiles.js')
, dynamicResponse = require(__dirname+'/../dynamic.js')
, production = process.env.NODE_ENV === 'production';
module.exports = async (req, res, next) => {
const { secureCookies, blockBypass } = config.get;
if (res.locals.preFetchedBypassId //if they already have a bypass
|| (!blockBypass.enabled //or if block bypass isnt enabled
&& (!blockBypass.forceAnonymizers //and we dont force it for anonymizer

@ -2,13 +2,15 @@
const { Captchas } = require(__dirname+'/../../db/')
, { ObjectId } = require(__dirname+'/../../db/db.js')
, { captchaOptions } = require(__dirname+'/../../configs/main.js')
, { hcaptcha, google } = require(__dirname+'/../../configs/secrets.js')
, FormData = require('form-data')
, fetch = require('node-fetch')
, { timingSafeEqual } = require('crypto')
module.exports = async (captchaInput, captchaId) => {
const { captchaOptions } = config.get;
//check if captcha field in form is valid
if (!captchaInput
|| (captchaInput.length !== 6 && !captchaOptions.type === 'text')) {
@ -56,7 +58,7 @@ module.exports = async (captchaInput, captchaId) => {
let recaptchaResponse;
try {
const form = new FormData();
form.append('secret', captchaOptions.google.secretKey);
form.append('secret', google.secretKey);
form.append('response', captchaInput[0]);
recaptchaResponse = await fetch('https://www.google.com/recaptcha/api/siteverify', {
method: 'POST',
@ -73,8 +75,8 @@ module.exports = async (captchaInput, captchaId) => {
let hcaptchaResponse;
try {
const form = new FormData();
form.append('secret', captchaOptions.hcaptcha.secretKey);
form.append('sitekey', captchaOptions.hcaptcha.siteKey);
form.append('secret', hcaptcha.secretKey);
form.append('sitekey', hcaptcha.siteKey);
form.append('response', captchaInput[0]);
hcaptchaResponse = await fetch('https://hcaptcha.com/siteverify', {
method: 'POST',

@ -3,11 +3,12 @@
const cache = require(__dirname+'/../../redis.js')
, dynamicResponse = require(__dirname+'/../dynamic.js')
, deleteTempFiles = require(__dirname+'/../files/deletetempfiles.js')
, { ipHeader, dnsbl, blockBypass } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, { batch } = require('dnsbl');
module.exports = async (req, res, next) => {
const { ipHeader, dnsbl, blockBypass } = config.get;
if (dnsbl.enabled && dnsbl.blacklists.length > 0 //if dnsbl enabled and has more than 0 blacklists
&& !res.locals.anonymizer //anonymizers cant be dnsbl'd
&& (!res.locals.blockBypass || !blockBypass.bypassDnsbl)) { //and there is no valid block bypass, or they do not bypass dnsbl

@ -3,10 +3,12 @@
const Mongo = require(__dirname+'/../../db/db.js')
, { Posts } = require(__dirname+'/../../db/')
, timeUtils = require(__dirname+'/../timeutils.js')
, { sameContentSameIp, sameContentAnyIp, anyContentSameIp } = require(__dirname+'/../../configs/main.js').floodTimers;
, config = require(__dirname+'/../../config.js');
module.exports = async (req, res) => {
const { sameContentSameIp, sameContentAnyIp, anyContentSameIp } = config.get.floodTimers;
if (res.locals.permLevel <= 1) { //global staff bypass spam check
return false;
}

@ -2,7 +2,7 @@
const { Bypass } = require(__dirname+'/../../db/')
, { ObjectId } = require(__dirname+'/../../db/db.js')
, { secureCookies, blockBypass } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, checkCaptcha = require(__dirname+'/../checks/captcha.js')
, remove = require('fs-extra').remove
, uploadDirectory = require(__dirname+'/../files/uploadDirectory.js')
@ -19,6 +19,7 @@ module.exports = async (req, res, next) => {
let bypassId = req.signedCookies.bypassid;
const { secureCookies, blockBypass } = config.get;
if (blockBypass.enabled || blockBypass.forceAnonymizers) {
const input = req.body.captcha;
const captchaId = req.cookies.captchaid;

@ -2,9 +2,10 @@
const escapeRegExp = require(__dirname+'/escaperegexp.js')
, { isIP } = require('net')
, { ipHashPermLevel } = require(__dirname+'/../configs/main.js')
, config = require(__dirname+'/../config.js')
module.exports = (query, permLevel) => {
const { ipHashPermLevel } = config.get;
if (query.ip && typeof query.ip === 'string') {
const decoded = decodeURIComponent(query.ip);
if (permLevel <= ipHashPermLevel && (isIP(decoded) || decoded.match(/[a-z0-9]{24}/i))) { //if perms to view raw ip or bypass, allow querying

@ -1,6 +1,7 @@
'use strict';
const { globalLimits, debugLogs, filterFileNames, spaceFileNameReplacement } = require(__dirname+'/../configs/main.js')
const { debugLogs } = require(__dirname+'/../configs/secrets.js')
, { globalLimits, filterFileNames, spaceFileNameReplacement } = require(__dirname+'/../config.js').get
, dynamicResponse = require(__dirname+'/dynamic.js')
, uploadLimitFunction = (req, res, next) => {
return dynamicResponse(req, res, 413, 'message', {

@ -1,9 +1,10 @@
const ffmpeg = require('fluent-ffmpeg')
, { thumbSize } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, uploadDirectory = require(__dirname+'/uploadDirectory.js');
module.exports = (file) => {
const { thumbSize } = config.get;
return new Promise((resolve, reject) => {
ffmpeg(`${uploadDirectory}/file/${file.filename}`)
.on('end', () => {

@ -1,5 +1,4 @@
const ffmpeg = require('fluent-ffmpeg')
, configs = require(__dirname+'/../../configs/main.js')
, uploadDirectory = require(__dirname+'/uploadDirectory.js');
module.exports = (filename, folder, temp) => {

@ -1,5 +1,4 @@
const gm = require('gm')
, configs = require(__dirname+'/../../configs/main.js')
, uploadDirectory = require(__dirname+'/uploadDirectory.js');
module.exports = (filename, folder, temp) => {

@ -1,10 +1,11 @@
const gm = require('gm')
, ffmpeg = require('fluent-ffmpeg')
, { thumbSize, ffmpegGifThumbnails } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, uploadDirectory = require(__dirname+'/uploadDirectory.js');
module.exports = (file, firstFrameOnly=true) => {
const { thumbSize, ffmpegGifThumbnails } = config.get;
return new Promise((resolve, reject) => {
if (ffmpegGifThumbnails && !firstFrameOnly) {
const thumbSizeFilter = file.geometry.width > file.geometry.height ? `${thumbSize}:-1` : `-1:${thumbSize}`;

@ -1,7 +1,7 @@
'use strict';
const FileType = require('file-type')
, { allowMimeNoMatch } = require(__dirname+'/../../configs/main.js');
, { allowMimeNoMatch } = require(__dirname+'/../../configs/secrets.js');
const image = new Set([
'image/jpeg',
@ -34,7 +34,7 @@ const audio = new Set([
'audio/wav',
]);
const other = new Set(require(__dirname+'/../../configs/main.js').otherMimeTypes);
const other = new Set(require(__dirname+'/../../configs/secrets.js').otherMimeTypes);
module.exports = {
@ -50,7 +50,7 @@ module.exports = {
const supposedMimeType = file.mimetype;
const realMimeType = await FileType.fromFile(file.tempFilePath);
if (!realMimeType) {
return allowMimeNoMatch;
return getconfig.allowMimeNoMatch;
}
return supposedMimeType === realMimeType.mime;
},

@ -1,8 +1,9 @@
const ffmpeg = require('fluent-ffmpeg')
, { thumbSize } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, uploadDirectory = require(__dirname+'/uploadDirectory.js');
module.exports = (file, geometry, timestamp) => {
const { thumbSize } = config.get;
return new Promise((resolve, reject) => {
const command = ffmpeg(`${uploadDirectory}/file/${file.filename}`)
.on('end', () => {

@ -1,9 +1,10 @@
'use strict'
const { countryNamesMap, isAnonymizer } = require(__dirname+'/countries.js')
, { countryCodeHeader } = require(__dirname+'/../configs/main.js')
, config = require(__dirname+'/../config.js')
module.exports = (req, res, next) => {
const { countryCodeHeader } = config.get;
const code = req.headers[countryCodeHeader] || 'XX';
res.locals.anonymizer = isAnonymizer(code);
res.locals.country = {

@ -1,6 +1,6 @@
'use strict';
const { ipHashSecret } = require(__dirname+'/../configs/main.js')
const { ipHashSecret } = require(__dirname+'/../configs/secrets.js')
, { createHash } = require('crypto');
module.exports = (ip) => createHash('sha256').update(ipHashSecret + ip).digest('base64');

@ -1,9 +1,10 @@
'use strict';
const imageHash = require('node-image-hash').hash
, { hashImages } = require(__dirname+'/../configs/main.js');
, config = require(__dirname+'/../config.js');
module.exports = async (req, res, next) => {
const { hashImages } = config.get;
if (hashImages && res.locals.numFiles > 0 && res.locals.permLevel > 1) {
const hashPromises = [];
for (let i = 0; i < res.locals.numFiles; i++) {

@ -17,7 +17,7 @@ const greentextRegex = /^&gt;((?!&gt;\d+|&gt;&gt;&#x2F;\w+(&#x2F;\d*)?|&gt;&gt;#
, trimNewlineRegex = /^\s*(\r?\n)*|(\r?\n)*$/g
, escape = require(__dirname+'/escape.js')
, { highlight, highlightAuto } = require('highlight.js')
, { highlightOptions } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, diceroll = require(__dirname+'/diceroll.js')
, linkmatch = require(__dirname+'/linkmatch.js')
, replacements = [
@ -54,6 +54,7 @@ module.exports = {
markdown: (text) => {
const chunks = text.split(splitRegex);
const { highlightOptions } = config.get;
for (let i = 0; i < chunks.length; i++) {
//every other chunk will be a code block
if (i % 2 === 0) {
@ -61,13 +62,13 @@ module.exports = {
const newlineFix = escaped.replace(/^\r?\n/,''); //fix ending newline because of codeblock
chunks[i] = module.exports.processRegularChunk(newlineFix);
} else {
chunks[i] = module.exports.processCodeChunk(chunks[i]);
chunks[i] = module.exports.processCodeChunk(chunks[i], highlightOptions);
}
}
return chunks.join('');
},
processCodeChunk: (text) => {
processCodeChunk: (text, highlightOptions) => {
const matches = text.match(codeRegex);
const trimFix = matches.groups.code.replace(trimNewlineRegex, '');
let lang;

@ -1,6 +1,6 @@
'use strict';
const { tripcodeSecret } = require(__dirname+'/../../configs/main.js')
const { tripcodeSecret } = require(__dirname+'/../../configs/secrets.js')
, { createHash } = require('crypto')
, { encode } = require('iconv-lite')
, crypt = require('unix-crypt-td-js')

@ -1,6 +1,6 @@
'use strict';
const { ipHeader, ipHashPermLevel } = require(__dirname+'/../configs/main.js')
const config = require(__dirname+'/../config.js')
, { parse } = require('ip6addr')
, deleteTempFiles = require(__dirname+'/files/deletetempfiles.js')
, dynamicResponse = require(__dirname+'/dynamic.js')
@ -21,6 +21,7 @@ module.exports = (req, res, next) => {
}
//ip for normal user
const { ipHeader, ipHashPermLevel } = config.get;
const ip = req.headers[ipHeader] || req.connection.remoteAddress;
try {
const ipParsed = parse(ip);

@ -1,6 +1,6 @@
'use strict';
const { refererCheck, allowedHosts } = require(__dirname+'/../configs/main.js')
const { refererCheck, allowedHosts } = require(__dirname+'/../configs/secrets.js')
, dynamicResponse = require(__dirname+'/dynamic.js')
, allowedHostSet = new Set(allowedHosts);

@ -2,13 +2,14 @@
const { enableUserBoardCreation, enableUserAccountCreation,
lockWait, globalLimits, boardDefaults, cacheTemplates,
meta, enableWebring, captchaOptions } = require(__dirname+'/../configs/main.js')
meta, enableWebring, captchaOptions } = require(__dirname+'/../config.js').get
, { outputFile } = require('fs-extra')
, formatSize = require(__dirname+'/files/formatsize.js')
, pug = require('pug')
, path = require('path')
, commit = require(__dirname+'/commit.js')
, uploadDirectory = require(__dirname+'/files/uploadDirectory.js')
, { hcaptcha, google } = require(__dirname+'/../configs/secrets.js')
, redlock = require(__dirname+'/../redlock.js')
, templateDirectory = path.join(__dirname+'/../views/pages/')
, renderLocals = {
@ -27,10 +28,10 @@ const { enableUserBoardCreation, enableUserAccountCreation,
switch (captchaOptions.type) {
case 'google':
renderLocals.googleRecaptchaSiteKey = captchaOptions.google.siteKey;
renderLocals.googleRecaptchaSiteKey = google.siteKey;
break;
case 'hcaptcha':
renderLocals.hcaptchaSitekey = captchaOptions.hcaptcha.siteKey;
renderLocals.hcaptchaSitekey = hcaptcha.siteKey;
break;
case 'grid':
renderLocals.captchaGridSize = captchaOptions.grid.size;

@ -19,7 +19,7 @@ module.exports = {
return data != null;
},
//check length of string or array
//check length of input, for strings or multi-select options
lengthBody: (data, minlength=0, maxlength=Infinity) => {
return data && (data.length < minlength || data.length > maxlength);
},
@ -48,8 +48,8 @@ module.exports = {
checkSchema: async (schema, permLevel) => {
const errors = [];
//filter check if my perm level is lower than the requirement. e.g. bypass filters checks
const filteredSchema = schema.filter(c => c.permLevel > permLevel);
for (check of filteredSchema) {
const filteredSchema = schema.filter(c => c.permLevel == null || c.permLevel > permLevel);
for (let check of filteredSchema) {
const result = await check.result;
const expected = (check.expected || false);
if (result !== expected) {

@ -4,7 +4,8 @@ const Mongo = require(__dirname+'/../db/db.js')
, timeUtils = require(__dirname+'/timeutils.js')
, uploadDirectory = require(__dirname+'/files/uploadDirectory.js')
, { remove } = require('fs-extra')
, { debugLogs, pruneModlogs, enableWebring, maxRecentNews } = require(__dirname+'/../configs/main.js')
, config = require(__dirname+'/../config.js')
, { debugLogs } = require(__dirname+'/../configs/secrets.js')
, { CustomPages, Stats, Posts, Files, Boards, News, Modlogs } = require(__dirname+'/../db/')
, cache = require(__dirname+'/../redis.js')
, render = require(__dirname+'/render.js')
@ -188,6 +189,7 @@ module.exports = {
const label = `/${options.board._id}/logs.html`;
const start = process.hrtime();
let dates = await Modlogs.getDates(options.board);
const { pruneModlogs } = config.get;
if (pruneModlogs) {
const pruneLogs = [];
const pruneAfter = new Date(Date.now()-timeUtils.DAY*pruneModlogs);
@ -218,6 +220,7 @@ module.exports = {
},
buildHomepage: async () => {
const { maxRecentNews } = config.get;
const label = '/index.html';
const start = process.hrtime();
let [ totalStats, boards, fileStats, recentNews ] = await Promise.all([

@ -1,7 +1,7 @@
'use strict';
const { readdirSync } = require('fs-extra')
, { themes, codeThemes } = require(__dirname+'/../configs/main.js');
, { themes, codeThemes } = require(__dirname+'/../config.js').get;
module.exports = {

@ -2,7 +2,8 @@
const session = require('express-session')
, redisStore = require('connect-redis')(session)
, { cookieSecret, secureCookies } = require(__dirname+'/../configs/main.js')
, { cookieSecret } = require(__dirname+'/../configs/secrets.js')
, config = require(__dirname+'/../config.js')
, { redisClient } = require(__dirname+'/../redis.js')
, production = process.env.NODE_ENV === 'production'
, { DAY } = require(__dirname+'/timeutils.js')
@ -10,6 +11,7 @@ const session = require('express-session')
module.exports = (req, res, next) => {
const { secureCookies } = config.get;
const proto = req.headers['x-forwarded-proto'];
const sessionMiddleware = sessionMiddlewareCache[proto] || (sessionMiddlewareCache[proto] = session({
secret: cookieSecret,

@ -1,12 +1,12 @@
'use strict';
const { globalLimits } = require(__dirname+'/../configs/main.js');
const config = require(__dirname+'/../config.js');
module.exports = async(db, redis) => {
console.log('Adding bumplimit field to boards on posts');
await db.collection('boards').updateMany({}, {
'$set': {
'settings.bumpLimit': globalLimits.bumpLimit.max,
'settings.bumpLimit': getconfig.globalLimits.bumpLimit.max,
}
});
console.log('Cleared boards cache');

@ -18,7 +18,7 @@ const { Posts, Boards, Modlogs } = require(__dirname+'/../../db/')
, getAffectedBoards = require(__dirname+'/../../helpers/affectedboards.js')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
, buildQueue = require(__dirname+'/../../queue.js')
, { postPasswordSecret } = require(__dirname+'/../../configs/main.js')
, { postPasswordSecret } = require(__dirname+'/../../configs/secrets.js')
, threadRegex = /\/[a-z0-9]+\/(?:manage\/)?thread\/(\d+)\.html/i
, { createHash, timingSafeEqual } = require('crypto');

@ -5,10 +5,12 @@ const { Bans, Modlogs } = require(__dirname+'/../../db/')
, hashIp = require(__dirname+'/../../helpers/dynamic.js')
, buildQueue = require(__dirname+'/../../queue.js')
, { isIP } = require('net')
, { defaultBanDuration } = require(__dirname+'/../../configs/main.js');
, config = require(__dirname+'/../../config.js');
module.exports = async (req, res, redirect) => {
const { defaultBanDuration } = config.get;
const actionDate = new Date();
const banPromise = Bans.insertOne({

@ -1,10 +1,11 @@
'use strict';
const { Bans } = require(__dirname+'/../../db/')
, { defaultBanDuration } = require(__dirname+'/../../configs/main.js');
, config = require(__dirname+'/../../config.js');
module.exports = async (req, res, next) => {
const { defaultBanDuration } = config.get;
const banDate = new Date();
const banExpiry = new Date(banDate.getTime() + (req.body.ban_duration || defaultBanDuration)); //uses config default if missing or malformed
const banReason = req.body.ban_reason || req.body.log_message || 'No reason specified';

@ -2,11 +2,12 @@
const { Bypass } = require(__dirname+'/../../db/')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
, { secureCookies, blockBypass } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, production = process.env.NODE_ENV === 'production';
module.exports = async (req, res, next) => {
const { secureCookies, blockBypass } = config.get;
const bypass = await Bypass.getBypass();
const bypassId = bypass.insertedId;
res.locals.blockBypass = bypass.ops[0];

@ -2,7 +2,7 @@
const { Boards, Posts, Accounts } = require(__dirname+'/../../db/')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
, { globalLimits } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, uploadDirectory = require(__dirname+'/../../helpers/files/uploadDirectory.js')
, buildQueue = require(__dirname+'/../../queue.js')
, { remove } = require('fs-extra')
@ -15,6 +15,8 @@ const { Boards, Posts, Accounts } = require(__dirname+'/../../db/')
module.exports = async (req, res, next) => {
const { globalLimits } = config.get;
//oldsettings before changes
const oldSettings = res.locals.board.settings;

@ -5,14 +5,14 @@ const { Boards, Posts, Accounts } = require(__dirname+'/../../db/')
, uploadDirectory = require(__dirname+'/../../helpers/files/uploadDirectory.js')
, buildQueue = require(__dirname+'/../../queue.js')
, redis = require(__dirname+'/../../redis.js')
, getConfig = require(__dirname+'/../../getconfig.js')
, config = require(__dirname+'/../../config.js')
, { trimSetting, numberSetting, booleanSetting, arraySetting } = require(__dirname+'/../../helpers/setting.js')
, { remove } = require('fs-extra');
module.exports = async (req, res, next) => {
const promises = [];
const oldSettings = getConfig();
const oldSettings = config.get;
const newSettings = {
...oldSettings,

@ -5,10 +5,12 @@ const { Boards, Accounts } = require(__dirname+'/../../db/')
, uploadDirectory = require(__dirname+'/../../helpers/files/uploadDirectory.js')
, restrictedURIs = new Set(['captcha', 'forms', 'randombanner', 'all'])
, { ensureDir } = require('fs-extra')
, { boardDefaults } = require(__dirname+'/../../configs/main.js');
, config = require(__dirname+'/../../config.js');
module.exports = async (req, res, next) => {
const { boardDefaults } = config.get;
const { name, description } = req.body
, uri = req.body.uri.toLowerCase()
, tags = req.body.tags.split(/\r?\n/).filter(n => n)

@ -6,13 +6,15 @@ const uploadDirectory = require(__dirname+'/../../helpers/files/uploadDirectory.
, { Posts, Files } = require(__dirname+'/../../db/')
, quoteHandler = require(__dirname+'/../../helpers/posting/quotes.js')
, { markdown } = require(__dirname+'/../../helpers/posting/markdown.js')
, { pruneImmediately } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, { func: pruneFiles } = require(__dirname+'/../../schedules/tasks/prune.js')
, sanitize = require('sanitize-html')
, sanitizeOptions = require(__dirname+'/../../helpers/posting/sanitizeoptions.js');
module.exports = async (posts, board, all=false) => {
const { pruneImmediately } = config.get;
//filter to threads
const threads = posts.filter(x => x.thread == null);

@ -1,12 +1,14 @@
'use strict';
const { Files } = require(__dirname+'/../../db/')
, { pruneImmediately } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, { func: pruneFiles } = require(__dirname+'/../../schedules/tasks/prune.js')
, deletePostFiles = require(__dirname+'/../../helpers/files/deletepostfiles.js');
module.exports = async (posts, unlinkOnly) => {
const { pruneImmediately } = config.get;
//get filenames from all the posts
let files = [];
for (let i = 0; i < posts.length; i++) {

@ -6,7 +6,7 @@ const { Posts, Bans, Modlogs } = require(__dirname+'/../../db/')
, { prepareMarkdown } = require(__dirname+'/../../helpers/posting/markdown.js')
, messageHandler = require(__dirname+'/../../helpers/posting/message.js')
, nameHandler = require(__dirname+'/../../helpers/posting/name.js')
, { previewReplies, strictFiltering } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, cache = require(__dirname+'/../../redis.js')
, buildQueue = require(__dirname+'/../../queue.js')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
@ -22,6 +22,7 @@ todo: handle some more situations
- allow for regular users (OP ONLY) and option for staff to disable in board settings
*/
const { previewReplies, strictFiltering } = config.get;
const { board, post } = res.locals;
//filters

@ -25,15 +25,16 @@ const path = require('path')
, timeUtils = require(__dirname+'/../../helpers/timeutils.js')
, deletePosts = require(__dirname+'/deletepost.js')
, spamCheck = require(__dirname+'/../../helpers/checks/spamcheck.js')
, { checkRealMimeTypes, thumbSize, thumbExtension, videoThumbPercentage,
postPasswordSecret, strictFiltering, animatedGifThumbnails,
audioThumbnails } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, buildQueue = require(__dirname+'/../../queue.js')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
, { buildThread } = require(__dirname+'/../../helpers/tasks.js');
module.exports = async (req, res, next) => {
const { checkRealMimeTypes, thumbSize, thumbExtension, videoThumbPercentage,
postPasswordSecret, strictFiltering, animatedGifThumbnails, audioThumbnails } = config.get;
//spam/flood check
const flood = await spamCheck(req, res);
if (flood) {

@ -2,7 +2,7 @@
const path = require('path')
, { remove, pathExists } = require('fs-extra')
, { globalLimits, checkRealMimeTypes } = require(__dirname+'/../../configs/main.js')
, config = require(__dirname+'/../../config.js')
, uploadDirectory = require(__dirname+'/../../helpers/files/uploadDirectory.js')
, moveUpload = require(__dirname+'/../../helpers/files/moveupload.js')
, mimeTypes = require(__dirname+'/../../helpers/files/mimetypes.js')
@ -14,7 +14,8 @@ const path = require('path')
module.exports = async (req, res, next) => {
const redirect = `/${req.params.board}/manage/banners.html`
const { globalLimits, checkRealMimeTypes } = config.get;
const redirect = `/${req.params.board}/manage/banners.html`;
// check all mime types before we try saving anything
for (let i = 0; i < res.locals.numFiles; i++) {

@ -1,6 +1,6 @@
'use strict';
const { enableWebring } = require(__dirname+'/../../configs/main.js')
const config = require(__dirname+'/../../config.js')
, { Boards, Webring } = require(__dirname+'/../../db/')
, cache = require(__dirname+'/../../redis.js')
, { relativeColor, relativeString } = require(__dirname+'/../../helpers/timeutils.js')
@ -9,6 +9,7 @@ const { enableWebring } = require(__dirname+'/../../configs/main.js')
module.exports = async (req, res, next) => {
const { enableWebring } = config.get;
const { page, offset, queryString } = pageQueryConverter(req.query, limit);
const direction = req.query.direction && req.query.direction === 'asc' ? 1 : -1;
const search = (typeof req.query.search === 'string' ? req.query.search : null);

@ -1,14 +1,16 @@
'use strict';
const { Captchas, Ratelimits } = require(__dirname+'/../../db/')
, { secureCookies, rateLimitCost, captchaOptions } = require(__dirname+'/../../configs/main.js')
, generateCaptcha = (captchaOptions.type !== 'google' && captchaOptions.type !== 'hcaptcha')
? require(__dirname+`/../../helpers/captcha/generators/${captchaOptions.type}.js`)
: null
, config = require(__dirname+'/../../config.js')
, production = process.env.NODE_ENV === 'production';
module.exports = async (req, res, next) => {
const { secureCookies, rateLimitCost, captchaOptions } = config.get;
const generateCaptcha = (captchaOptions.type !== 'google' && captchaOptions.type !== 'hcaptcha')
? require(__dirname+`/../../helpers/captcha/generators/${captchaOptions.type}.js`)
: null;
if (!production && req.cookies['captchaid'] != null) {
return res.redirect(`/captcha/${req.cookies['captchaid']}.jpg`);
}

@ -2,10 +2,11 @@
const { Posts, Boards } = require(__dirname+'/../../db/')
, cache = require(__dirname+'/../../redis.js')
, { overboardLimit } = require(__dirname+'/../../configs/main.js');
, config = require(__dirname+'/../../config.js');
module.exports = async (req, res, next) => {
const { overboardLimit } = config.get;
let threads = (await cache.get('overboard')) || [];
if (!threads || threads.length === 0) {
try {

@ -2,10 +2,12 @@
const { Posts, Boards } = require(__dirname+'/../../db/')
, cache = require(__dirname+'/../../redis.js')
, { overboardCatalogLimit } = require(__dirname+'/../../configs/main.js');
, config = require(__dirname+'/../../config.js');
module.exports = async (req, res, next) => {
const { overboardCatalogLimit } = config.get;
let threads = (await cache.get('catalog')) || [];
if (!threads || threads.length === 0) {
try {

@ -1,7 +1,7 @@
'use strict';
const Queue = require('bull')
, { redis } = require(__dirname+'/configs/main.js')
, { redis } = require(__dirname+'/configs/secrets.js')
, taskQueue = new Queue('task', { redis });
module.exports = {

@ -22,22 +22,20 @@ module.exports = {
},
addCallback: (channel, cb) => {
if (messageCallbacks.length == 0) {
if (messageCallbacks[channel].length === 0) {
subscriber.subscribe('config', (err, count) => {
if (err) {
return console.error(err);
}
console.log(`Redis subscribed to ${count} channels`);
});
subscriber.on("message", (channel, message) => {
console.log(`Redis subscriber message from channel ${channel}`);
const data = JSON.parse(message);
messageCallbacks[channel].forEach(cb => {
cb(data);
})
});
});
}
messageCallbacks[channel].push();
messageCallbacks[channel].push(cb);
},
//get a value with key

@ -1,6 +1,6 @@
'use strict';
const getConfig = require(__dirname+'/../getconfig.js');
const config = require(__dirname+'/../config.js');
module.exports = class Schedule {
@ -31,7 +31,7 @@ module.exports = class Schedule {
//check config and either start or stop
update () {
if (!this.condition || getConfig()[this.condition]) {
if (!this.condition || config.get[this.condition]) {
this.start();
} else {
this.stop();

@ -4,12 +4,14 @@ process
.on('uncaughtException', console.error)
.on('unhandledRejection', console.error);
const Mongo = require(__dirname+'/../db/db.js');
const Mongo = require(__dirname+'/../db/db.js')
, config = require(__dirname+'/../config.js');
(async () => {
await Mongo.connect();
await Mongo.checkVersion();
await config.load();
//start all the scheduled tasks
require(__dirname+'/tasks/index.js');

@ -3,14 +3,13 @@
const Mongo = require(__dirname+'/../../db/db.js')
, { Posts } = require(__dirname+'/../../db/')
, { createHash, randomBytes } = require('crypto')
, { pruneIps } = require(__dirname+'/../../configs/main.js')
, timeUtils = require(__dirname+'/../../helpers/timeutils.js');
module.exports = {
func: async (days) => {
const beforeDate = new Date();
beforeDate.setDate(beforeDate.getDate() - days);
beforeDate.setDate(beforeDate.getDate() - (days || getconfig.pruneIps));
const beforeDateMongoId = Mongo.ObjectId.createFromTime(Math.floor(beforeDate.getTime()/1000));
const tempIpHashSecret = randomBytes(20).toString('base64');
const bulkWrites = [];

@ -1,7 +1,7 @@
'use strict';
const Files = require(__dirname+'/../../db/files.js')
, { debugLogs } = require(__dirname+'/../../configs/main.js')
, { debugLogs } = require(__dirname+'/../../configs/secrets.js')
, { remove } = require('fs-extra')
, uploadDirectory = require(__dirname+'/../../helpers/files/uploadDirectory.js')
, timeUtils = require(__dirname+'/../../helpers/timeutils.js');

@ -1,8 +1,9 @@
'use strict';
const fetch = require('node-fetch')
, { debugLogs, meta } = require(__dirname+'/../../configs/main.js')
, { debugLogs } = require(__dirname+'/../../configs/secrets.js')
, { logo, following, blacklist, proxy } = require(__dirname+'/../../configs/webring.json')
, config = require(__dirname+'/../../config.js')
, Mongo = require(__dirname+'/../../db/db.js')
, { Boards, Webring } = require(__dirname+'/../../db/')
, { outputFile } = require('fs-extra')
@ -15,6 +16,9 @@ const fetch = require('node-fetch')
module.exports = {
func: async () => {
return
const { meta } = config.get;
const label = `updating webring`;
const start = process.hrtime();

@ -4,7 +4,7 @@ process
.on('uncaughtException', console.error)
.on('unhandledRejection', console.error);
const getConfig = require(__dirname+'/getconfig.js')
const config = require(__dirname+'/config.js')
, express = require('express')
, path = require('path')
, app = express()
@ -29,6 +29,7 @@ const getConfig = require(__dirname+'/getconfig.js')
debugLogs && console.log('CONNECTING TO MONGODB');
await Mongo.connect();
await Mongo.checkVersion();
await config.load();
// connect to redis
debugLogs && console.log('CONNECTING TO REDIS');
@ -61,8 +62,8 @@ const getConfig = require(__dirname+'/getconfig.js')
const loadAppLocals = () => {
const { cacheTemplates, boardDefaults, globalLimits, captchaOptions,
enableUserBoardCreation, enableUserAccountCreation, cookieSecret,
debugLogs, ipHashPermLevel, meta, enableWebring } = getConfig();
enableUserBoardCreation, enableUserAccountCreation,
debugLogs, ipHashPermLevel, meta, enableWebring } = config.get;
//cache loaded templates
app.cache = {};
app[cacheTemplates === true ? 'enable' : 'disable']('view cache');

@ -1,6 +1,6 @@
'use strict';
const { redis: redisConfig, ipHashPermLevel } = require(__dirname+'/configs/main.js')
const { redis: redisConfig } = require(__dirname+'/configs/secrets.js')
, hasPerms = require(__dirname+'/helpers/checks/hasperms.js')
, roomRegex = /^(?<roomBoard>[a-z0-9]+)-(?<roomName>[a-z0-9-]+)$/i;
@ -51,7 +51,7 @@ module.exports = {
}
if (room.endsWith('-raw')) {
//if its a -raw room, prioritise ipHashPermLevel
requiredAuth = Math.min(requiredAuth, ipHashPermLevel);
requiredAuth = Math.min(requiredAuth, getconfig.ipHashPermLevel);
}
if (authLevel <= requiredAuth) {
//user has perms to join

@ -4,14 +4,14 @@ process
.on('uncaughtException', console.error)
.on('unhandledRejection', console.error);
const { debugLogs } = require(__dirname+'/getconfig.js')()
, Mongo = require(__dirname+'/db/db.js');
const Mongo = require(__dirname+'/db/db.js')
, config = require(__dirname+'/config.js');
(async () => {
debugLogs && console.log('CONNECTING TO MONGODB');
await Mongo.connect();
await Mongo.checkVersion();
await config.load();
const tasks = require(__dirname+'/helpers/tasks.js')
, { queue } = require(__dirname+'/queue.js')

Loading…
Cancel
Save