|
|
|
'use strict';
|
|
|
|
|
|
|
|
const { Boards, Posts } = require(__dirname+'/../../db/')
|
|
|
|
, { debugLogs } = require(__dirname+'/../../configs/secrets.js')
|
|
|
|
, dynamicResponse = require(__dirname+'/../../lib/misc/dynamic.js')
|
|
|
|
, config = require(__dirname+'/../../lib/misc/config.js')
|
|
|
|
, uploadDirectory = require(__dirname+'/../../lib/file/uploaddirectory.js')
|
|
|
|
, buildQueue = require(__dirname+'/../../lib/build/queue.js')
|
|
|
|
, { remove } = require('fs-extra')
|
|
|
|
, deletePosts = require(__dirname+'/deletepost.js')
|
|
|
|
, { prepareMarkdown } = require(__dirname+'/../../lib/post/markdown/markdown.js')
|
|
|
|
, messageHandler = require(__dirname+'/../../lib/post/message.js')
|
|
|
|
, { countryCodes } = require(__dirname+'/../../lib/misc/countries.js')
|
|
|
|
, { trimSetting, numberSetting, booleanSetting, arraySetting } = require(__dirname+'/../../lib/input/setting.js')
|
|
|
|
, { compareSettings } = require(__dirname+'/../../lib/input/settingsdiff.js')
|
|
|
|
, validCountryCodes = new Set(countryCodes)
|
|
|
|
, settingChangeEntries = Object.entries({
|
|
|
|
'userPostDelete': ['board', 'catalog', 'threads'],
|
|
|
|
'userPostSpoiler': ['board', 'catalog', 'threads'],
|
|
|
|
'userPostUnlink': ['board', 'catalog', 'threads'],
|
|
|
|
'replyLimit': ['board', 'threads'],
|
|
|
|
'archiveLinks': ['board', 'threads'],
|
|
|
|
'reverseImageSearchLinks': ['board', 'threads'],
|
|
|
|
'name': ['board', 'threads', 'catalog', 'other'],
|
|
|
|
'description': ['board', 'threads', 'catalog', 'other'],
|
|
|
|
'theme': ['board', 'threads', 'catalog', 'other'],
|
|
|
|
'codetheme': ['board', 'threads', 'catalog', 'other'],
|
|
|
|
'announcement.raw': ['board', 'threads', 'catalog', 'other'],
|
|
|
|
'customCss': ['board', 'threads', 'catalog', 'other'],
|
|
|
|
'enableTegaki': ['board', 'threads', 'catalog'],
|
|
|
|
'hideBanners': ['board', 'threads', 'catalog'],
|
|
|
|
});
|
|
|
|
|
|
|
|
module.exports = async (req, res) => {
|
|
|
|
|
|
|
|
const { globalLimits } = config.get;
|
|
|
|
|
|
|
|
//oldsettings before changes
|
|
|
|
const oldSettings = res.locals.board.settings;
|
|
|
|
|
|
|
|
//array of promises we might need
|
|
|
|
const promises = [];
|
|
|
|
|
|
|
|
const announcement = req.body.announcement === null ? null : prepareMarkdown(req.body.announcement, false);
|
|
|
|
let markdownAnnouncement = oldSettings.announcement.markdown;
|
|
|
|
if (announcement !== oldSettings.announcement.raw) {
|
|
|
|
({ message: markdownAnnouncement } = await messageHandler(announcement, req.params.board, null, res.locals.permissions));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (req.body.countries) {
|
|
|
|
req.body.countries = [...new Set(req.body.countries)] //prevents submitting multiple of same code, not like it matters, but meh
|
|
|
|
.filter(code => validCountryCodes.has(code))
|
|
|
|
.slice(0,countryCodes.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
const newSettings = {
|
|
|
|
'name': trimSetting(req.body.name, oldSettings.name),
|
|
|
|
'description': trimSetting(req.body.description, oldSettings.description),
|
|
|
|
'defaultName': trimSetting(req.body.default_name, oldSettings.defaultName),
|
|
|
|
'theme': req.body.theme || oldSettings.theme,
|
|
|
|
'codeTheme': req.body.code_theme || oldSettings.codeTheme,
|
|
|
|
'sfw': booleanSetting(req.body.sfw),
|
|
|
|
'archiveLinks': booleanSetting(req.body.archive_links),
|
|
|
|
'reverseImageSearchLinks': booleanSetting(req.body.reverse_image_search_links),
|
|
|
|
'unlistedLocal': booleanSetting(req.body.unlisted_local),
|
|
|
|
'unlistedWebring': booleanSetting(req.body.unlisted_webring),
|
|
|
|
'early404': booleanSetting(req.body.early404),
|
|
|
|
'ids': booleanSetting(req.body.ids),
|
|
|
|
'geoFlags': booleanSetting(req.body.geo_flags),
|
|
|
|
'customFlags': booleanSetting(req.body.custom_flags),
|
|
|
|
'enableTegaki': booleanSetting(req.body.enable_tegaki),
|
|
|
|
'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),
|
|
|
|
'forceThreadMessage': booleanSetting(req.body.force_thread_message),
|
|
|
|
'forceThreadFile': booleanSetting(req.body.force_thread_file),
|
|
|
|
'forceReplyMessage': booleanSetting(req.body.force_reply_message),
|
|
|
|
'forceReplyFile': booleanSetting(req.body.force_reply_file),
|
|
|
|
'forceThreadSubject': booleanSetting(req.body.force_thread_subject),
|
|
|
|
'disableReplySubject': booleanSetting(req.body.disable_reply_subject),
|
|
|
|
'hideBanners': booleanSetting(req.body.hide_banners),
|
|
|
|
'captchaMode': numberSetting(req.body.captcha_mode, oldSettings.captchaMode),
|
|
|
|
'tphTrigger': numberSetting(req.body.tph_trigger, oldSettings.tphTrigger),
|
|
|
|
'tphTriggerAction': numberSetting(req.body.tph_trigger_action, oldSettings.tphTriggerAction),
|
|
|
|
'pphTrigger': numberSetting(req.body.pph_trigger, oldSettings.pphTrigger),
|
|
|
|
'pphTriggerAction': numberSetting(req.body.pph_trigger_action, oldSettings.pphTriggerAction),
|
|
|
|
'captchaReset': numberSetting(req.body.captcha_reset, oldSettings.captchaReset),
|
|
|
|
'lockReset': numberSetting(req.body.lock_reset, oldSettings.lockReset),
|
|
|
|
'threadLimit': numberSetting(req.body.thread_limit, oldSettings.threadLimit),
|
|
|
|
'replyLimit': numberSetting(req.body.reply_limit, oldSettings.replyLimit),
|
|
|
|
'bumpLimit': numberSetting(req.body.bump_limit, oldSettings.bumpLimit),
|
|
|
|
'maxFiles': numberSetting(req.body.max_files, oldSettings.maxFiles),
|
|
|
|
'minThreadMessageLength': numberSetting(req.body.min_thread_message_length, oldSettings.minThreadMessageLength),
|
|
|
|
'minReplyMessageLength': numberSetting(req.body.min_reply_message_length, oldSettings.minReplyMessageLength),
|
|
|
|
'maxThreadMessageLength': numberSetting(req.body.max_thread_message_length, oldSettings.maxThreadMessageLength),
|
|
|
|
'maxReplyMessageLength': numberSetting(req.body.max_reply_message_length, oldSettings.maxReplyMessageLength),
|
|
|
|
'lockMode': numberSetting(req.body.lock_mode, oldSettings.lockMode),
|
|
|
|
'messageR9KMode': numberSetting(req.body.message_r9k_mode, oldSettings.messageR9KMode),
|
|
|
|
'fileR9KMode': numberSetting(req.body.file_r9k_mode, oldSettings.fileR9KMode),
|
|
|
|
'filterMode': numberSetting(req.body.filter_mode, oldSettings.filterMode),
|
|
|
|
'filterBanDuration': numberSetting(req.body.ban_duration, oldSettings.filterBanDuration),
|
|
|
|
'deleteProtectionAge': numberSetting(req.body.delete_protection_age, oldSettings.deleteProtectionAge),
|
|
|
|
'deleteProtectionCount': numberSetting(req.body.delete_protection_count, oldSettings.deleteProtectionCount),
|
|
|
|
'filters': arraySetting(req.body.filters, oldSettings.filters, 50),
|
|
|
|
'blockedCountries': req.body.countries || [],
|
|
|
|
'disableAnonymizerFilePosting': booleanSetting(req.body.disable_anonymizer_file_posting),
|
|
|
|
'strictFiltering': booleanSetting(req.body.strict_filtering),
|
|
|
|
'customCss': globalLimits.customCss.enabled ? (req.body.custom_css !== null ? req.body.custom_css : oldSettings.customCss) : null,
|
|
|
|
'announcement': {
|
|
|
|
'raw': announcement !== null ? announcement : oldSettings.announcement.raw,
|
|
|
|
'markdown': req.body.announcement !== null ? markdownAnnouncement : oldSettings.announcement.markdown,
|
|
|
|
},
|
|
|
|
'allowedFileTypes': {
|
|
|
|
'animatedImage': booleanSetting(req.body.files_allow_animated_image),
|
|
|
|
'image': booleanSetting(req.body.files_allow_image),
|
|
|
|
'video': booleanSetting(req.body.files_allow_video),
|
|
|
|
'audio': booleanSetting(req.body.files_allow_audio),
|
|
|
|
'other': booleanSetting(req.body.files_allow_other),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
//settings changed in the db
|
|
|
|
await Boards.updateOne(req.params.board, {
|
|
|
|
'$set': {
|
|
|
|
'settings': newSettings,
|
|
|
|
'tags': arraySetting(req.body.tags, res.locals.board.tags, 10),
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
//update this in locals incase is used in later parts
|
|
|
|
res.locals.board.settings = newSettings;
|
|
|
|
|
|
|
|
//pages in new vs old settings
|
|
|
|
const oldMaxPage = Math.ceil(oldSettings.threadLimit/10);
|
|
|
|
const newMaxPage = Math.ceil(newSettings.threadLimit/10);
|
|
|
|
|
|
|
|
const rebuildTasks = compareSettings(settingChangeEntries, oldSettings, newSettings, 4);
|
|
|
|
|
|
|
|
if (rebuildTasks.size < 4) {
|
|
|
|
//after that is stuff not direct equality comparisons calcluated by compareSettings()
|
|
|
|
if (newSettings.captchaMode > oldSettings.captchaMode) {
|
|
|
|
if (oldSettings.captchaMode === 0) {
|
|
|
|
rebuildTasks.add('board')
|
|
|
|
.add('catalog');
|
|
|
|
}
|
|
|
|
if (newSettings.captchaMode === 2) {
|
|
|
|
rebuildTasks.add('threads');
|
|
|
|
}
|
|
|
|
} else if (newSettings.captchaMode < oldSettings.captchaMode) {
|
|
|
|
if (oldSettings.captchaMode === 2) {
|
|
|
|
rebuildTasks.add('threads');
|
|
|
|
}
|
|
|
|
if (newSettings.captchaMode === 0) {
|
|
|
|
rebuildTasks.add('board')
|
|
|
|
.add('catalog');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//do rebuilding and pruning if max number of pages is changed and any threads are pruned
|
|
|
|
if (newMaxPage < oldMaxPage) {
|
|
|
|
//prune old threads
|
|
|
|
const prunedThreads = await Posts.pruneThreads(res.locals.board);
|
|
|
|
if (prunedThreads.length > 0) {
|
|
|
|
await deletePosts(prunedThreads, req.params.board);
|
|
|
|
//remove board page html/json for pages > newMaxPage
|
|
|
|
for (let i = newMaxPage+1; i <= oldMaxPage; i++) {
|
|
|
|
promises.push(remove(`${uploadDirectory}/html/${req.params.board}/${i}.html`));
|
|
|
|
promises.push(remove(`${uploadDirectory}/json/${req.params.board}/${i}.json`));
|
|
|
|
}
|
|
|
|
//rebuild all board pages for page nav numbers, and catalog
|
|
|
|
rebuildTasks.add('board')
|
|
|
|
.add('catalog');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rebuildTasks.has('threads')) {
|
|
|
|
promises.push(remove(`${uploadDirectory}/html/${req.params.board}/thread/`));
|
|
|
|
}
|
|
|
|
if (rebuildTasks.has('board')) {
|
|
|
|
buildQueue.push({
|
|
|
|
'task': 'buildBoardMultiple',
|
|
|
|
'options': {
|
|
|
|
'board': res.locals.board,
|
|
|
|
'startpage': 1,
|
|
|
|
'endpage': newMaxPage
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (rebuildTasks.has('catalog')) {
|
|
|
|
buildQueue.push({
|
|
|
|
'task': 'buildCatalog',
|
|
|
|
'options': {
|
|
|
|
'board': res.locals.board,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (rebuildTasks.has('other')) {
|
|
|
|
promises.push(remove(`${uploadDirectory}/html/${req.params.board}/logs/`));
|
|
|
|
promises.push(remove(`${uploadDirectory}/html/${req.params.board}/custompage/`));
|
|
|
|
buildQueue.push({
|
|
|
|
'task': 'buildModLogList',
|
|
|
|
'options': {
|
|
|
|
'board': res.locals.board,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
buildQueue.push({
|
|
|
|
'task': 'buildBanners',
|
|
|
|
'options': {
|
|
|
|
'board': res.locals.board,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
//finish the promises in parallel e.g. removing files
|
|
|
|
if (promises.length > 0) {
|
|
|
|
await Promise.all(promises);
|
|
|
|
}
|
|
|
|
|
|
|
|
debugLogs && console.log(req.params.board, 'board settings changed');
|
|
|
|
|
|
|
|
return dynamicResponse(req, res, 200, 'message', {
|
|
|
|
'title': 'Success',
|
|
|
|
'message': 'Updated settings.',
|
|
|
|
'redirect': `/${req.params.board}/manage/settings.html`
|
|
|
|
});
|
|
|
|
|
|
|
|
};
|