option for boards to enforce unique files board wide or per thread

merge-requests/208/head
Thomas Lynch 4 years ago
parent 649a3d2e56
commit f9e15e23e8
  1. 1
      configs/main.js.example
  2. 21
      db/posts.js
  3. 2
      helpers/paramconverter.js
  4. 1
      migrations/index.js
  5. 12
      migrations/migration-0.0.13.js
  6. 1
      models/forms/changeboardsettings.js
  7. 18
      models/forms/makepost.js
  8. 2
      package.json
  9. 6
      views/pages/managesettings.pug

@ -290,6 +290,7 @@ module.exports = {
codeTheme: 'ir-black',
sfw: false, //safe for work board
lockMode: 0, //board lock mode
fileR9KMode: 0, //r9k for files, 0=off, 1=per thread, 2=whole board
unlistedLocal: false, //board hidden from on-site board list and frontpage
unlistedWebring: false, //board hidden from webring
captchaMode: 0, //0=disabled, 1=for threads, 2=for all posts

@ -296,6 +296,27 @@ module.exports = {
},
checkExistingFiles: async (board, thread = null, hashes) => {
const query = {
'board': board,
'files.hash': {
'$in': hashes
}
}
if (thread !== null) {
query['$or'] = [
{ 'thread': thread },
{ 'postId': thread },
]
}
const postWithExistingFiles = await db.findOne(query, {
'projection': {
'files.hash': 1,
}
});
return postWithExistingFiles;
},
allBoardPosts: (board) => {
return db.find({
'board': board

@ -5,7 +5,7 @@ const { ObjectId } = require(__dirname+'/../db/db.js')
'checkedreports', 'checkedbans', 'checkedbanners', 'checkedaccounts', 'countries']) //only these should be arrays, since express bodyparser can output arrays
, trimFields = ['tags', 'uri', 'moderators', 'filters', 'announcement', 'description', 'message',
'name', 'subject', 'email', 'postpassword', 'password', 'default_name', 'report_reason', 'ban_reason', 'log_message', 'custom_css'] //trim if we dont want filed with whitespace
, numberFields = ['filter_mode', 'lock_mode', 'captcha_mode', 'tph_trigger', 'pph_trigger', 'trigger_action', 'bump_limit', 'reply_limit', 'move_to_thread',, 'postId',
, numberFields = ['filter_mode', 'lock_mode', 'file_r9k_mode', 'captcha_mode', 'tph_trigger', 'pph_trigger', 'trigger_action', 'bump_limit', 'reply_limit', 'move_to_thread',, 'postId',
'max_files', 'thread_limit', 'thread', 'max_thread_message_length', 'max_reply_message_length', 'min_thread_message_length', 'min_reply_message_length', 'auth_level'] //convert these to numbers before they hit our routes
, banDurationRegex = /^(?<YEAR>[\d]+y)?(?<MONTH>[\d]+m)?(?<WEEK>[\d]+w)?(?<DAY>[\d]+d)?(?<HOUR>[\d]+h)?$/
, timeUtils = require(__dirname+'/timeutils.js')

@ -13,4 +13,5 @@ module.exports = {
'0.0.10': require(__dirname+'/migration-0.0.10.js'), //add links to modlog for new logs
'0.0.11': require(__dirname+'/migration-0.0.11.js'), //rename captcha "text" field to "answer" since we support multiple captcha types now
'0.0.12': require(__dirname+'/migration-0.0.12.js'), //yotsuba b -> yotsuba-b
'0.0.13': require(__dirname+'/migration-0.0.13.js'), //add r9k mode
}

@ -0,0 +1,12 @@
'use strict';
module.exports = async(db, redis) => {
console.log('Adding file r9k setting to boards db');
await db.collection('boards').updateMany({}, {
'$set': {
'settings.fileR9KMode': 0,
}
});
console.log('Cleared boards cache');
await redis.deletePattern('board:*');
};

@ -107,6 +107,7 @@ module.exports = async (req, res, next) => {
'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),
'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),
'tags': arraySetting(req.body.tags, oldSettings.tags, 10),

@ -50,7 +50,7 @@ module.exports = async (req, res, next) => {
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;
captchaMode, lockMode, allowedFileTypes, flags, fileR9KMode } = res.locals.board.settings;
if (res.locals.permLevel >= 4
&& res.locals.country
&& blockedCountries.includes(res.locals.country.code)) {
@ -161,6 +161,22 @@ module.exports = async (req, res, next) => {
let files = [];
// if we got a file
if (res.locals.numFiles > 0) {
if ((req.body.thread && fileR9KMode === 1) || fileR9KMode === 2) {
const filesHashes = req.files.file.map(f => f.sha256);
const postWithExistingFiles = await Posts.checkExistingFiles(res.locals.board._id, (fileR9KMode === 2 ? null : req.body.thread), filesHashes);
if (postWithExistingFiles != null) {
await deleteTempFiles(req).catch(e => console.error);
const conflictingFiles = req.files.file
.filter(f => postWithExistingFiles.files.some(fx => fx.hash === f.sha256))
.map(f => f.name)
.join(', ');
return dynamicResponse(req, res, 409, 'message', {
'title': 'Conflict',
'message': `Uploaded files must be unique ${fileR9KMode === 1 ? 'in this thread' : 'on this board'}.\nAt least the following file${conflictingFiles.length > 1 ? 's are': ' is'} not unique: ${conflictingFiles}`,
'redirect': redirect
});
}
}
// check all mime types before we try saving anything
for (let i = 0; i < res.locals.numFiles; i++) {
if (!mimeTypes.allowed(req.files.file[i].mimetype, allowedFileTypes)) {

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

@ -161,6 +161,12 @@ block content
option(value='0', selected=board.settings.lockMode === 0) Unlocked
option(value='1', selected=board.settings.lockMode === 1) Lock thread creation
option(value='2', selected=board.settings.lockMode === 2) Lock board
.row
.label Unique Files
select(name='file_r9k_mode')
option(value='0', selected=board.settings.fileR9KMode === 0) Off
option(value='1', selected=board.settings.fileR9KMode === 1) Per Thread
option(value='2', selected=board.settings.fileR9KMode === 2) Board Wide
.row
.label Unlist locally
label.postform-style.ph-5

Loading…
Cancel
Save