From e823cad14ed15694e81ef0f38f57546184b58549 Mon Sep 17 00:00:00 2001 From: fatchan Date: Thu, 11 Apr 2019 16:25:40 +0000 Subject: [PATCH 01/20] scuffed bans. todo: custom duration, expiry in DB, show banned post --- controllers/forms.js | 193 ++++++++++++++++++++------------- db-models/bans.js | 29 +++++ helpers/bancheck.js | 22 ++++ models/forms/ban-poster.js | 52 +++++++++ models/forms/delete-post.js | 49 ++++----- models/forms/dismiss-report.js | 28 ++--- models/forms/edit-post.js | 26 ++--- models/forms/make-post.js | 2 +- models/forms/report-post.js | 15 +-- models/forms/spoiler-post.js | 62 +++++------ views/pages/manage.pug | 11 +- views/pages/message.pug | 4 + wipe.js | 3 + 13 files changed, 308 insertions(+), 188 deletions(-) create mode 100644 db-models/bans.js create mode 100644 helpers/bancheck.js create mode 100644 models/forms/ban-poster.js diff --git a/controllers/forms.js b/controllers/forms.js index 677d87bf..68ef34dc 100644 --- a/controllers/forms.js +++ b/controllers/forms.js @@ -5,92 +5,95 @@ const express = require('express') , Boards = require(__dirname+'/../db-models/boards.js') , Posts = require(__dirname+'/../db-models/posts.js') , Trips = require(__dirname+'/../db-models/trips.js') + , Bans = require(__dirname+'/../db-models/bans.js') + , banPoster = require(__dirname+'/../models/forms/ban-poster.js') , makePost = require(__dirname+'/../models/forms/make-post.js') , deletePosts = require(__dirname+'/../models/forms/delete-post.js') , spoilerPosts = require(__dirname+'/../models/forms/spoiler-post.js') , reportPosts = require(__dirname+'/../models/forms/report-post.js') , dismissReports = require(__dirname+'/../models/forms/dismiss-report.js') , loginAccount = require(__dirname+'/../models/forms/login.js') - , registerAccount = require(__dirname+'/../models/forms/register.js') - , numberConverter = require(__dirname+'/../helpers/number-converter.js'); + , registerAccount = require(__dirname+'/../models/forms/register.js') + , numberConverter = require(__dirname+'/../helpers/number-converter.js') + , banCheck = require(__dirname+'/../helpers/bancheck.js'); // login to account router.post('/login', (req, res, next) => { - const errors = []; - - //check exist - if (!req.body.username || req.body.username.length <= 0) { - errors.push('Missing username'); - } - if (!req.body.password || req.body.password.length <= 0) { - errors.push('Missing password'); - } - - //check too long - if (req.body.username && req.body.username.length > 50) { - errors.push('Username must be 50 characters or less'); - } - if (req.body.password && req.body.password.length > 100) { - errors.push('Password must be 100 characters or less'); - } - - if (errors.length > 0) { - return res.status(400).render('message', { - 'title': 'Bad request', - 'errors': errors, - 'redirect': '/login' - }) - } - - loginAccount(req, res); + const errors = []; + + //check exist + if (!req.body.username || req.body.username.length <= 0) { + errors.push('Missing username'); + } + if (!req.body.password || req.body.password.length <= 0) { + errors.push('Missing password'); + } + + //check too long + if (req.body.username && req.body.username.length > 50) { + errors.push('Username must be 50 characters or less'); + } + if (req.body.password && req.body.password.length > 100) { + errors.push('Password must be 100 characters or less'); + } + + if (errors.length > 0) { + return res.status(400).render('message', { + 'title': 'Bad request', + 'errors': errors, + 'redirect': '/login' + }) + } + + loginAccount(req, res); }); //register account router.post('/register', (req, res, next) => { - const errors = []; - - //check exist - if (!req.body.username || req.body.username.length <= 0) { - errors.push('Missing username'); - } - if (!req.body.password || req.body.password.length <= 0) { - errors.push('Missing password'); - } - if (!req.body.passwordconfirm || req.body.passwordconfirm.length <= 0) { - errors.push('Missing password confirmation'); - } - - //check too long - if (req.body.username && req.body.username.length > 50) { - errors.push('Username must be 50 characters or less'); - } - if (req.body.password && req.body.password.length > 100) { - errors.push('Password must be 100 characters or less'); - } - if (req.body.passwordconfirm && req.body.passwordconfirm.length > 100) { - errors.push('Password confirmation must be 100 characters or less'); - } - if (req.body.password != req.body.passwordconfirm) { - errors.push('Password and password confirmation must match'); - } - - if (errors.length > 0) { - return res.status(400).render('message', { - 'title': 'Bad request', - 'errors': errors, - 'redirect': '/register' - }) - } - - registerAccount(req, res); + const errors = []; + + //check exist + if (!req.body.username || req.body.username.length <= 0) { + errors.push('Missing username'); + } + if (!req.body.password || req.body.password.length <= 0) { + errors.push('Missing password'); + } + if (!req.body.passwordconfirm || req.body.passwordconfirm.length <= 0) { + errors.push('Missing password confirmation'); + } + + //check too long + if (req.body.username && req.body.username.length > 50) { + errors.push('Username must be 50 characters or less'); + } + if (req.body.password && req.body.password.length > 100) { + errors.push('Password must be 100 characters or less'); + } + if (req.body.passwordconfirm && req.body.passwordconfirm.length > 100) { + errors.push('Password confirmation must be 100 characters or less'); + } + if (req.body.password != req.body.passwordconfirm) { + errors.push('Password and password confirmation must match'); + } + + if (errors.length > 0) { + return res.status(400).render('message', { + 'title': 'Bad request', + 'errors': errors, + 'redirect': '/register' + }) + } + + registerAccount(req, res); }); // make new post -router.post('/board/:board', Boards.exists, numberConverter, (req, res, next) => { +router.post('/board/:board', Boards.exists, banCheck, numberConverter, async (req, res, next) => { let numFiles = 0; if (req.files && req.files.file) { @@ -139,7 +142,7 @@ router.post('/board/:board', Boards.exists, numberConverter, (req, res, next) => }); //report, delete, sticky, etc -router.post('/board/:board/posts', Boards.exists, numberConverter, (req, res, next) => { +router.post('/board/:board/posts', Boards.exists, banCheck, numberConverter, async (req, res, next) => { const errors = []; @@ -152,12 +155,20 @@ router.post('/board/:board/posts', Boards.exists, numberConverter, (req, res, ne if (req.body.reason && req.body.reason.length > 50) { errors.push('Report must be 50 characters or less'); } - if (!(req.body.report || req.body.delete || req.body.dismiss || req.body.spoiler)) { + if (!(req.body.report + || req.body.delete + || req.body.dismiss + || req.body.spoiler + || req.body.ban + || req.body.global_ban)) { errors.push('Must select an action') } if (req.body.report && (!req.body.reason || req.body.reason.length === 0)) { errors.push('Reports must have a reason') } + if ((req.body.ban || req.body.global_ban) && (!req.body.reason || req.body.reason.length === 0)) { + errors.push('Bans must have a reason') + } if (errors.length > 0) { return res.status(400).render('message', { @@ -167,16 +178,46 @@ router.post('/board/:board/posts', Boards.exists, numberConverter, (req, res, ne }) } - if (req.body.report) { - reportPosts(req, res); - } else if (req.body.delete) { - deletePosts(req, res); - } else if (req.body.spoiler) { - spoilerPosts(req, res); - } else if (req.body.dismiss) { - dismissReports(req, res); + const messages = []; + try { + + //TODO: maybe fetch the posts first instead of checking multiple times with multiple actions + + //global or board ban + if (req.body.global_ban) { + messages.push((await banPoster(req, res, null))); + } else if (req.body.ban) { + messages.push((await banPoster(req, res, req.params.board))); + } + + // then if not deleting, we can spoiler and report or dismiss reports + if (req.body.delete) { + messages.push((await deletePosts(req, res))); + } else { + if (req.body.spoiler) { + messages.push((await spoilerPosts(req, res))); + } + if (req.body.report) { + messages.push((await reportPosts(req, res))); + } else if (req.body.dismiss) { + messages.push((await dismissReports(req, res))); + } + } + + } catch (err) { + if (err.status) { + return res.status(err.status).render('message', err.message); + } + console.error(err); + return res.status(500).render('error'); } + return res.render('message', { + 'title': 'Success', + 'messages': messages, + 'redirect': `/${req.params.board}` + }); + }); diff --git a/db-models/bans.js b/db-models/bans.js new file mode 100644 index 00000000..3f235e74 --- /dev/null +++ b/db-models/bans.js @@ -0,0 +1,29 @@ +'use strict'; + +const Mongo = require(__dirname+'/../helpers/db.js') + , db = Mongo.client.db('jschan').collection('bans'); + +module.exports = { + + find: (ip, board) => { + return db.find({ + 'ip': ip, + 'board': { + '$in': [board, null] + } + }).toArray(); + }, + + insertOne: (ban) => { + return db.insertOne(ban); + }, + + insertMany: (bans) => { + return db.insertMany(bans); + }, + + deleteAll: () => { + return db.deleteMany({}); + }, + +} diff --git a/helpers/bancheck.js b/helpers/bancheck.js new file mode 100644 index 00000000..1d0f5fb5 --- /dev/null +++ b/helpers/bancheck.js @@ -0,0 +1,22 @@ +'use strict'; + +const Bans = require(__dirname+'/../db-models/bans.js') + , hasPerms = require(__dirname+'/has-perms.js'); + +module.exports = async (req, res, next) => { + + if (!hasPerms(req, res)) { + const ip = req.headers['x-real-ip'] || req.connection.remoteAddress; + const bans = await Bans.find(ip, res.locals.board._id); + if (bans && bans.length > 0) { + //TODO: show posts banned for, expiry, etc + return res.status(403).render('message', { + 'title': 'Forbidden', + 'message': 'You are banned', + 'redirect': '/' + }); + } + } + next(); + +} diff --git a/models/forms/ban-poster.js b/models/forms/ban-poster.js new file mode 100644 index 00000000..c017ca5c --- /dev/null +++ b/models/forms/ban-poster.js @@ -0,0 +1,52 @@ +'use strict'; + +const uploadDirectory = require(__dirname+'/../../helpers/uploadDirectory.js') + , hasPerms = require(__dirname+'/../../helpers/has-perms.js') + , Bans = require(__dirname+'/../../db-models/bans.js') + , Posts = require(__dirname+'/../../db-models/posts.js'); + +module.exports = async (req, res, board) => { + + //if user is not logged in or if logged in but not authed, they cannot ban + if (!hasPerms(req, res)) { + throw { + 'status': 403, + 'message': { + 'title': 'Forbidden', + 'message': 'You do not have permission to issue bans', + 'redirect': `/${req.params.board}` + } + }; + } + + //get all posts that were checked + let posts = await Posts.getPosts(req.params.board, req.body.checked, true); //admin arument true, fetches passwords and salts + + if (!posts || posts.length === 0) { + throw { + 'status': 400, + 'message': { + 'title': 'Bad requests', + 'message': 'No posts found', + 'redirect': `/${req.params.board}` + } + }; + } + + const bans = posts.map(post => { + return { + 'ip': post.ip, + 'board': board, + 'post': post, + 'issuer': req.session.user.username + } + }); + + let bannedIps = 0; + const result = await Bans.insertMany(bans, board); + console.log(result) + bannedIps = result.insertedCount; + + return `Banned ${bannedIps} ips`; + +} diff --git a/models/forms/delete-post.js b/models/forms/delete-post.js index 20d63536..7eb79f8e 100644 --- a/models/forms/delete-post.js +++ b/models/forms/delete-post.js @@ -11,20 +11,17 @@ const path = require('path') module.exports = async (req, res) => { //get all posts that were checked - let posts; - try { - posts = await Posts.getPosts(req.params.board, req.body.checked, true); //admin arument true, fetches passwords and salts - } catch (err) { - console.error(err); - return res.status(500).render('error'); - } + let posts = await Posts.getPosts(req.params.board, req.body.checked, true); //admin arument true, fetches passwords and salts if (!posts || posts.length === 0) { - return res.status(400).render('message', { - 'title': 'Bad requests', - 'message': 'No posts found', - 'redirect': `/${req.params.board}` - }); + throw { + 'status': 400, + 'message': { + 'title': 'Bad request', + 'message': 'No posts found', + 'redirect': `/${req.params.board}` + } + }; } //if user is not logged in OR if lgoged in but not authed, filter the posts by passwords that are not null @@ -37,11 +34,14 @@ module.exports = async (req, res) => { && post.password == req.body.password }); if (posts.length === 0) { - return res.status(403).render('message', { - 'title': 'Forbidden', - 'message': 'Password did not match any selected posts', - 'redirect': `/${req.params.board}` - }); + throw { + 'status': 403, + 'message': { + 'title': 'Forbidden', + 'message': 'Password did not match any selected posts', + 'redirect': `/${req.params.board}` + } + }; } } @@ -60,13 +60,8 @@ module.exports = async (req, res) => { //delete posts from DB let deletedPosts = 0; - try { - const result = await Posts.deleteMany(req.params.board, allPosts.map(x => x.postId)); - deletedPosts = result.deletedCount; - } catch (err) { - console.error(err); - return res.status(500).render('error'); - } + const result = await Posts.deleteMany(req.params.board, allPosts.map(x => x.postId)); + deletedPosts = result.deletedCount; //get filenames from all the posts let fileNames = []; @@ -84,10 +79,6 @@ module.exports = async (req, res) => { })); //hooray! - return res.render('message', { - 'title': 'Success', - 'message': `Deleted ${threadIds.length} threads and ${deletedPosts} posts`, - 'redirect': `/${req.params.board}` - }); + return `Deleted ${threadIds.length} threads and ${deletedPosts} posts` } diff --git a/models/forms/dismiss-report.js b/models/forms/dismiss-report.js index c22a663d..c7d8e46d 100644 --- a/models/forms/dismiss-report.js +++ b/models/forms/dismiss-report.js @@ -6,26 +6,18 @@ const Posts = require(__dirname+'/../../db-models/posts.js') module.exports = async (req, res) => { if (!hasPerms(req, res)) { - return res.status(403).render('message', { - 'title': 'Forbidden', - 'message': `You are not authorised to dismiss reports.`, - 'redirect': `/${req.params.board}` - }); + throw { + 'status': 403, + 'message': { + 'title': 'Forbidden', + 'message': `You are not authorised to dismiss reports.`, + 'redirect': `/${req.params.board}` + } + }; } - try { - //dismiss reports from all checked posts - await Posts.dismissReports(req.params.board, req.body.checked); - } catch (err) { - console.error(err); - return res.status(500).render('error'); - } + await Posts.dismissReports(req.params.board, req.body.checked); - //hooray! - return res.render('message', { - 'title': 'Success', - 'message': `Dismissed report(s) successfully`, - 'redirect': `/${req.params.board}/manage` - }); + return `Dismissed report(s) successfully` } diff --git a/models/forms/edit-post.js b/models/forms/edit-post.js index 6064c249..6c8bad63 100644 --- a/models/forms/edit-post.js +++ b/models/forms/edit-post.js @@ -7,19 +7,17 @@ const uuidv4 = require('uuid/v4') module.exports = async (req, res, numFiles) => { // get the post that we are trying to edit - let post; - try { - post = await Posts.getPost(req.params.board, req.body.id, true); - } catch (err) { - console.error(err); - return res.status(500).render('error'); - } + let post = await Posts.getPost(req.params.board, req.body.id, true); + if (!thread || thread.thread != null) { - return res.status(400).render('message', { - 'title': 'Bad request', - 'message': 'Post does not exist.', - 'redirect': redirect - }); + throw { + 'status': 400, + 'message': { + 'title': 'Bad request', + 'message': 'Post does not exist.', + 'redirect': redirect + } + }; } // sticky, lock, sage, spoiler, etc @@ -27,8 +25,6 @@ module.exports = async (req, res, numFiles) => { //TODO } - const post = await Posts.updateOne(req.params.board, data) - const successRedirect = `/${req.params.board}/thread/${req.body.thread || post.insertedId}`; + return ``; - return res.redirect(successRedirect); } diff --git a/models/forms/make-post.js b/models/forms/make-post.js index 65c0e436..5d5c76fd 100644 --- a/models/forms/make-post.js +++ b/models/forms/make-post.js @@ -178,7 +178,7 @@ module.exports = async (req, res, numFiles) => { return res.status(500).render('error'); } - const successRedirect = `/${req.params.board}/thread/${req.body.thread || postId}`; + const successRedirect = `/${req.params.board}/thread/${req.body.thread || postId}#${postId}`; return res.redirect(successRedirect); } diff --git a/models/forms/report-post.js b/models/forms/report-post.js index 4f454a93..89d012fb 100644 --- a/models/forms/report-post.js +++ b/models/forms/report-post.js @@ -11,19 +11,10 @@ module.exports = async (req, res) => { 'ip': ip } - try { - //push the report to all checked posts - await Posts.reportMany(req.params.board, req.body.checked, report); - } catch (err) { - console.error(err); - return res.status(500).render('error'); - } + //push the report to all checked posts + await Posts.reportMany(req.params.board, req.body.checked, report); //hooray! - return res.render('message', { - 'title': 'Success', - 'message': `Reported post(s) successfully`, - 'redirect': `/${req.params.board}` - }); + return `Reported post(s) successfully` } diff --git a/models/forms/spoiler-post.js b/models/forms/spoiler-post.js index 15847f77..ceababbb 100644 --- a/models/forms/spoiler-post.js +++ b/models/forms/spoiler-post.js @@ -11,20 +11,17 @@ const path = require('path') module.exports = async (req, res) => { //get all posts that were checked - let posts; - try { - posts = await Posts.getPosts(req.params.board, req.body.checked, true); //admin arument true, fetches passwords and salts - } catch (err) { - console.error(err); - return res.status(500).render('error'); - } + let posts = await Posts.getPosts(req.params.board, req.body.checked, true); //admin arument true, fetches passwords and salts if (!posts || posts.length === 0) { - return res.status(400).render('message', { - 'title': 'Bad requests', - 'message': 'No posts found', - 'redirect': `/${req.params.board}` - }); + throw { + 'status': 400, + 'message': { + 'title': 'Bad requests', + 'message': 'No posts found', + 'redirect': `/${req.params.board}` + } + }; } //if user is not logged in OR if lgoged in but not authed, filter the posts by passwords that are not null @@ -37,11 +34,14 @@ module.exports = async (req, res) => { && post.password == req.body.password }); if (posts.length === 0) { - return res.status(403).render('message', { - 'title': 'Forbidden', - 'message': 'Password did not match any selected posts', - 'redirect': `/${req.params.board}` - }); + throw { + 'status': 403, + 'message': { + 'title': 'Forbidden', + 'message': 'Password did not match any selected posts', + 'redirect': `/${req.params.board}` + } + }; } } @@ -51,28 +51,22 @@ module.exports = async (req, res) => { return !post.spoiler }); if (posts.length === 0) { - return res.status(409).render('message', { - 'title': 'Conflict', - 'message': 'Selected posts are already spoilered', - 'redirect': `/${req.params.board}` - }); + throw { + 'status': 409, + 'message': { + 'title': 'Conflict', + 'message': 'Posts already spoilered', + 'redirect': `/${req.params.board}` + } + }; } // spoiler posts let spoileredPosts = 0; - try { - const result = await Posts.spoilerMany(req.params.board, posts.map(x => x.postId)); - spoileredPosts = result.modifiedCount; - } catch (err) { - console.error(err); - return res.status(500).render('error'); - } + const result = await Posts.spoilerMany(req.params.board, posts.map(x => x.postId)); + spoileredPosts = result.modifiedCount; //hooray! - return res.render('message', { - 'title': 'Success', - 'message': `Spoilered ${spoileredPosts} posts`, - 'redirect': `/${req.params.board}` - }); + return `Spoilered ${spoileredPosts} posts` } diff --git a/views/pages/manage.pug b/views/pages/manage.pug index a99461b1..cb6e86e7 100644 --- a/views/pages/manage.pug +++ b/views/pages/manage.pug @@ -2,7 +2,7 @@ extends ../layout.pug include ../mixins/post.pug block head - title Login + title Manage block content include ../includes/boardheader.pug @@ -28,8 +28,13 @@ block content | Spoiler span label - input.post-check(type='checkbox', name='dismiss' value=1) - | Dismiss + input.post-check(type='checkbox', name='ban' value=1) + | Ban + label + input.post-check(type='checkbox', name='global_ban' value=1) + | Global Ban + label + input#report(type='text', name='reason', placeholder='ban reason' autocomplete='off') input(type='submit', value='submit') diff --git a/views/pages/message.pug b/views/pages/message.pug index dab4ab0c..70fb77c1 100644 --- a/views/pages/message.pug +++ b/views/pages/message.pug @@ -7,6 +7,10 @@ block content h1 #{title} if message blockquote #{message} + if messages + ul + each msg in messages + li #{msg} if errors ul each error in errors diff --git a/wipe.js b/wipe.js index 59c62519..0189c090 100644 --- a/wipe.js +++ b/wipe.js @@ -12,6 +12,7 @@ const Mongo = require(__dirname+'/helpers/db.js') await Mongo.connect(); const Boards = require(__dirname+'/db-models/boards.js') , Posts = require(__dirname+'/db-models/posts.js') + , Bans = require(__dirname+'/db-models/bans.js') , Trips = require(__dirname+'/db-models/trips.js') , Accounts = require(__dirname+'/db-models/accounts.js'); console.log('deleting accounts') @@ -24,6 +25,8 @@ const Mongo = require(__dirname+'/helpers/db.js') await Boards.deleteIncrement('b'); await Boards.deleteAll(); await Trips.deleteAll(); + console.log('deleting bans'); + await Bans.deleteAll(); console.log('adding b and pol') await Boards.insertOne({ _id: 'pol', From db963d4607290e98e49b90a1d19f632d906e7b6b Mon Sep 17 00:00:00 2001 From: fatchan Date: Sat, 13 Apr 2019 08:57:03 +0000 Subject: [PATCH 02/20] global and board IP bans, improved error handling, improved permissions checks --- controllers/forms.js | 41 ++++++++++++---------- controllers/pages.js | 43 ++++++++++++------------ db-models/bans.js | 2 ++ db-models/posts.js | 16 +++++++++ helpers/bancheck.js | 10 +++--- helpers/has-perms.js | 9 ----- helpers/hasperms.js | 16 +++++++++ helpers/haspermsmiddleware.js | 16 +++++++++ helpers/{check-auth.js => isloggedin.js} | 0 models/forms/ban-poster.js | 35 ++++++++----------- models/forms/delete-post.js | 41 +++++++++------------- models/forms/dismiss-report.js | 12 ++++--- models/forms/edit-post.js | 13 ++++--- models/forms/login.js | 8 ++--- models/forms/make-post.js | 13 +++---- models/forms/register.js | 8 ++--- models/forms/report-post.js | 8 +++-- models/forms/spoiler-post.js | 13 ++++--- models/pages/board.js | 3 +- models/pages/catalog.js | 3 +- models/pages/globalmanage.js | 20 +++++++++++ models/pages/home.js | 3 +- models/pages/login.js | 2 +- models/pages/manage.js | 5 ++- models/pages/register.js | 11 +----- models/pages/thread.js | 3 +- server.js | 5 ++- views/mixins/post.pug | 4 +-- views/pages/ban.pug | 25 ++++++++++++++ views/pages/globalmanage.pug | 11 ++++++ views/pages/manage.pug | 6 +++- wipe.js | 2 ++ 32 files changed, 247 insertions(+), 160 deletions(-) delete mode 100644 helpers/has-perms.js create mode 100644 helpers/hasperms.js create mode 100644 helpers/haspermsmiddleware.js rename helpers/{check-auth.js => isloggedin.js} (100%) create mode 100644 models/pages/globalmanage.js create mode 100644 views/pages/ban.pug create mode 100644 views/pages/globalmanage.pug diff --git a/controllers/forms.js b/controllers/forms.js index 68ef34dc..92af8cd0 100644 --- a/controllers/forms.js +++ b/controllers/forms.js @@ -46,7 +46,7 @@ router.post('/login', (req, res, next) => { }) } - loginAccount(req, res); + loginAccount(req, res, next); }); @@ -88,7 +88,7 @@ router.post('/register', (req, res, next) => { }) } - registerAccount(req, res); + registerAccount(req, res, next); }); @@ -137,11 +137,11 @@ router.post('/board/:board', Boards.exists, banCheck, numberConverter, async (re }) } - makePost(req, res, numFiles); + makePost(req, res, next, numFiles); }); -//report, delete, sticky, etc +//report/delete/spoiler/ban router.post('/board/:board/posts', Boards.exists, banCheck, numberConverter, async (req, res, next) => { const errors = []; @@ -178,38 +178,45 @@ router.post('/board/:board/posts', Boards.exists, banCheck, numberConverter, asy }) } + const posts = await Posts.getPosts(req.params.board, req.body.checked, true); + if (!posts || posts.length === 0) { + return res.status(404).render('message', { + 'title': 'Not found', + 'errors': 'Selected posts not found', + 'redirect': `/${req.params.board}` + }) + } + const messages = []; try { - //TODO: maybe fetch the posts first instead of checking multiple times with multiple actions - - //global or board ban if (req.body.global_ban) { - messages.push((await banPoster(req, res, null))); + messages.push((await banPoster(req, res, next, null, posts))); } else if (req.body.ban) { - messages.push((await banPoster(req, res, req.params.board))); + messages.push((await banPoster(req, res, next, req.params.board, posts))); } - // then if not deleting, we can spoiler and report or dismiss reports if (req.body.delete) { - messages.push((await deletePosts(req, res))); + messages.push((await deletePosts(req, res, next, posts))); } else { if (req.body.spoiler) { - messages.push((await spoilerPosts(req, res))); + messages.push((await spoilerPosts(req, res, next, posts))); } if (req.body.report) { - messages.push((await reportPosts(req, res))); + messages.push((await reportPosts(req, res, next))); } else if (req.body.dismiss) { - messages.push((await dismissReports(req, res))); + messages.push((await dismissReports(req, res, next))); } } } catch (err) { + //something not right if (err.status) { + // return out special error return res.status(err.status).render('message', err.message); } - console.error(err); - return res.status(500).render('error'); + //some other error, use regular error handler + return next(err); } return res.render('message', { @@ -220,6 +227,4 @@ router.post('/board/:board/posts', Boards.exists, banCheck, numberConverter, asy }); - - module.exports = router; diff --git a/controllers/pages.js b/controllers/pages.js index c3e3bae0..d52a711d 100644 --- a/controllers/pages.js +++ b/controllers/pages.js @@ -3,13 +3,15 @@ const express = require('express') , router = express.Router() , Boards = require(__dirname+'/../db-models/boards.js') - , checkAuth = require(__dirname+'/../helpers/check-auth.js') + , hasPerms = require(__dirname+'/../helpers/haspermsmiddleware.js') + , isLoggedIn = require(__dirname+'/../helpers/isloggedin.js') , numberConverter = require(__dirname+'/../helpers/number-converter.js') //page models - , home = require(__dirname+'/../models/pages/home.js') - , register = require(__dirname+'/../models/pages/register.js') - , manage = require(__dirname+'/../models/pages/manage.js') - , login = require(__dirname+'/../models/pages/login.js') + , home = require(__dirname+'/../models/pages/home.js') + , register = require(__dirname+'/../models/pages/register.js') + , manage = require(__dirname+'/../models/pages/manage.js') + , globalmanage = require(__dirname+'/../models/pages/globalmanage.js') + , login = require(__dirname+'/../models/pages/login.js') , board = require(__dirname+'/../models/pages/board.js') , catalog = require(__dirname+'/../models/pages/catalog.js') , thread = require(__dirname+'/../models/pages/thread.js'); @@ -24,27 +26,24 @@ router.get('/login', login); router.get('/register', register); //logout -router.get('/logout', (req, res, next) => { - - if (req.session.authenticated === true) { - req.session.destroy(); - return res.render('message', { - 'title': 'Success', - 'message': 'You have been logged out successfully', - 'redirect': '/' - }); - } - - return res.status(400).render('message', { - 'title': 'Bad request', - 'message': 'You are not logged in', - 'redirect': '/login' - }) +router.get('/logout', isLoggedIn, (req, res, next) => { + + //remove session + req.session.destroy(); + + return res.render('message', { + 'title': 'Success', + 'message': 'You have been logged out successfully', + 'redirect': '/' + }); }); //board manage page -router.get('/:board/manage', Boards.exists, checkAuth, Boards.canManage, manage); +router.get('/:board/manage', Boards.exists, isLoggedIn, hasPerms, manage); + +//board manage page +router.get('/globalmanage', isLoggedIn, hasPerms, globalmanage); // board page/recents router.get('/:board/:page(\\d+)?', Boards.exists, numberConverter, board); diff --git a/db-models/bans.js b/db-models/bans.js index 3f235e74..c4d58fb2 100644 --- a/db-models/bans.js +++ b/db-models/bans.js @@ -5,6 +5,8 @@ const Mongo = require(__dirname+'/../helpers/db.js') module.exports = { + db, + find: (ip, board) => { return db.find({ 'ip': ip, diff --git a/db-models/posts.js b/db-models/posts.js index 0688a6ce..065f82d6 100644 --- a/db-models/posts.js +++ b/db-models/posts.js @@ -240,6 +240,22 @@ module.exports = { }).toArray(); }, + getAllReports: () => { + return db.find({ + 'reports.0': { + '$exists': true + } + }, { + 'projection': { + 'salt': 0, + 'password': 0, + 'ip': 0, + } + }).sort({ + 'board': 1 + }).toArray(); + }, + deleteOne: (board, options) => { return db.deleteOne(options); }, diff --git a/helpers/bancheck.js b/helpers/bancheck.js index 1d0f5fb5..5e53bff6 100644 --- a/helpers/bancheck.js +++ b/helpers/bancheck.js @@ -1,19 +1,17 @@ 'use strict'; const Bans = require(__dirname+'/../db-models/bans.js') - , hasPerms = require(__dirname+'/has-perms.js'); + , hasPerms = require(__dirname+'/hasperms.js'); module.exports = async (req, res, next) => { if (!hasPerms(req, res)) { const ip = req.headers['x-real-ip'] || req.connection.remoteAddress; - const bans = await Bans.find(ip, res.locals.board._id); + const bans = await Bans.find(ip, res.locals.board ? res.locals.board._id : null); if (bans && bans.length > 0) { //TODO: show posts banned for, expiry, etc - return res.status(403).render('message', { - 'title': 'Forbidden', - 'message': 'You are banned', - 'redirect': '/' + return res.status(403).render('ban', { + bans: bans }); } } diff --git a/helpers/has-perms.js b/helpers/has-perms.js deleted file mode 100644 index 3c64c804..00000000 --- a/helpers/has-perms.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -module.exports = (req, res) => { - return req.session.authenticated //if the user is authed - && req.session.user //if the user is logged in - && (req.session.user.authLevel > 1 //and is not a regular user - || res.locals.board.owner == req.session.user.username //or us board owner - || res.locals.board.moderators.includes(req.session.user.username)); //or is board moderator -} diff --git a/helpers/hasperms.js b/helpers/hasperms.js new file mode 100644 index 00000000..7131aaac --- /dev/null +++ b/helpers/hasperms.js @@ -0,0 +1,16 @@ +'use strict'; + +module.exports = (req, res) => { + return req.session.authenticated //if the user is authed + && req.session.user //if the user is logged in + && ( + req.session.user.authLevel > 1 //and is not a regular user + || ( + res.locals.board + && ( + res.locals.board.owner == req.session.user.username //and board owner + || res.locals.board.moderators.includes(req.session.user.username) //or board mod + ) + ) + ) +} diff --git a/helpers/haspermsmiddleware.js b/helpers/haspermsmiddleware.js new file mode 100644 index 00000000..e928c663 --- /dev/null +++ b/helpers/haspermsmiddleware.js @@ -0,0 +1,16 @@ +'use strict'; + +const hasPerms = require(__dirname+'/hasperms.js'); + +module.exports = async (req, res, next) => { + + if (!hasPerms(req, res)) { + return res.status(403).render('message', { + 'title': 'Forbidden', + 'message': 'You do not have permission to access this page', + 'redirect': '/' + }); + } + next(); + +} diff --git a/helpers/check-auth.js b/helpers/isloggedin.js similarity index 100% rename from helpers/check-auth.js rename to helpers/isloggedin.js diff --git a/models/forms/ban-poster.js b/models/forms/ban-poster.js index c017ca5c..16eb2554 100644 --- a/models/forms/ban-poster.js +++ b/models/forms/ban-poster.js @@ -1,11 +1,13 @@ 'use strict'; const uploadDirectory = require(__dirname+'/../../helpers/uploadDirectory.js') - , hasPerms = require(__dirname+'/../../helpers/has-perms.js') + , hasPerms = require(__dirname+'/../../helpers/hasperms.js') , Bans = require(__dirname+'/../../db-models/bans.js') , Posts = require(__dirname+'/../../db-models/posts.js'); -module.exports = async (req, res, board) => { +module.exports = async (req, res, next, board, checkedPosts) => { + + const posts = checkedPosts; //if user is not logged in or if logged in but not authed, they cannot ban if (!hasPerms(req, res)) { @@ -19,33 +21,24 @@ module.exports = async (req, res, board) => { }; } - //get all posts that were checked - let posts = await Posts.getPosts(req.params.board, req.body.checked, true); //admin arument true, fetches passwords and salts - - if (!posts || posts.length === 0) { - throw { - 'status': 400, - 'message': { - 'title': 'Bad requests', - 'message': 'No posts found', - 'redirect': `/${req.params.board}` - } - }; - } - const bans = posts.map(post => { return { 'ip': post.ip, + 'reason': req.body.reason || 'No reason specified', 'board': board, - 'post': post, - 'issuer': req.session.user.username + 'post': req.body.delete ? null : post, + 'issuer': req.session.user.username, + 'date': new Date(), + 'expireAt': new Date((new Date).getTime() + (72*1000*60*60)) // 72h ban } }); let bannedIps = 0; - const result = await Bans.insertMany(bans, board); - console.log(result) - bannedIps = result.insertedCount; + try { + bannedIps = await Bans.insertMany(bans).then(result => result.insertedCount); + } catch (err) { + return next(err); + } return `Banned ${bannedIps} ips`; diff --git a/models/forms/delete-post.js b/models/forms/delete-post.js index 7eb79f8e..e52db7ad 100644 --- a/models/forms/delete-post.js +++ b/models/forms/delete-post.js @@ -5,24 +5,12 @@ const path = require('path') , fs = require('fs') , unlink = util.promisify(fs.unlink) , uploadDirectory = require(__dirname+'/../../helpers/uploadDirectory.js') - , hasPerms = require(__dirname+'/../../helpers/has-perms.js') + , hasPerms = require(__dirname+'/../../helpers/hasperms.js') , Posts = require(__dirname+'/../../db-models/posts.js'); -module.exports = async (req, res) => { +module.exports = async (req, res, next, checkedPosts) => { - //get all posts that were checked - let posts = await Posts.getPosts(req.params.board, req.body.checked, true); //admin arument true, fetches passwords and salts - - if (!posts || posts.length === 0) { - throw { - 'status': 400, - 'message': { - 'title': 'Bad request', - 'message': 'No posts found', - 'redirect': `/${req.params.board}` - } - }; - } + let posts = checkedPosts; //if user is not logged in OR if lgoged in but not authed, filter the posts by passwords that are not null if (!hasPerms(req, res)) { @@ -34,14 +22,14 @@ module.exports = async (req, res) => { && post.password == req.body.password }); if (posts.length === 0) { - throw { - 'status': 403, - 'message': { - 'title': 'Forbidden', - 'message': 'Password did not match any selected posts', - 'redirect': `/${req.params.board}` - } - }; + throw { + 'status': 403, + 'message': { + 'title': 'Forbidden', + 'message': 'Password did not match any selected posts', + 'redirect': `/${req.params.board}` + } + }; } } @@ -60,8 +48,11 @@ module.exports = async (req, res) => { //delete posts from DB let deletedPosts = 0; - const result = await Posts.deleteMany(req.params.board, allPosts.map(x => x.postId)); - deletedPosts = result.deletedCount; + try { + deletedPosts = await Posts.deleteMany(req.params.board, allPosts.map(x => x.postId)).then(result => result.deletedCount); + } catch (err) { + return next(err); + } //get filenames from all the posts let fileNames = []; diff --git a/models/forms/dismiss-report.js b/models/forms/dismiss-report.js index c7d8e46d..e8bb8e0c 100644 --- a/models/forms/dismiss-report.js +++ b/models/forms/dismiss-report.js @@ -1,9 +1,9 @@ 'use strict'; const Posts = require(__dirname+'/../../db-models/posts.js') - , hasPerms = require(__dirname+'/../../helpers/has-perms.js'); + , hasPerms = require(__dirname+'/../../helpers/hasperms.js'); -module.exports = async (req, res) => { +module.exports = async (req, res, next) => { if (!hasPerms(req, res)) { throw { @@ -16,8 +16,12 @@ module.exports = async (req, res) => { }; } - await Posts.dismissReports(req.params.board, req.body.checked); + try { + await Posts.dismissReports(req.params.board, req.body.checked); + } catch (err) { + return next(err); + } - return `Dismissed report(s) successfully` + return `Dismissed report(s) successfully`; } diff --git a/models/forms/edit-post.js b/models/forms/edit-post.js index 6c8bad63..c256881c 100644 --- a/models/forms/edit-post.js +++ b/models/forms/edit-post.js @@ -1,13 +1,18 @@ 'use strict'; const uuidv4 = require('uuid/v4') - , path = require('path') - , Posts = require(__dirname+'/../../db-models/posts.js') + , path = require('path') + , Posts = require(__dirname+'/../../db-models/posts.js') -module.exports = async (req, res, numFiles) => { +module.exports = async (req, res, next, numFiles) => { // get the post that we are trying to edit - let post = await Posts.getPost(req.params.board, req.body.id, true); + let post; + try { + post = await Posts.getPost(req.params.board, req.body.id, true); + } catch (err) { + return next(err); + } if (!thread || thread.thread != null) { throw { diff --git a/models/forms/login.js b/models/forms/login.js index 07720bef..4db2da19 100644 --- a/models/forms/login.js +++ b/models/forms/login.js @@ -3,7 +3,7 @@ const bcrypt = require('bcrypt') , Accounts = require(__dirname+'/../../db-models/accounts.js'); -module.exports = async (req, res) => { +module.exports = async (req, res, next) => { const username = req.body.username.toLowerCase(); const password = req.body.password; @@ -13,8 +13,7 @@ module.exports = async (req, res) => { try { account = await Accounts.findOne(username); } catch (err) { - console.error(err); - return res.status(500).render('error'); + return next(err); } //if the account doesnt exist, reject @@ -31,8 +30,7 @@ module.exports = async (req, res) => { try { passwordMatch = await bcrypt.compare(password, account.passwordHash); } catch (err) { - console.error(err); - return res.status(500).render('error'); + return next(err); } //if hashes matched diff --git a/models/forms/make-post.js b/models/forms/make-post.js index 5d5c76fd..359399a0 100644 --- a/models/forms/make-post.js +++ b/models/forms/make-post.js @@ -25,7 +25,7 @@ const uuidv4 = require('uuid/v4') , videoIdentify = require(__dirname+'/../../helpers/files/video-identify.js') , formatSize = require(__dirname+'/../../helpers/files/format-size.js') -module.exports = async (req, res, numFiles) => { +module.exports = async (req, res, next, numFiles) => { // check if this is responding to an existing thread let redirect = `/${req.params.board}` @@ -35,8 +35,7 @@ module.exports = async (req, res, numFiles) => { try { thread = await Posts.getPost(req.params.board, req.body.thread, true); } catch (err) { - console.error(err); - return res.status(500).render('error'); + return next(err); } if (!thread || thread.thread != null) { return res.status(400).render('message', { @@ -100,7 +99,7 @@ module.exports = async (req, res, numFiles) => { await videoThumbnail(filename); break; default: - return res.status(500).render('error'); //how did we get here? + return next(err); } //make thumbnail @@ -119,9 +118,8 @@ module.exports = async (req, res, numFiles) => { files.push(processedFile); } catch (err) { - console.error(err); //TODO: DELETE FAILED FILES - return res.status(500).render('error'); + return next(err); } } } @@ -174,8 +172,7 @@ module.exports = async (req, res, numFiles) => { try { postId = await Posts.insertOne(req.params.board, data); } catch (err) { - console.error(err); - return res.status(500).render('error'); + return next(err); } const successRedirect = `/${req.params.board}/thread/${req.body.thread || postId}#${postId}`; diff --git a/models/forms/register.js b/models/forms/register.js index 023357e3..6a576c8c 100644 --- a/models/forms/register.js +++ b/models/forms/register.js @@ -3,7 +3,7 @@ const bcrypt = require('bcrypt') , Accounts = require(__dirname+'/../../db-models/accounts.js'); -module.exports = async (req, res) => { +module.exports = async (req, res, next) => { const username = req.body.username.toLowerCase(); const password = req.body.password; @@ -12,8 +12,7 @@ module.exports = async (req, res) => { try { account = await Accounts.findOne(username); } catch (err) { - console.error(err); - return res.status(500).render('error'); + return next(err); } // if the account exists reject @@ -29,8 +28,7 @@ module.exports = async (req, res) => { try { await Accounts.insertOne(username, password, 1); } catch (err) { - console.error(err); - return res.status(500).render('error'); + return next(err); } return res.render('message', { diff --git a/models/forms/report-post.js b/models/forms/report-post.js index 89d012fb..2778bc67 100644 --- a/models/forms/report-post.js +++ b/models/forms/report-post.js @@ -2,7 +2,7 @@ const Posts = require(__dirname+'/../../db-models/posts.js'); -module.exports = async (req, res) => { +module.exports = async (req, res, next) => { const ip = req.headers['x-real-ip'] || req.connection.remoteAddress; const report = { @@ -12,7 +12,11 @@ module.exports = async (req, res) => { } //push the report to all checked posts - await Posts.reportMany(req.params.board, req.body.checked, report); + try { + await Posts.reportMany(req.params.board, req.body.checked, report); + } catch (err) { + return next(err); + } //hooray! return `Reported post(s) successfully` diff --git a/models/forms/spoiler-post.js b/models/forms/spoiler-post.js index ceababbb..04f17d25 100644 --- a/models/forms/spoiler-post.js +++ b/models/forms/spoiler-post.js @@ -5,13 +5,13 @@ const path = require('path') , fs = require('fs') , unlink = util.promisify(fs.unlink) , uploadDirectory = require(__dirname+'/../../helpers/uploadDirectory.js') - , hasPerms = require(__dirname+'/../../helpers/has-perms.js') + , hasPerms = require(__dirname+'/../../helpers/hasperms.js') , Posts = require(__dirname+'/../../db-models/posts.js'); -module.exports = async (req, res) => { +module.exports = async (req, res, next, checkedPosts) => { //get all posts that were checked - let posts = await Posts.getPosts(req.params.board, req.body.checked, true); //admin arument true, fetches passwords and salts + let posts = checkedPosts; if (!posts || posts.length === 0) { throw { @@ -63,8 +63,11 @@ module.exports = async (req, res) => { // spoiler posts let spoileredPosts = 0; - const result = await Posts.spoilerMany(req.params.board, posts.map(x => x.postId)); - spoileredPosts = result.modifiedCount; + try { + spoileredPosts = await Posts.spoilerMany(req.params.board, posts.map(x => x.postId)).then(result => result.modifiedCount); + } catch (err) { + return next(err); + } //hooray! return `Spoilered ${spoileredPosts} posts` diff --git a/models/pages/board.js b/models/pages/board.js index a4f4a64b..5a1389eb 100644 --- a/models/pages/board.js +++ b/models/pages/board.js @@ -10,8 +10,7 @@ module.exports = async (req, res, next) => { threads = await Posts.getRecent(req.params.board, req.params.page || 1); pages = Math.ceil((await Posts.getPages(req.params.board)) / 10); } catch (err) { - console.error(err) - return next(); + return next(err); } //render the page diff --git a/models/pages/catalog.js b/models/pages/catalog.js index aefd36ce..a8cb2ac2 100644 --- a/models/pages/catalog.js +++ b/models/pages/catalog.js @@ -9,8 +9,7 @@ module.exports = async (req, res, next) => { try { threads = await Posts.getCatalog(req.params.board); } catch (err) { - console.error(err); - return next(); + return next(err); } //render the page diff --git a/models/pages/globalmanage.js b/models/pages/globalmanage.js new file mode 100644 index 00000000..51bf9157 --- /dev/null +++ b/models/pages/globalmanage.js @@ -0,0 +1,20 @@ +'use strict'; + +const Posts = require(__dirname+'/../../db-models/posts.js'); + +module.exports = async (req, res, next) => { + + let posts; + try { + posts = await Posts.getAllReports(); + } catch (err) { + return next(err); + } + + //render the page + res.render('globalmanage', { + csrf: req.csrfToken(), + posts: posts + }); + +} diff --git a/models/pages/home.js b/models/pages/home.js index f8640e0f..2bfbb9e7 100644 --- a/models/pages/home.js +++ b/models/pages/home.js @@ -9,8 +9,7 @@ module.exports = async (req, res, next) => { try { boards = await Boards.find(); } catch (err) { - console.error(err) - return next(); + return next(err); } //render the page diff --git a/models/pages/login.js b/models/pages/login.js index d4241372..70f9275d 100644 --- a/models/pages/login.js +++ b/models/pages/login.js @@ -1,6 +1,6 @@ 'use strict'; -module.exports = (req, res) => { +module.exports = (req, res, next) => { //render the page res.render('login', { diff --git a/models/pages/manage.js b/models/pages/manage.js index 4a1de412..a981b79c 100644 --- a/models/pages/manage.js +++ b/models/pages/manage.js @@ -2,14 +2,13 @@ const Posts = require(__dirname+'/../../db-models/posts.js'); -module.exports = async (req, res) => { +module.exports = async (req, res, next) => { let posts; try { posts = await Posts.getReports(req.params.board); } catch (err) { - console.error(err); - return res.status(500).render('error'); + return next(err) } //render the page diff --git a/models/pages/register.js b/models/pages/register.js index ce522342..f8ce78fb 100644 --- a/models/pages/register.js +++ b/models/pages/register.js @@ -1,15 +1,6 @@ 'use strict'; -module.exports = (req, res) => { - - //send home if already logged in - if (req.session.authenticated === true) { - return res.status(400).render('message', { - 'title': 'Notice', - 'message': 'You are already logged in. Redirecting you to back home.', - 'redirect': '/' - }); - } +module.exports = (req, res, next) => { //render the page res.render('register', { diff --git a/models/pages/thread.js b/models/pages/thread.js index 67b2b75d..3ff515ce 100644 --- a/models/pages/thread.js +++ b/models/pages/thread.js @@ -9,8 +9,7 @@ module.exports = async (req, res, next) => { try { thread = await Posts.getThread(req.params.board, req.params.id); } catch (err) { - console.error(err); - return next(); + return next(err); } if (!thread) { diff --git a/server.js b/server.js index 5ee75cd9..8773f802 100644 --- a/server.js +++ b/server.js @@ -74,7 +74,10 @@ const express = require('express') return res.status(403).send('Invalid CSRF token') } console.error(err.stack) - return res.status(500).render('error') + return res.status(500).render('message', { + 'title': 'Internal Server Error', + 'redirect': req.header('Referer') || '/' + }) }) // listen diff --git a/views/mixins/post.pug b/views/mixins/post.pug index 9af10305..e1cc90e9 100644 --- a/views/mixins/post.pug +++ b/views/mixins/post.pug @@ -1,4 +1,4 @@ -mixin post(board, post, truncate) +mixin post(board, post, truncate, showreports) article(id=post.postId class='post-container '+(post.thread ? '' : 'op')) header.post-info input.post-check(type='checkbox', name='checked[]' value=post.postId) @@ -44,7 +44,7 @@ mixin post(board, post, truncate) blockquote.post-message !{post.message} else blockquote.post-message !{post.message} - if post.reports + if showreports && post.reports each report in post.reports .reports.post-container span Date: #{report.date.toLocaleString()} diff --git a/views/pages/ban.pug b/views/pages/ban.pug new file mode 100644 index 00000000..515efd80 --- /dev/null +++ b/views/pages/ban.pug @@ -0,0 +1,25 @@ +extends ../layout.pug +include ../mixins/post.pug + +block head + title Banned! + +block content + h1.board-title Banned! + hr(size=1) + Bans currently in place against your IP: + hr(size=1) + for ban in bans + if ban.board + div Board: #[a(href=`/${ban.board}`) /#{ban.board}/] + else + div Global ban. + div Reason: #{ban.reason} + div Issuer: #{ban.issuer} + div Date: #{ban.date} + div Expiry: #{ban.expireAt} + if ban.post + span Post: + section.thread + +post(ban.post.board, ban.post, false) + hr(size=1) diff --git a/views/pages/globalmanage.pug b/views/pages/globalmanage.pug new file mode 100644 index 00000000..75b0bb85 --- /dev/null +++ b/views/pages/globalmanage.pug @@ -0,0 +1,11 @@ +extends ../layout.pug +include ../mixins/post.pug + +block head + title Manage + +block content + h1.board-title Global Management + hr(size=1) + p under construction + diff --git a/views/pages/manage.pug b/views/pages/manage.pug index cb6e86e7..f4cacf23 100644 --- a/views/pages/manage.pug +++ b/views/pages/manage.pug @@ -15,7 +15,7 @@ block content hr(size=1) for post in posts section.thread - +post(board, post) + +post(board, post, false, true) hr(size=1) section.action-wrapper span @@ -26,6 +26,10 @@ block content label input.post-check(type='checkbox', name='spoiler' value=1) | Spoiler + span + label + input.post-check(type='checkbox', name='dismiss' value=1) + | Dismiss span label input.post-check(type='checkbox', name='ban' value=1) diff --git a/wipe.js b/wipe.js index 0189c090..09f59161 100644 --- a/wipe.js +++ b/wipe.js @@ -43,6 +43,8 @@ const Mongo = require(__dirname+'/helpers/db.js') moderators: [], }) console.log('creating indexes') + await Bans.db.dropIndexes(); + await Bans.db.createIndex({ "expireAt": 1 }, { expireAfterSeconds: 0 }); await Posts.db.dropIndexes(); //these are fucked await Posts.db.createIndex({ From 3c327862d914e72a8ecbb2a533bbe34eccbf39a2 Mon Sep 17 00:00:00 2001 From: fatchan Date: Sat, 13 Apr 2019 13:51:24 +0000 Subject: [PATCH 03/20] some pages redirect after logging in --- controllers/forms.js | 11 ++++++++++- helpers/isloggedin.js | 11 +++++++++-- models/forms/login.js | 11 ++++------- models/pages/login.js | 3 ++- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/controllers/forms.js b/controllers/forms.js index 92af8cd0..e50187f7 100644 --- a/controllers/forms.js +++ b/controllers/forms.js @@ -14,6 +14,7 @@ const express = require('express') , dismissReports = require(__dirname+'/../models/forms/dismiss-report.js') , loginAccount = require(__dirname+'/../models/forms/login.js') , registerAccount = require(__dirname+'/../models/forms/register.js') + , hasPerms = require(__dirname+'/../helpers/haspermsmiddleware.js') , numberConverter = require(__dirname+'/../helpers/number-converter.js') , banCheck = require(__dirname+'/../helpers/bancheck.js'); @@ -160,7 +161,8 @@ router.post('/board/:board/posts', Boards.exists, banCheck, numberConverter, asy || req.body.dismiss || req.body.spoiler || req.body.ban - || req.body.global_ban)) { + || req.body.global_ban + || req.body.preserve_post)) { errors.push('Must select an action') } if (req.body.report && (!req.body.reason || req.body.reason.length === 0)) { @@ -227,4 +229,11 @@ router.post('/board/:board/posts', Boards.exists, banCheck, numberConverter, asy }); +//unban +router.post('/board/:board/bans', Boards.exists, banCheck, hasPerms, numberConverter, async (req, res, next) => { + + //TODO: unbans + +}); + module.exports = router; diff --git a/helpers/isloggedin.js b/helpers/isloggedin.js index 3809e84d..a444a9bd 100644 --- a/helpers/isloggedin.js +++ b/helpers/isloggedin.js @@ -1,6 +1,13 @@ 'use strict'; module.exports = (req, res, next) => { - if (req.session.authenticated === true) return next() - res.redirect('/login') + if (req.session.authenticated === true) { + return next(); + } + const redirect = req.params.board; + if (redirect) { + res.redirect(`/login?redirect=${redirect}`); + } else { + res.redirect('/login'); + } } diff --git a/models/forms/login.js b/models/forms/login.js index 4db2da19..ce055232 100644 --- a/models/forms/login.js +++ b/models/forms/login.js @@ -7,6 +7,7 @@ module.exports = async (req, res, next) => { const username = req.body.username.toLowerCase(); const password = req.body.password; + const redirect = req.body.redirect; //fetch an account let account; @@ -21,7 +22,7 @@ module.exports = async (req, res, next) => { return res.status(403).render('message', { 'title': 'Forbidden', 'message': 'Incorrect username or password', - 'redirect': '/login' + 'redirect': redirect ? `/login?redirect=${redirect}` : '/login' }); } @@ -44,18 +45,14 @@ module.exports = async (req, res, next) => { req.session.authenticated = true; //successful login - return res.render('message', { - 'title': 'Success', - 'message': `Welcome, ${username}`, - 'redirect': '/' - }); + return res.redirect(redirect ? `/${redirect}/manage` : '/'); } return res.status(403).render('message', { 'title': 'Forbidden', 'message': 'Incorrect username or password', - 'redirect': '/login' + 'redirect': redirect ? `/login?redirect=${redirect}` : '/login' }); } diff --git a/models/pages/login.js b/models/pages/login.js index 70f9275d..373b330d 100644 --- a/models/pages/login.js +++ b/models/pages/login.js @@ -4,7 +4,8 @@ module.exports = (req, res, next) => { //render the page res.render('login', { - csrf: req.csrfToken() + csrf: req.csrfToken(), + redirect: req.query.redirect, }); } From e8be5ea7b76fe09835b9a5c3d5c974cca44fcf82 Mon Sep 17 00:00:00 2001 From: fatchan Date: Sat, 13 Apr 2019 13:52:35 +0000 Subject: [PATCH 04/20] template improvements and no need to pass board to post mixin --- views/includes/boardheader.pug | 7 ++-- views/includes/navbar.pug | 6 ++-- views/mixins/ban.pug | 16 +++++++++ views/mixins/post.pug | 6 ++-- views/pages/ban.pug | 15 ++------ views/pages/board.pug | 4 +-- views/pages/login.pug | 1 + views/pages/manage.pug | 63 ++++++++++++++++++++-------------- views/pages/thread.pug | 4 +-- 9 files changed, 71 insertions(+), 51 deletions(-) create mode 100644 views/mixins/ban.pug diff --git a/views/includes/boardheader.pug b/views/includes/boardheader.pug index b95ca162..265ef00e 100644 --- a/views/includes/boardheader.pug +++ b/views/includes/boardheader.pug @@ -1,3 +1,4 @@ -a.no-decoration(href=`/${board._id}`) - h1.board-title /#{board._id}/ - #{board.name} -h4.board-description #{board.description} +section.board-header + a.no-decoration(href=`/${board._id}`) + h1.board-title /#{board._id}/ - #{board.name} + h4.board-description #{board.description} diff --git a/views/includes/navbar.pug b/views/includes/navbar.pug index 9d77b7a0..7589d92d 100644 --- a/views/includes/navbar.pug +++ b/views/includes/navbar.pug @@ -1,5 +1,7 @@ nav.navbar a.nav-item(href='/') Home - a.nav-item(href='/login') Login if board - a.nav-item(href=`/${board._id}/manage`) Manage Board + a.nav-item.right(href=`/${board._id}/manage`) Manage Board + a.nav-item.right(href='/logout') Logout + a.nav-item.right(href='/register') Register + a.nav-item.right(href='/login') Login diff --git a/views/mixins/ban.pug b/views/mixins/ban.pug new file mode 100644 index 00000000..b506ff61 --- /dev/null +++ b/views/mixins/ban.pug @@ -0,0 +1,16 @@ +include ./post.pug + +mixin ban(ban) + if ban.board + div Board: #[a(href=`/${ban.board}`) /#{ban.board}/] + else + div Global ban. + div Reason: #{ban.reason} + div Issuer: #{ban.issuer} + div Date: #{ban.date} + div Expiry: #{ban.expireAt} + if ban.post + span Post: + section.thread + +post(ban.post, false) + diff --git a/views/mixins/post.pug b/views/mixins/post.pug index e1cc90e9..16a6d6ef 100644 --- a/views/mixins/post.pug +++ b/views/mixins/post.pug @@ -1,4 +1,4 @@ -mixin post(board, post, truncate, showreports) +mixin post(post, truncate, showreports) article(id=post.postId class='post-container '+(post.thread ? '' : 'op')) header.post-info input.post-check(type='checkbox', name='checked[]' value=post.postId) @@ -11,7 +11,7 @@ mixin post(board, post, truncate, showreports) span.post-name #{post.name} span #{post.date.toLocaleString()} span.user-id(style=`background: #${post.userId}`) #{post.userId} - span: a(href=`/${board._id}/thread/${post.thread ? post.thread : post.postId}#${post.postId}`) ##{post.postId} + span: a(href=`/${post.board}/thread/${post.thread ? post.thread : post.postId}#${post.postId}`) ##{post.postId} if post.files.length > 0 .post-files each file in post.files @@ -39,7 +39,7 @@ mixin post(board, post, truncate, showreports) } if truncated blockquote.post-message !{truncatedMessage} - p Message too long. #[a(href=`/${board._id}/thread/${post.thread == null ? post.postId : post.thread}#${post.postId}`) Click here] to view the full text. + p Message too long. #[a(href=`/${post.board}/thread/${post.thread == null ? post.postId : post.thread}#${post.postId}`) Click here] to view the full text. else blockquote.post-message !{post.message} else diff --git a/views/pages/ban.pug b/views/pages/ban.pug index 515efd80..1a733185 100644 --- a/views/pages/ban.pug +++ b/views/pages/ban.pug @@ -1,5 +1,5 @@ extends ../layout.pug -include ../mixins/post.pug +include ../mixins/ban.pug block head title Banned! @@ -10,16 +10,5 @@ block content Bans currently in place against your IP: hr(size=1) for ban in bans - if ban.board - div Board: #[a(href=`/${ban.board}`) /#{ban.board}/] - else - div Global ban. - div Reason: #{ban.reason} - div Issuer: #{ban.issuer} - div Date: #{ban.date} - div Expiry: #{ban.expireAt} - if ban.post - span Post: - section.thread - +post(ban.post.board, ban.post, false) + +ban(ban) hr(size=1) diff --git a/views/pages/board.pug b/views/pages/board.pug index 75137981..b912c862 100644 --- a/views/pages/board.pug +++ b/views/pages/board.pug @@ -17,9 +17,9 @@ block content hr(size=1) for thread in threads section.thread - +post(board, thread, true) + +post(thread, true) for post in thread.replies - +post(board, post, true) + +post(post, true) hr(size=1) if pages > 0 span.pages Page diff --git a/views/pages/login.pug b/views/pages/login.pug index 85d15f09..fcd7d50f 100644 --- a/views/pages/login.pug +++ b/views/pages/login.pug @@ -7,6 +7,7 @@ block content section.form-wrapper form.form-post(action='/forms/login' method='POST') input(type='hidden' name='_csrf' value=csrf) + input(type='hidden' name='redirect' value=redirect) input#username(type='text', name='username', placeholder='username' maxlength='50') input#password(type='password', name='password', placeholder='password' maxlength='100') input(type='submit', value='submit') diff --git a/views/pages/manage.pug b/views/pages/manage.pug index f4cacf23..447a1878 100644 --- a/views/pages/manage.pug +++ b/views/pages/manage.pug @@ -1,5 +1,6 @@ extends ../layout.pug include ../mixins/post.pug +include ../mixins/ban.pug block head title Manage @@ -7,38 +8,48 @@ block head block content include ../includes/boardheader.pug hr(size=1) - h4.board-description Management Panel - form(action='/forms/board/'+board._id+'/posts' method='POST' enctype='application/x-www-form-urlencoded') + h4 Reports: + form(action=`/forms/board/${board._id}/posts` method='POST' enctype='application/x-www-form-urlencoded') input(type='hidden' name='_csrf' value=csrf) if posts.length === 0 p No reports. hr(size=1) for post in posts section.thread - +post(board, post, false, true) + +post(post, false, true) hr(size=1) section.action-wrapper - span - label - input.post-check(type='checkbox', name='delete' value=1) - | Delete - span - label - input.post-check(type='checkbox', name='spoiler' value=1) - | Spoiler - span - label - input.post-check(type='checkbox', name='dismiss' value=1) - | Dismiss - span - label - input.post-check(type='checkbox', name='ban' value=1) - | Ban - label - input.post-check(type='checkbox', name='global_ban' value=1) - | Global Ban - label - input#report(type='text', name='reason', placeholder='ban reason' autocomplete='off') + label + input.post-check(type='checkbox', name='delete' value=1) + | Delete Post + label + input.post-check(type='checkbox', name='spoiler' value=1) + | Spoiler Images + label + input.post-check(type='checkbox', name='dismiss' value=1) + | Dismiss Reports + label + input.post-check(type='checkbox', name='ban' value=1) + | Ban Poster + label + input.post-check(type='checkbox', name='global_ban' value=1) + | Global Ban Poster + label + input.post-check(type='checkbox', name='preserve_post' value=1) + | Show Post In Ban + label + input#report(type='text', name='reason', placeholder='ban reason' autocomplete='off') input(type='submit', value='submit') - - + hr(size=1) + h4 Bans: + form(action=`/forms/board/${board._id}/unban` method='POST' enctype='application/x-www-form-urlencoded') + input(type='hidden' name='_csrf' value=csrf) + if bans.length === 0 + p No bans. + hr(size=1) + for ban in bans + section.thread + +ban(ban) + hr(size=1) + //section.action-wrapper + input(type='submit', value='unban') diff --git a/views/pages/thread.pug b/views/pages/thread.pug index cb23c2ba..a047896e 100644 --- a/views/pages/thread.pug +++ b/views/pages/thread.pug @@ -18,8 +18,8 @@ block content form(action='/forms/board/'+board._id+'/posts' method='POST' enctype='application/x-www-form-urlencoded') input(type='hidden' name='_csrf' value=csrf) section.thread - +post(board, thread) + +post(thread) for post in thread.replies - +post(board, post) + +post(post) hr(size=1) include ../includes/deletefooter.pug From 529bc5e91d82403aca6ec94f2eebcb241c445515 Mon Sep 17 00:00:00 2001 From: fatchan Date: Sat, 13 Apr 2019 13:53:50 +0000 Subject: [PATCH 05/20] option to preserve post in ban + show bans on manage page --- controllers/pages.js | 1 - db-models/bans.js | 6 ++++++ models/forms/ban-poster.js | 2 +- models/forms/register.js | 6 +----- models/pages/manage.js | 8 ++++++-- server.js | 2 +- static/css/style.css | 19 ++++++++++++++++--- 7 files changed, 31 insertions(+), 13 deletions(-) diff --git a/controllers/pages.js b/controllers/pages.js index d52a711d..8c8a354b 100644 --- a/controllers/pages.js +++ b/controllers/pages.js @@ -30,7 +30,6 @@ router.get('/logout', isLoggedIn, (req, res, next) => { //remove session req.session.destroy(); - return res.render('message', { 'title': 'Success', 'message': 'You have been logged out successfully', diff --git a/db-models/bans.js b/db-models/bans.js index c4d58fb2..80163324 100644 --- a/db-models/bans.js +++ b/db-models/bans.js @@ -16,6 +16,12 @@ module.exports = { }).toArray(); }, + getBoardBans: (board) => { + return db.find({ + 'board': board, + }).toArray(); + }, + insertOne: (ban) => { return db.insertOne(ban); }, diff --git a/models/forms/ban-poster.js b/models/forms/ban-poster.js index 16eb2554..2893d406 100644 --- a/models/forms/ban-poster.js +++ b/models/forms/ban-poster.js @@ -26,7 +26,7 @@ module.exports = async (req, res, next, board, checkedPosts) => { 'ip': post.ip, 'reason': req.body.reason || 'No reason specified', 'board': board, - 'post': req.body.delete ? null : post, + 'post': req.body.preserve_post ? post : null, 'issuer': req.session.user.username, 'date': new Date(), 'expireAt': new Date((new Date).getTime() + (72*1000*60*60)) // 72h ban diff --git a/models/forms/register.js b/models/forms/register.js index 6a576c8c..ef79e9fd 100644 --- a/models/forms/register.js +++ b/models/forms/register.js @@ -31,10 +31,6 @@ module.exports = async (req, res, next) => { return next(err); } - return res.render('message', { - 'title': 'Success', - 'message': `Welcome, ${username}`, - 'redirect': '/' - }); + return res.redirect('/login') } diff --git a/models/pages/manage.js b/models/pages/manage.js index a981b79c..1ef2ec91 100644 --- a/models/pages/manage.js +++ b/models/pages/manage.js @@ -1,12 +1,15 @@ 'use strict'; -const Posts = require(__dirname+'/../../db-models/posts.js'); +const Posts = require(__dirname+'/../../db-models/posts.js') + , Bans = require(__dirname+'/../../db-models/bans.js'); module.exports = async (req, res, next) => { let posts; + let bans; try { posts = await Posts.getReports(req.params.board); + bans = await Bans.getBoardBans(req.params.board); } catch (err) { return next(err) } @@ -14,7 +17,8 @@ module.exports = async (req, res, next) => { //render the page res.render('manage', { csrf: req.csrfToken(), - posts: posts + posts: posts, + bans: bans || [], }); } diff --git a/server.js b/server.js index 8773f802..be89365c 100644 --- a/server.js +++ b/server.js @@ -53,7 +53,7 @@ const express = require('express') // use pug view engine app.set('view engine', 'pug'); app.set('views', path.join(__dirname, 'views/pages')); -// app.enable('view cache'); + app.enable('view cache'); // static files app.use('/css', express.static(__dirname + '/static/css')); diff --git a/static/css/style.css b/static/css/style.css index 5a6d4c3d..5160b871 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -13,6 +13,12 @@ object { object-fit: scale-down; } +.board-header { + display: flex; + flex-direction: column; + align-items: center; +} + .catalog-tile-button { width: 100%; line-height: 30px; @@ -116,7 +122,7 @@ input, textarea { } .action-wrapper { - align-items: center; + /*align-items: center;*/ /*flex-direction: row;*/ } @@ -177,7 +183,7 @@ input textarea { .board-title { color: #af0a0f; - font: bolder; + font: bolder 28px Tahoma; letter-spacing: -2px; text-align: center; margin: 0; @@ -256,6 +262,10 @@ input textarea { border-right: 1px solid lightgray; } +.right { + float: right; +} + .nav-item:hover { box-shadow: inset 0 0 100px 100px rgba(255,255,255,.1); } @@ -274,7 +284,6 @@ table, th, td { } .boards-table { - font-size: 16pt; margin: 0 auto; } @@ -321,4 +330,8 @@ hr { justify-content: space-evenly; } + .boards-table { + width: 100%; + } + } From 4c615bd2a23c60b5b5125cf779d785fd30f03f4c Mon Sep 17 00:00:00 2001 From: fatchan Date: Sat, 13 Apr 2019 16:15:02 +0000 Subject: [PATCH 06/20] dont show thread with single post, make sure thread post is a thread --- db-models/posts.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/db-models/posts.js b/db-models/posts.js index 065f82d6..dfbfab98 100644 --- a/db-models/posts.js +++ b/db-models/posts.js @@ -61,7 +61,8 @@ module.exports = { const data = await Promise.all([ db.findOne({ 'postId': id, - 'board': board + 'board': board, + 'thread': null, }, { 'projection': { 'salt': 0, From c4243d1f81f55a63af751ac96570577ab4da7799 Mon Sep 17 00:00:00 2001 From: fatchan Date: Sat, 13 Apr 2019 16:15:48 +0000 Subject: [PATCH 07/20] markdown fix and simpler login check/redirect --- helpers/isloggedin.js | 7 +------ helpers/markdown.js | 8 ++++---- models/forms/login.js | 2 +- views/includes/navbar.pug | 6 ++++-- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/helpers/isloggedin.js b/helpers/isloggedin.js index a444a9bd..69d540c1 100644 --- a/helpers/isloggedin.js +++ b/helpers/isloggedin.js @@ -4,10 +4,5 @@ module.exports = (req, res, next) => { if (req.session.authenticated === true) { return next(); } - const redirect = req.params.board; - if (redirect) { - res.redirect(`/login?redirect=${redirect}`); - } else { - res.redirect('/login'); - } + res.redirect('/login'); } diff --git a/helpers/markdown.js b/helpers/markdown.js index a0e100ce..ad436888 100644 --- a/helpers/markdown.js +++ b/helpers/markdown.js @@ -1,9 +1,9 @@ 'use strict'; const Posts = require(__dirname+'/../db-models/posts.js') - , quoteRegex = /^>>\d+$/gm - , greentextRegex = /^>[^>].+$/gm - , redtextRegex = /^<[^<].+$/gm + , quoteRegex = /^>>\d+/gm + , greentextRegex = /^>[^>].+/gm + , redtextRegex = /^<[^<].+/gm , boldRegex = /==.+==/gm , italicRegex = /__.+__/gm , linkRegex = /https?\:\/\/[^\s]+/g @@ -43,7 +43,7 @@ module.exports = (board, thread, text) => { //italic text = text.replace(italicRegex, (match) => { const italic = match.substring(2, match.length-2); - return `${italic}`; + return `${italic}`; }); //spoilers diff --git a/models/forms/login.js b/models/forms/login.js index ce055232..fa5940e1 100644 --- a/models/forms/login.js +++ b/models/forms/login.js @@ -45,7 +45,7 @@ module.exports = async (req, res, next) => { req.session.authenticated = true; //successful login - return res.redirect(redirect ? `/${redirect}/manage` : '/'); + return res.redirect(redirect || '/'); } diff --git a/views/includes/navbar.pug b/views/includes/navbar.pug index 7589d92d..ad2623ca 100644 --- a/views/includes/navbar.pug +++ b/views/includes/navbar.pug @@ -2,6 +2,8 @@ nav.navbar a.nav-item(href='/') Home if board a.nav-item.right(href=`/${board._id}/manage`) Manage Board - a.nav-item.right(href='/logout') Logout + a.nav-item.right(href=`/login?redirect=/${board._id}/`) Login + else + a.nav-item.right(href='/login') Login a.nav-item.right(href='/register') Register - a.nav-item.right(href='/login') Login + a.nav-item.right(href='/logout') Logout From 956a8693eccb769f51fe725f90899e6e2bddf517 Mon Sep 17 00:00:00 2001 From: fatchan Date: Sun, 14 Apr 2019 12:39:30 +0000 Subject: [PATCH 08/20] small form improvements and corrections --- controllers/forms.js | 5 ++--- models/forms/delete-post.js | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/controllers/forms.js b/controllers/forms.js index e50187f7..a727fd3f 100644 --- a/controllers/forms.js +++ b/controllers/forms.js @@ -161,9 +161,8 @@ router.post('/board/:board/posts', Boards.exists, banCheck, numberConverter, asy || req.body.dismiss || req.body.spoiler || req.body.ban - || req.body.global_ban - || req.body.preserve_post)) { - errors.push('Must select an action') + || req.body.global_ban)) { + errors.push('Invalid actions selected') } if (req.body.report && (!req.body.reason || req.body.reason.length === 0)) { errors.push('Reports must have a reason') diff --git a/models/forms/delete-post.js b/models/forms/delete-post.js index e52db7ad..12510c1d 100644 --- a/models/forms/delete-post.js +++ b/models/forms/delete-post.js @@ -70,6 +70,6 @@ module.exports = async (req, res, next, checkedPosts) => { })); //hooray! - return `Deleted ${threadIds.length} threads and ${deletedPosts} posts` + return `Deleted ${threadIds.length} threads and ${deletedPosts-threadIds.length} posts` } From 8ce5aa292c7162bcfd2e71a4613f0028de90f00b Mon Sep 17 00:00:00 2001 From: fatchan Date: Sun, 14 Apr 2019 13:44:37 +0000 Subject: [PATCH 09/20] quotes cross-thread fixes #11 --- helpers/markdown.js | 7 ------ helpers/quotes.js | 46 +++++++++++++++++++++++++++++++++++++++ models/forms/make-post.js | 5 ++++- 3 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 helpers/quotes.js diff --git a/helpers/markdown.js b/helpers/markdown.js index ad436888..7a3d5f43 100644 --- a/helpers/markdown.js +++ b/helpers/markdown.js @@ -1,7 +1,6 @@ 'use strict'; const Posts = require(__dirname+'/../db-models/posts.js') - , quoteRegex = /^>>\d+/gm , greentextRegex = /^>[^>].+/gm , redtextRegex = /^<[^<].+/gm , boldRegex = /==.+==/gm @@ -28,12 +27,6 @@ module.exports = (board, thread, text) => { return `${match}`; }); - //quotes - text = text.replace(quoteRegex, (match) => { - const quotenum = match.substring(2); - return `>>${quotenum}`; - }); - //bold text = text.replace(boldRegex, (match) => { const bold = match.substring(2, match.length-2); diff --git a/helpers/quotes.js b/helpers/quotes.js new file mode 100644 index 00000000..0d022ddf --- /dev/null +++ b/helpers/quotes.js @@ -0,0 +1,46 @@ +'use strict'; + +const Posts = require(__dirname+'/../db-models/posts.js') + , quoteRegex = /^>>\d+/gm; + +module.exports = async (board, text) => { + + //get the matches + const matches = text.match(quoteRegex); + if (!matches) { + return text; + } + + //get all the Ids + const quoteIds = matches.map(x => +x.substring(2)); + + //get all posts with those Ids + const posts = await Posts.getPosts(board, quoteIds, false); + + //turn the result into a map of postId => threadId/postId + const postThreadObject = {}; + let validQuotes = 0; + for (let i = 0; i < posts.length; i++) { + const post = posts[i]; + postThreadObject[post.postId] = post.thread || post.postId; + validQuotes++; + } + + console.log(validQuotes); + //if none of the quotes were real, dont do a replace + if (validQuotes === 0) { + return text; + } + + //then replace the quotes with only ones that exist + text = text.replace(quoteRegex, (match) => { + const quotenum = +match.substring(2); + if (postThreadObject[quotenum]) { + return `>>${quotenum}`; + } + return match; + }); + + return text; + +} diff --git a/models/forms/make-post.js b/models/forms/make-post.js index 359399a0..10cf8950 100644 --- a/models/forms/make-post.js +++ b/models/forms/make-post.js @@ -8,6 +8,7 @@ const uuidv4 = require('uuid/v4') , uploadDirectory = require(__dirname+'/../../helpers/uploadDirectory.js') , Posts = require(__dirname+'/../../db-models/posts.js') , getTripCode = require(__dirname+'/../../helpers/tripcode.js') + , getQuotes = require(__dirname+'/../../helpers/quotes.js') , simpleMarkdown = require(__dirname+'/../../helpers/markdown.js') , sanitize = require('sanitize-html') , sanitizeOptions = { @@ -147,7 +148,9 @@ module.exports = async (req, res, next, numFiles) => { //simple markdown and sanitize let message = req.body.message; if (message && message.length > 0) { - message = sanitize(simpleMarkdown(req.params.board, req.body.thread, message), sanitizeOptions); + message = simpleMarkdown(req.params.board, req.body.thread, message); + message = await getQuotes(req.params.board, message); + message = sanitize(message, sanitizeOptions); } //add post to DB From 345f948136cb168ada36a29c27344d02c92e6cee Mon Sep 17 00:00:00 2001 From: fatchan Date: Sun, 14 Apr 2019 13:53:41 +0000 Subject: [PATCH 10/20] change quote regex, dont log and change target styling to highlight OPs --- helpers/quotes.js | 3 +-- static/css/style.css | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/helpers/quotes.js b/helpers/quotes.js index 0d022ddf..0d6786aa 100644 --- a/helpers/quotes.js +++ b/helpers/quotes.js @@ -1,7 +1,7 @@ 'use strict'; const Posts = require(__dirname+'/../db-models/posts.js') - , quoteRegex = /^>>\d+/gm; + , quoteRegex = />>\d+/gm; module.exports = async (board, text) => { @@ -26,7 +26,6 @@ module.exports = async (board, text) => { validQuotes++; } - console.log(validQuotes); //if none of the quotes were real, dont do a replace if (validQuotes === 0) { return text; diff --git a/static/css/style.css b/static/css/style.css index 5160b871..0b04cbdc 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -52,6 +52,7 @@ object { box-shadow: 0 0 3px black; min-width: 64px; min-height: 64px; + object-fit: cover; } .catalog { @@ -210,7 +211,7 @@ input textarea { } .post-container:target, .op:target { - background-color: #d6bad0; + background-color: #d6bad0!important; border-color: #ba9dbf; } From 279b3c2dd509d7876b51850de2bc4e15b80938be Mon Sep 17 00:00:00 2001 From: fatchan Date: Mon, 15 Apr 2019 12:08:50 +0000 Subject: [PATCH 11/20] remove old unused api stuff --- models/api/get-boards.js | 17 ----------------- models/api/get-catalog.js | 20 -------------------- models/api/get-recent.js | 20 -------------------- models/api/get-thread.js | 20 -------------------- 4 files changed, 77 deletions(-) delete mode 100644 models/api/get-boards.js delete mode 100644 models/api/get-catalog.js delete mode 100644 models/api/get-recent.js delete mode 100644 models/api/get-thread.js diff --git a/models/api/get-boards.js b/models/api/get-boards.js deleted file mode 100644 index d8bc0a5d..00000000 --- a/models/api/get-boards.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -const Boards = require(__dirname+'/../../db-models/boards.js'); - -module.exports = async (req, res) => { - //get a list of boards - let boards; - try { - boards = await Boards.find(); - } catch (err) { - console.error(err); - return res.status(500).json({ 'message': 'Error fetching from DB' }) - } - - //render the page - res.json(boards) -} diff --git a/models/api/get-catalog.js b/models/api/get-catalog.js deleted file mode 100644 index 9a4e78e4..00000000 --- a/models/api/get-catalog.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -const Posts = require(__dirname+'/../../db-models/posts.js'); - -module.exports = async (req, res) => { - //get the recently bumped thread & preview posts - let data; - try { - data = await Posts.getCatalog(req.params.board); - } catch (err) { - console.error(err); - return res.status(500).json({ 'message': 'Error fetching from DB' }); - } - - if (!data) { - return res.status(404).json({ 'message': 'Not found' }); - } - - return res.json(data) -} diff --git a/models/api/get-recent.js b/models/api/get-recent.js deleted file mode 100644 index 2f6cf32b..00000000 --- a/models/api/get-recent.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -const Posts = require(__dirname+'/../../db-models/posts.js'); - -module.exports = async (req, res) => { - //get the recently bumped thread & preview posts - let threads; - try { - threads = await Posts.getRecent(req.params.board, req.params.page || 1); - } catch (err) { - console.error(err); - return res.status(500).json({ 'message': 'Error fetching from DB' }); - } - - if (!threads || threads.lenth === 0) { - return res.status(404).json({ 'message': 'Not found' }); - } - - return res.json(threads); -} diff --git a/models/api/get-thread.js b/models/api/get-thread.js deleted file mode 100644 index 58fe7e95..00000000 --- a/models/api/get-thread.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -const Posts = require(__dirname+'/../../db-models/posts.js'); - -module.exports = async (req, res) => { - //get the recently bumped thread & preview posts - let thread; - try { - thread = await Posts.getThread(req.params.board, req.params.id); - } catch (err) { - console.error(err); - return res.status(500).json({ 'message': 'Error fetching from DB' }); - } - - if (!thread) { - return res.status(404).json({ 'message': 'Not found' }); - } - - return res.json(thread) -} From 335b38f9cc49b4cb476c7e1503936b3f5c6be3ea Mon Sep 17 00:00:00 2001 From: fatchan Date: Mon, 15 Apr 2019 12:55:26 +0000 Subject: [PATCH 12/20] unbanning, rename some routes and remove some unnecessary error catches --- controllers/forms.js | 48 ++++++++++++++++++++++++++++------ db-models/bans.js | 23 ++++++++++++++++ helpers/number-converter.js | 6 ++--- models/forms/ban-poster.js | 7 +---- models/forms/delete-post.js | 7 +---- models/forms/dismiss-report.js | 6 +---- models/forms/edit-post.js | 32 +++-------------------- models/forms/make-post.js | 4 +-- models/forms/removebans.js | 13 +++++++++ models/forms/report-post.js | 6 +---- models/forms/spoiler-post.js | 9 ++----- static/css/style.css | 2 +- views/includes/postform.pug | 15 +++++------ views/mixins/ban.pug | 26 +++++++++--------- views/mixins/post.pug | 4 +-- views/pages/board.pug | 2 +- views/pages/manage.pug | 4 +-- views/pages/thread.pug | 2 +- 18 files changed, 118 insertions(+), 98 deletions(-) create mode 100644 models/forms/removebans.js diff --git a/controllers/forms.js b/controllers/forms.js index a727fd3f..946f6206 100644 --- a/controllers/forms.js +++ b/controllers/forms.js @@ -7,6 +7,7 @@ const express = require('express') , Trips = require(__dirname+'/../db-models/trips.js') , Bans = require(__dirname+'/../db-models/bans.js') , banPoster = require(__dirname+'/../models/forms/ban-poster.js') + , removeBans = require(__dirname+'/../models/forms/removebans.js') , makePost = require(__dirname+'/../models/forms/make-post.js') , deletePosts = require(__dirname+'/../models/forms/delete-post.js') , spoilerPosts = require(__dirname+'/../models/forms/spoiler-post.js') @@ -94,7 +95,7 @@ router.post('/register', (req, res, next) => { }); // make new post -router.post('/board/:board', Boards.exists, banCheck, numberConverter, async (req, res, next) => { +router.post('/board/:board/post', Boards.exists, banCheck, numberConverter, async (req, res, next) => { let numFiles = 0; if (req.files && req.files.file) { @@ -143,11 +144,11 @@ router.post('/board/:board', Boards.exists, banCheck, numberConverter, async (re }); //report/delete/spoiler/ban -router.post('/board/:board/posts', Boards.exists, banCheck, numberConverter, async (req, res, next) => { +router.post('/board/:board/actions', Boards.exists, banCheck, numberConverter, async (req, res, next) => { const errors = []; - if (!req.body.checked || req.body.checked.length === 0 || req.body.checked.length > 10) { + if (!req.body.checkedposts || req.body.checkedposts.length === 0 || req.body.checkedposts.length > 10) { errors.push('Must select 1-10 posts') } if (req.body.password && req.body.password.length > 50) { @@ -179,7 +180,7 @@ router.post('/board/:board/posts', Boards.exists, banCheck, numberConverter, asy }) } - const posts = await Posts.getPosts(req.params.board, req.body.checked, true); + const posts = await Posts.getPosts(req.params.board, req.body.checkedposts, true); if (!posts || posts.length === 0) { return res.status(404).render('message', { 'title': 'Not found', @@ -190,7 +191,6 @@ router.post('/board/:board/posts', Boards.exists, banCheck, numberConverter, asy const messages = []; try { - if (req.body.global_ban) { messages.push((await banPoster(req, res, next, null, posts))); } else if (req.body.ban) { @@ -209,7 +209,6 @@ router.post('/board/:board/posts', Boards.exists, banCheck, numberConverter, asy messages.push((await dismissReports(req, res, next))); } } - } catch (err) { //something not right if (err.status) { @@ -229,10 +228,43 @@ router.post('/board/:board/posts', Boards.exists, banCheck, numberConverter, asy }); //unban -router.post('/board/:board/bans', Boards.exists, banCheck, hasPerms, numberConverter, async (req, res, next) => { +router.post('/board/:board/unban', Boards.exists, banCheck, hasPerms, numberConverter, async (req, res, next) => { + + //keep this for later in case i add other options to unbans + const errors = []; - //TODO: unbans + if (!req.body.checkedbans || req.body.checkedbans.length === 0 || req.body.checkedbans.length > 10) { + errors.push('Must select 1-10 bans') + } + + if (errors.length > 0) { + return res.status(400).render('message', { + 'title': 'Bad request', + 'errors': errors, + 'redirect': `/${req.params.board}/manage` + }); + } + + const messages = []; + try { + messages.push((await removeBans(req, res, next))); + } catch (err) { + //something not right + if (err.status) { + // return out special error + return res.status(err.status).render('message', err.message); + } + //some other error, use regular error handler + return next(err); + } + + return res.render('message', { + 'title': 'Success', + 'messages': messages, + 'redirect': `/${req.params.board}/manage` + }); }); module.exports = router; + diff --git a/db-models/bans.js b/db-models/bans.js index 80163324..21ef11bb 100644 --- a/db-models/bans.js +++ b/db-models/bans.js @@ -1,3 +1,4 @@ + 'use strict'; const Mongo = require(__dirname+'/../helpers/db.js') @@ -16,12 +17,34 @@ module.exports = { }).toArray(); }, + findMany: (board, ids) => { + return db.find({ + '_id': { + '$in': ids + }, + 'board': board + }).toArray(); + }, + + getAll: () => { // for a global ban manage page maybe? still TODO + return db.find({}).toArray(); + }, + getBoardBans: (board) => { return db.find({ 'board': board, }).toArray(); }, + removeMany: (board, ids) => { + return db.deleteMany({ + 'board': board, + '_id': { + '$in': ids + } + }) + }, + insertOne: (ban) => { return db.insertOne(ban); }, diff --git a/helpers/number-converter.js b/helpers/number-converter.js index 149ce2f4..e103ba83 100644 --- a/helpers/number-converter.js +++ b/helpers/number-converter.js @@ -6,9 +6,9 @@ module.exports = (req, res, next) => { if (req.body.thread) { req.body.thread = +req.body.thread; } - if (req.body.checked) { - //syntax casts all string to number - req.body.checked = req.body.checked.map(Number); + if (req.body.checkedposts) { + //syntax tries to convert all string to number + req.body.checkedposts = req.body.checkedposts.map(Number); } //and for params diff --git a/models/forms/ban-poster.js b/models/forms/ban-poster.js index 2893d406..30ac18fe 100644 --- a/models/forms/ban-poster.js +++ b/models/forms/ban-poster.js @@ -33,12 +33,7 @@ module.exports = async (req, res, next, board, checkedPosts) => { } }); - let bannedIps = 0; - try { - bannedIps = await Bans.insertMany(bans).then(result => result.insertedCount); - } catch (err) { - return next(err); - } + const bannedIps = await Bans.insertMany(bans).then(result => result.insertedCount); return `Banned ${bannedIps} ips`; diff --git a/models/forms/delete-post.js b/models/forms/delete-post.js index 12510c1d..d4360fdb 100644 --- a/models/forms/delete-post.js +++ b/models/forms/delete-post.js @@ -47,12 +47,7 @@ module.exports = async (req, res, next, checkedPosts) => { const allPosts = posts.concat(threadPosts) //delete posts from DB - let deletedPosts = 0; - try { - deletedPosts = await Posts.deleteMany(req.params.board, allPosts.map(x => x.postId)).then(result => result.deletedCount); - } catch (err) { - return next(err); - } + const deletedPosts = await Posts.deleteMany(req.params.board, allPosts.map(x => x.postId)).then(result => result.deletedCount); //get filenames from all the posts let fileNames = []; diff --git a/models/forms/dismiss-report.js b/models/forms/dismiss-report.js index e8bb8e0c..15d3b147 100644 --- a/models/forms/dismiss-report.js +++ b/models/forms/dismiss-report.js @@ -16,11 +16,7 @@ module.exports = async (req, res, next) => { }; } - try { - await Posts.dismissReports(req.params.board, req.body.checked); - } catch (err) { - return next(err); - } + await Posts.dismissReports(req.params.board, req.body.checkedposts); return `Dismissed report(s) successfully`; diff --git a/models/forms/edit-post.js b/models/forms/edit-post.js index c256881c..6b293bc2 100644 --- a/models/forms/edit-post.js +++ b/models/forms/edit-post.js @@ -1,35 +1,9 @@ 'use strict'; -const uuidv4 = require('uuid/v4') - , path = require('path') - , Posts = require(__dirname+'/../../db-models/posts.js') +const Posts = require(__dirname+'/../../db-models/posts.js'); -module.exports = async (req, res, next, numFiles) => { +module.exports = async (req, res, next) => { - // get the post that we are trying to edit - let post; - try { - post = await Posts.getPost(req.params.board, req.body.id, true); - } catch (err) { - return next(err); - } - - if (!thread || thread.thread != null) { - throw { - 'status': 400, - 'message': { - 'title': 'Bad request', - 'message': 'Post does not exist.', - 'redirect': redirect - } - }; - } - - // sticky, lock, sage, spoiler, etc - for (let i = 0; i < req.body.actions.length; i++) { - //TODO - } - - return ``; + throw new Error('Not implemented'); } diff --git a/models/forms/make-post.js b/models/forms/make-post.js index 10cf8950..b5b16c41 100644 --- a/models/forms/make-post.js +++ b/models/forms/make-post.js @@ -8,7 +8,7 @@ const uuidv4 = require('uuid/v4') , uploadDirectory = require(__dirname+'/../../helpers/uploadDirectory.js') , Posts = require(__dirname+'/../../db-models/posts.js') , getTripCode = require(__dirname+'/../../helpers/tripcode.js') - , getQuotes = require(__dirname+'/../../helpers/quotes.js') + , linkQuotes = require(__dirname+'/../../helpers/quotes.js') , simpleMarkdown = require(__dirname+'/../../helpers/markdown.js') , sanitize = require('sanitize-html') , sanitizeOptions = { @@ -149,7 +149,7 @@ module.exports = async (req, res, next, numFiles) => { let message = req.body.message; if (message && message.length > 0) { message = simpleMarkdown(req.params.board, req.body.thread, message); - message = await getQuotes(req.params.board, message); + message = await linkQuotes(req.params.board, message); message = sanitize(message, sanitizeOptions); } diff --git a/models/forms/removebans.js b/models/forms/removebans.js new file mode 100644 index 00000000..9293729a --- /dev/null +++ b/models/forms/removebans.js @@ -0,0 +1,13 @@ +'use strict'; + +const Bans = require(__dirname+'/../../db-models/bans.js') + , { ObjectId } = require('mongodb'); + +module.exports = async (req, res, next) => { + + const banIds = req.body.checkedbans.map(ObjectId); + const removedBans = await Bans.removeMany(req.params.board, banIds).then(result => result.deletedCount); + + return `Removed ${removedBans} bans`; + +} diff --git a/models/forms/report-post.js b/models/forms/report-post.js index 2778bc67..b745d264 100644 --- a/models/forms/report-post.js +++ b/models/forms/report-post.js @@ -12,11 +12,7 @@ module.exports = async (req, res, next) => { } //push the report to all checked posts - try { - await Posts.reportMany(req.params.board, req.body.checked, report); - } catch (err) { - return next(err); - } + await Posts.reportMany(req.params.board, req.body.checkedposts, report); //hooray! return `Reported post(s) successfully` diff --git a/models/forms/spoiler-post.js b/models/forms/spoiler-post.js index 04f17d25..088fa714 100644 --- a/models/forms/spoiler-post.js +++ b/models/forms/spoiler-post.js @@ -46,7 +46,7 @@ module.exports = async (req, res, next, checkedPosts) => { } - //filter by not spoilered + //filter by not spoilered. maybe i add filters with optiins in the controller where it gets the posts? posts = posts.filter(post => { return !post.spoiler }); @@ -62,12 +62,7 @@ module.exports = async (req, res, next, checkedPosts) => { } // spoiler posts - let spoileredPosts = 0; - try { - spoileredPosts = await Posts.spoilerMany(req.params.board, posts.map(x => x.postId)).then(result => result.modifiedCount); - } catch (err) { - return next(err); - } + const spoileredPosts = await Posts.spoilerMany(req.params.board, posts.map(x => x.postId)).then(result => result.modifiedCount); //hooray! return `Spoilered ${spoileredPosts} posts` diff --git a/static/css/style.css b/static/css/style.css index 0b04cbdc..3a5b8679 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -200,7 +200,7 @@ input textarea { margin: 0; } -.post-container { +.post-container, .ban { margin: 1px; padding: 2px; background: #D6DAF0; diff --git a/views/includes/postform.pug b/views/includes/postform.pug index d304b6a1..4384dd1d 100644 --- a/views/includes/postform.pug +++ b/views/includes/postform.pug @@ -1,20 +1,20 @@ section.form-wrapper - form.form-post(action='/forms/board/'+board._id, enctype='multipart/form-data', method='POST') - + form.form-post(action=`/forms/board/${board._id}/post`, enctype='multipart/form-data', method='POST') + input(type='hidden' name='_csrf' value=csrf) input(type='hidden' name='thread' value=thread != null ? thread.postId : null) - + input#title(type='text', name='subject', placeholder='subject' autocomplete='off' maxlength='50') - + input#name(type='text', name='name', placeholder='name' autocomplete='off' maxlength='50') input#name(type='text', name='email', placeholder='email' autocomplete='off' maxlength='50') - + input#password(type='password', name='password', placeholder='post password' autocomplete='off' maxlength='50') - + textarea#message(name='message', rows='8', cols='50', placeholder='message' autocomplete='off' maxlength='2000') - + span input#file(type='file', name='file' multiple) label @@ -22,4 +22,3 @@ section.form-wrapper | Spoiler input(type='submit', value='submit') - diff --git a/views/mixins/ban.pug b/views/mixins/ban.pug index b506ff61..c6043836 100644 --- a/views/mixins/ban.pug +++ b/views/mixins/ban.pug @@ -1,16 +1,18 @@ include ./post.pug mixin ban(ban) - if ban.board - div Board: #[a(href=`/${ban.board}`) /#{ban.board}/] - else - div Global ban. - div Reason: #{ban.reason} - div Issuer: #{ban.issuer} - div Date: #{ban.date} - div Expiry: #{ban.expireAt} - if ban.post - span Post: - section.thread - +post(ban.post, false) + .ban + input.post-check(type='checkbox', name='checkedbans[]' value=ban._id) + if ban.board + div Board: #[a(href=`/${ban.board}`) /#{ban.board}/] + else + div Global ban. + div Reason: #{ban.reason} + div Issuer: #{ban.issuer} + div Date: #{ban.date} + div Expiry: #{ban.expireAt} + if ban.post + span Post: + section.thread + +post(ban.post, false) diff --git a/views/mixins/post.pug b/views/mixins/post.pug index 16a6d6ef..ac90cab0 100644 --- a/views/mixins/post.pug +++ b/views/mixins/post.pug @@ -1,7 +1,7 @@ mixin post(post, truncate, showreports) article(id=post.postId class='post-container '+(post.thread ? '' : 'op')) header.post-info - input.post-check(type='checkbox', name='checked[]' value=post.postId) + input.post-check(type='checkbox', name='checkedposts[]' value=post.postId) if post.subject span.post-subject #{post.subject} if post.email @@ -16,7 +16,7 @@ mixin post(post, truncate, showreports) .post-files each file in post.files .post-file - .post-file-info + small.post-file-info span: a(href='/img/'+file.filename download=file.originalFilename) #{file.originalFilename} br span (#{file.sizeString} #{file.geometryString}) diff --git a/views/pages/board.pug b/views/pages/board.pug index b912c862..a1d39007 100644 --- a/views/pages/board.pug +++ b/views/pages/board.pug @@ -10,7 +10,7 @@ block content include ../includes/postform.pug .mode Posting mode: Thread [#[a.no-decoration(href=`/${board._id}/catalog`) Catalog]] hr(size=1) - form(action='/forms/board/'+board._id+'/posts' method='POST' enctype='application/x-www-form-urlencoded') + form(action='/forms/board/'+board._id+'/actions' method='POST' enctype='application/x-www-form-urlencoded') input(type='hidden' name='_csrf' value=csrf) if threads.length === 0 p No posts. diff --git a/views/pages/manage.pug b/views/pages/manage.pug index 447a1878..35fcc8a8 100644 --- a/views/pages/manage.pug +++ b/views/pages/manage.pug @@ -9,7 +9,7 @@ block content include ../includes/boardheader.pug hr(size=1) h4 Reports: - form(action=`/forms/board/${board._id}/posts` method='POST' enctype='application/x-www-form-urlencoded') + form(action=`/forms/board/${board._id}/actions` method='POST' enctype='application/x-www-form-urlencoded') input(type='hidden' name='_csrf' value=csrf) if posts.length === 0 p No reports. @@ -51,5 +51,5 @@ block content section.thread +ban(ban) hr(size=1) - //section.action-wrapper + section.action-wrapper input(type='submit', value='unban') diff --git a/views/pages/thread.pug b/views/pages/thread.pug index a047896e..f0fcfbf4 100644 --- a/views/pages/thread.pug +++ b/views/pages/thread.pug @@ -15,7 +15,7 @@ block content include ../includes/postform.pug .mode Posting mode: Reply [#[a.no-decoration(href=`/${board._id}`) Go Back]] hr(size=1) - form(action='/forms/board/'+board._id+'/posts' method='POST' enctype='application/x-www-form-urlencoded') + form(action=`/forms/board/${board._id}/actions` method='POST' enctype='application/x-www-form-urlencoded') input(type='hidden' name='_csrf' value=csrf) section.thread +post(thread) From a4f45119623cf012c3eeca3a73b223003c9b5a7d Mon Sep 17 00:00:00 2001 From: fatchan Date: Mon, 15 Apr 2019 13:42:37 +0000 Subject: [PATCH 13/20] styling changes --- static/css/style.css | 35 ++++++++++++++++++++++++++--------- views/mixins/post.pug | 3 ++- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/static/css/style.css b/static/css/style.css index 3a5b8679..d4a1988b 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -142,9 +142,7 @@ input, textarea { .post-check { position: relative; top: 3px; - margin: 2px; - margin-right: 4px; - padding: 0; + margin: -3px 1px !important; } .post-files { @@ -174,6 +172,10 @@ input, textarea { figure { } +blockquote a { + color: #d00; +} + input textarea { padding: 8px; } @@ -200,6 +202,10 @@ input textarea { margin: 0; } +.post-message { + overflow-y: auto; +} + .post-container, .ban { margin: 1px; padding: 2px; @@ -220,7 +226,6 @@ input textarea { margin-left: 0; display: block; border: none; - width: 100%; } .post-subject { @@ -233,16 +238,22 @@ input textarea { font-weight: bold; } -.post-info { - margin-top: -2px; +.post-container.op .post-info { + background: none; } -.post-image { +.post-info { + margin: -2px; + /*margin-left: -3px;*/ + padding: 2px; + padding-left: 1px; + /*background-color: #B7C5D9;*/ } -.post-content { - +.post-info * { + margin-bottom: 0; + margin-top: -2px; } .navbar { @@ -251,6 +262,7 @@ input textarea { position: fixed; width: 100%; background: #eef2ff; + z-index: 1; } .nav-item { @@ -318,6 +330,7 @@ hr { .post-check { top: 2px; + margin-left: 2px!important; height: 8px; } @@ -335,4 +348,8 @@ hr { width: 100%; } + .post-info { + background-color: #B7C5D9; + } + } diff --git a/views/mixins/post.pug b/views/mixins/post.pug index ac90cab0..3474f78f 100644 --- a/views/mixins/post.pug +++ b/views/mixins/post.pug @@ -1,7 +1,8 @@ mixin post(post, truncate, showreports) article(id=post.postId class='post-container '+(post.thread ? '' : 'op')) header.post-info - input.post-check(type='checkbox', name='checkedposts[]' value=post.postId) + span + input.post-check(type='checkbox', name='checkedposts[]' value=post.postId) if post.subject span.post-subject #{post.subject} if post.email From ff4f6c47582e5f7defde4964b72e2f693c6701bd Mon Sep 17 00:00:00 2001 From: fatchan Date: Mon, 15 Apr 2019 15:16:49 +0000 Subject: [PATCH 14/20] stop calling that a model --- controllers/forms.js | 8 +++--- controllers/pages.js | 2 +- {db-models => db}/accounts.js | 0 {db-models => db}/bans.js | 0 {db-models => db}/boards.js | 0 {helpers => db}/db.js | 0 {db-models => db}/posts.js | 0 {db-models => db}/trips.js | 0 helpers/bancheck.js | 2 +- helpers/markdown.js | 2 +- helpers/number-converter.js | 10 +++++++ helpers/quotes.js | 2 +- helpers/tripcode.js | 2 +- models/forms/ban-poster.js | 4 +-- models/forms/delete-post.js | 2 +- models/forms/dismiss-report.js | 2 +- models/forms/edit-post.js | 2 +- models/forms/login.js | 2 +- models/forms/make-post.js | 2 +- models/forms/register.js | 2 +- models/forms/removebans.js | 2 +- models/forms/report-post.js | 2 +- models/forms/spoiler-post.js | 2 +- models/pages/board.js | 12 ++++++--- models/pages/catalog.js | 2 +- models/pages/globalmanage.js | 2 +- models/pages/home.js | 2 +- models/pages/manage.js | 4 +-- models/pages/thread.js | 2 +- server.js | 2 +- static/css/style.css | 26 ++++++++++++++----- views/includes/deletefooter.pug | 40 +++++++++++++++++----------- views/includes/pages.pug | 6 +++++ views/pages/board.pug | 11 ++++---- views/pages/manage.pug | 46 ++++++++++----------------------- wipe.js | 10 +++---- 36 files changed, 120 insertions(+), 95 deletions(-) rename {db-models => db}/accounts.js (100%) rename {db-models => db}/bans.js (100%) rename {db-models => db}/boards.js (100%) rename {helpers => db}/db.js (100%) rename {db-models => db}/posts.js (100%) rename {db-models => db}/trips.js (100%) create mode 100644 views/includes/pages.pug diff --git a/controllers/forms.js b/controllers/forms.js index 946f6206..da521304 100644 --- a/controllers/forms.js +++ b/controllers/forms.js @@ -2,10 +2,10 @@ const express = require('express') , router = express.Router() - , Boards = require(__dirname+'/../db-models/boards.js') - , Posts = require(__dirname+'/../db-models/posts.js') - , Trips = require(__dirname+'/../db-models/trips.js') - , Bans = require(__dirname+'/../db-models/bans.js') + , Boards = require(__dirname+'/../db/boards.js') + , Posts = require(__dirname+'/../db/posts.js') + , Trips = require(__dirname+'/../db/trips.js') + , Bans = require(__dirname+'/../db/bans.js') , banPoster = require(__dirname+'/../models/forms/ban-poster.js') , removeBans = require(__dirname+'/../models/forms/removebans.js') , makePost = require(__dirname+'/../models/forms/make-post.js') diff --git a/controllers/pages.js b/controllers/pages.js index 8c8a354b..e9a37d0a 100644 --- a/controllers/pages.js +++ b/controllers/pages.js @@ -45,7 +45,7 @@ router.get('/:board/manage', Boards.exists, isLoggedIn, hasPerms, manage); router.get('/globalmanage', isLoggedIn, hasPerms, globalmanage); // board page/recents -router.get('/:board/:page(\\d+)?', Boards.exists, numberConverter, board); +router.get('/:board', Boards.exists, numberConverter, board); // thread view page router.get('/:board/thread/:id(\\d+)', Boards.exists, numberConverter, thread); diff --git a/db-models/accounts.js b/db/accounts.js similarity index 100% rename from db-models/accounts.js rename to db/accounts.js diff --git a/db-models/bans.js b/db/bans.js similarity index 100% rename from db-models/bans.js rename to db/bans.js diff --git a/db-models/boards.js b/db/boards.js similarity index 100% rename from db-models/boards.js rename to db/boards.js diff --git a/helpers/db.js b/db/db.js similarity index 100% rename from helpers/db.js rename to db/db.js diff --git a/db-models/posts.js b/db/posts.js similarity index 100% rename from db-models/posts.js rename to db/posts.js diff --git a/db-models/trips.js b/db/trips.js similarity index 100% rename from db-models/trips.js rename to db/trips.js diff --git a/helpers/bancheck.js b/helpers/bancheck.js index 5e53bff6..684f4cbf 100644 --- a/helpers/bancheck.js +++ b/helpers/bancheck.js @@ -1,6 +1,6 @@ 'use strict'; -const Bans = require(__dirname+'/../db-models/bans.js') +const Bans = require(__dirname+'/../db/bans.js') , hasPerms = require(__dirname+'/hasperms.js'); module.exports = async (req, res, next) => { diff --git a/helpers/markdown.js b/helpers/markdown.js index 7a3d5f43..8e9435d9 100644 --- a/helpers/markdown.js +++ b/helpers/markdown.js @@ -1,6 +1,6 @@ 'use strict'; -const Posts = require(__dirname+'/../db-models/posts.js') +const Posts = require(__dirname+'/../db/posts.js') , greentextRegex = /^>[^>].+/gm , redtextRegex = /^<[^<].+/gm , boldRegex = /==.+==/gm diff --git a/helpers/number-converter.js b/helpers/number-converter.js index e103ba83..6604e1ad 100644 --- a/helpers/number-converter.js +++ b/helpers/number-converter.js @@ -19,6 +19,16 @@ module.exports = (req, res, next) => { req.params.page = +req.params.page; } + //and query + if (req.query.p) { + const pnum = +req.query.p; + if (Number.isSafeInteger(pnum)) { + req.query.p = +req.query.p; + } else { + req.query.p = null; + } + } + next(); } diff --git a/helpers/quotes.js b/helpers/quotes.js index 0d6786aa..428b6cf9 100644 --- a/helpers/quotes.js +++ b/helpers/quotes.js @@ -1,6 +1,6 @@ 'use strict'; -const Posts = require(__dirname+'/../db-models/posts.js') +const Posts = require(__dirname+'/../db/posts.js') , quoteRegex = />>\d+/gm; module.exports = async (board, text) => { diff --git a/helpers/tripcode.js b/helpers/tripcode.js index f7736f5c..e1ac247b 100644 --- a/helpers/tripcode.js +++ b/helpers/tripcode.js @@ -1,6 +1,6 @@ 'use strict'; -const Tripcodes = require(__dirname+'/../db-models/trips.js') +const Tripcodes = require(__dirname+'/../db/trips.js') , crypto = require('crypto'); module.exports = async (password) => { diff --git a/models/forms/ban-poster.js b/models/forms/ban-poster.js index 30ac18fe..8ee14f41 100644 --- a/models/forms/ban-poster.js +++ b/models/forms/ban-poster.js @@ -2,8 +2,8 @@ const uploadDirectory = require(__dirname+'/../../helpers/uploadDirectory.js') , hasPerms = require(__dirname+'/../../helpers/hasperms.js') - , Bans = require(__dirname+'/../../db-models/bans.js') - , Posts = require(__dirname+'/../../db-models/posts.js'); + , Bans = require(__dirname+'/../../db/bans.js') + , Posts = require(__dirname+'/../../db/posts.js'); module.exports = async (req, res, next, board, checkedPosts) => { diff --git a/models/forms/delete-post.js b/models/forms/delete-post.js index d4360fdb..85e08b00 100644 --- a/models/forms/delete-post.js +++ b/models/forms/delete-post.js @@ -6,7 +6,7 @@ const path = require('path') , unlink = util.promisify(fs.unlink) , uploadDirectory = require(__dirname+'/../../helpers/uploadDirectory.js') , hasPerms = require(__dirname+'/../../helpers/hasperms.js') - , Posts = require(__dirname+'/../../db-models/posts.js'); + , Posts = require(__dirname+'/../../db/posts.js'); module.exports = async (req, res, next, checkedPosts) => { diff --git a/models/forms/dismiss-report.js b/models/forms/dismiss-report.js index 15d3b147..fa4d1cd5 100644 --- a/models/forms/dismiss-report.js +++ b/models/forms/dismiss-report.js @@ -1,6 +1,6 @@ 'use strict'; -const Posts = require(__dirname+'/../../db-models/posts.js') +const Posts = require(__dirname+'/../../db/posts.js') , hasPerms = require(__dirname+'/../../helpers/hasperms.js'); module.exports = async (req, res, next) => { diff --git a/models/forms/edit-post.js b/models/forms/edit-post.js index 6b293bc2..37efbecd 100644 --- a/models/forms/edit-post.js +++ b/models/forms/edit-post.js @@ -1,6 +1,6 @@ 'use strict'; -const Posts = require(__dirname+'/../../db-models/posts.js'); +const Posts = require(__dirname+'/../../db/posts.js'); module.exports = async (req, res, next) => { diff --git a/models/forms/login.js b/models/forms/login.js index fa5940e1..fa8e8b1a 100644 --- a/models/forms/login.js +++ b/models/forms/login.js @@ -1,7 +1,7 @@ 'use strict'; const bcrypt = require('bcrypt') - , Accounts = require(__dirname+'/../../db-models/accounts.js'); + , Accounts = require(__dirname+'/../../db/accounts.js'); module.exports = async (req, res, next) => { diff --git a/models/forms/make-post.js b/models/forms/make-post.js index b5b16c41..9839e99c 100644 --- a/models/forms/make-post.js +++ b/models/forms/make-post.js @@ -6,7 +6,7 @@ const uuidv4 = require('uuid/v4') , crypto = require('crypto') , randomBytes = util.promisify(crypto.randomBytes) , uploadDirectory = require(__dirname+'/../../helpers/uploadDirectory.js') - , Posts = require(__dirname+'/../../db-models/posts.js') + , Posts = require(__dirname+'/../../db/posts.js') , getTripCode = require(__dirname+'/../../helpers/tripcode.js') , linkQuotes = require(__dirname+'/../../helpers/quotes.js') , simpleMarkdown = require(__dirname+'/../../helpers/markdown.js') diff --git a/models/forms/register.js b/models/forms/register.js index ef79e9fd..51f96bf5 100644 --- a/models/forms/register.js +++ b/models/forms/register.js @@ -1,7 +1,7 @@ 'use strict'; const bcrypt = require('bcrypt') - , Accounts = require(__dirname+'/../../db-models/accounts.js'); + , Accounts = require(__dirname+'/../../db/accounts.js'); module.exports = async (req, res, next) => { diff --git a/models/forms/removebans.js b/models/forms/removebans.js index 9293729a..db0af77a 100644 --- a/models/forms/removebans.js +++ b/models/forms/removebans.js @@ -1,6 +1,6 @@ 'use strict'; -const Bans = require(__dirname+'/../../db-models/bans.js') +const Bans = require(__dirname+'/../../db/bans.js') , { ObjectId } = require('mongodb'); module.exports = async (req, res, next) => { diff --git a/models/forms/report-post.js b/models/forms/report-post.js index b745d264..dad4bf94 100644 --- a/models/forms/report-post.js +++ b/models/forms/report-post.js @@ -1,6 +1,6 @@ 'use strict'; -const Posts = require(__dirname+'/../../db-models/posts.js'); +const Posts = require(__dirname+'/../../db/posts.js'); module.exports = async (req, res, next) => { diff --git a/models/forms/spoiler-post.js b/models/forms/spoiler-post.js index 088fa714..f819ba2a 100644 --- a/models/forms/spoiler-post.js +++ b/models/forms/spoiler-post.js @@ -6,7 +6,7 @@ const path = require('path') , unlink = util.promisify(fs.unlink) , uploadDirectory = require(__dirname+'/../../helpers/uploadDirectory.js') , hasPerms = require(__dirname+'/../../helpers/hasperms.js') - , Posts = require(__dirname+'/../../db-models/posts.js'); + , Posts = require(__dirname+'/../../db/posts.js'); module.exports = async (req, res, next, checkedPosts) => { diff --git a/models/pages/board.js b/models/pages/board.js index 5a1389eb..fa82182c 100644 --- a/models/pages/board.js +++ b/models/pages/board.js @@ -1,22 +1,28 @@ 'use strict'; -const Posts = require(__dirname+'/../../db-models/posts.js'); +const Posts = require(__dirname+'/../../db/posts.js'); module.exports = async (req, res, next) => { //get the recently bumped thread & preview posts + const page = req.query.p || 1; let threads; let pages; try { - threads = await Posts.getRecent(req.params.board, req.params.page || 1); pages = Math.ceil((await Posts.getPages(req.params.board)) / 10); + if (page > pages) { + return next(); + } + threads = await Posts.getRecent(req.params.board, page); } catch (err) { return next(err); } + //render the page res.render('board', { csrf: req.csrfToken(), threads: threads || [], - pages: pages + pages, + page, }); } diff --git a/models/pages/catalog.js b/models/pages/catalog.js index a8cb2ac2..2dd42f0a 100644 --- a/models/pages/catalog.js +++ b/models/pages/catalog.js @@ -1,6 +1,6 @@ 'use strict'; -const Posts = require(__dirname+'/../../db-models/posts.js'); +const Posts = require(__dirname+'/../../db/posts.js'); module.exports = async (req, res, next) => { diff --git a/models/pages/globalmanage.js b/models/pages/globalmanage.js index 51bf9157..30f775d1 100644 --- a/models/pages/globalmanage.js +++ b/models/pages/globalmanage.js @@ -1,6 +1,6 @@ 'use strict'; -const Posts = require(__dirname+'/../../db-models/posts.js'); +const Posts = require(__dirname+'/../../db/posts.js'); module.exports = async (req, res, next) => { diff --git a/models/pages/home.js b/models/pages/home.js index 2bfbb9e7..cdddc7ba 100644 --- a/models/pages/home.js +++ b/models/pages/home.js @@ -1,6 +1,6 @@ 'use strict'; -const Boards = require(__dirname+'/../../db-models/boards.js'); +const Boards = require(__dirname+'/../../db/boards.js'); module.exports = async (req, res, next) => { diff --git a/models/pages/manage.js b/models/pages/manage.js index 1ef2ec91..a4b7cc02 100644 --- a/models/pages/manage.js +++ b/models/pages/manage.js @@ -1,7 +1,7 @@ 'use strict'; -const Posts = require(__dirname+'/../../db-models/posts.js') - , Bans = require(__dirname+'/../../db-models/bans.js'); +const Posts = require(__dirname+'/../../db/posts.js') + , Bans = require(__dirname+'/../../db/bans.js'); module.exports = async (req, res, next) => { diff --git a/models/pages/thread.js b/models/pages/thread.js index 3ff515ce..1a887e60 100644 --- a/models/pages/thread.js +++ b/models/pages/thread.js @@ -1,6 +1,6 @@ 'use strict'; -const Posts = require(__dirname+'/../../db-models/posts.js'); +const Posts = require(__dirname+'/../../db/posts.js'); module.exports = async (req, res, next) => { diff --git a/server.js b/server.js index be89365c..8773f802 100644 --- a/server.js +++ b/server.js @@ -53,7 +53,7 @@ const express = require('express') // use pug view engine app.set('view engine', 'pug'); app.set('views', path.join(__dirname, 'views/pages')); - app.enable('view cache'); +// app.enable('view cache'); // static files app.use('/css', express.static(__dirname + '/static/css')); diff --git a/static/css/style.css b/static/css/style.css index d4a1988b..2a45827a 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -92,8 +92,12 @@ object { color: green; } +blockquote a { + color: #d00; +} + blockquote { - word-break: break-all; + /*word-break: break-all;*/ white-space: pre-wrap; } @@ -123,8 +127,12 @@ input, textarea { } .action-wrapper { - /*align-items: center;*/ - /*flex-direction: row;*/ + display: flex; + flex-direction: column; +} + +.action-wrapper * { + margin: 3px 0; } .form-post { @@ -172,10 +180,6 @@ input, textarea { figure { } -blockquote a { - color: #d00; -} - input textarea { padding: 8px; } @@ -316,6 +320,10 @@ hr { @media only screen and (max-width: 800px) { + body { + /*font-size: 14pt;*/ + } + input { height: 30px; } @@ -328,6 +336,10 @@ hr { width: 100%; } + blockquote { + margin: 1em; + } + .post-check { top: 2px; margin-left: 2px!important; diff --git a/views/includes/deletefooter.pug b/views/includes/deletefooter.pug index 7f74c19f..0aba7b03 100644 --- a/views/includes/deletefooter.pug +++ b/views/includes/deletefooter.pug @@ -1,15 +1,25 @@ -section.action-wrapper - span - label - input.post-check(type='checkbox', name='delete' value=1) - | Delete - label - input.post-check(type='checkbox', name='spoiler' value=1) - | Spoiler - input#password(type='password', name='password', placeholder='post password' autocomplete='off') - span - label - input.post-check(type='checkbox', name='report' value=1) - | Report - input#report(type='text', name='reason', placeholder='reason' autocomplete='off') - input(type='submit', value='submit') +.action-wrapper Actions: + label + input.post-check(type='checkbox', name='delete' value=1) + | Delete Post + label + input.post-check(type='checkbox', name='spoiler' value=1) + | Spoiler Images + label + input#password(type='text', name='password', placeholder='post password' autocomplete='off') +.action-wrapper Mod Actions: + label + input.post-check(type='checkbox', name='dismiss' value=1) + | Dismiss Reports + label + input.post-check(type='checkbox', name='ban' value=1) + | Ban Poster + label + input.post-check(type='checkbox', name='global_ban' value=1) + | Global Ban Poster + label + input.post-check(type='checkbox', name='preserve_post' value=1) + | Show Post In Ban + label + input#report(type='text', name='reason', placeholder='ban reason' autocomplete='off') +input(type='submit', value='submit') diff --git a/views/includes/pages.pug b/views/includes/pages.pug new file mode 100644 index 00000000..e9834882 --- /dev/null +++ b/views/includes/pages.pug @@ -0,0 +1,6 @@ +span.pages Page: + - for(let i = 1; i <= pages; i++) + if i === page + span: a(href=`/${board._id}?p=${i}`) [#{i}] + else + span: a(href=`/${board._id}?p=${i}`) #{i} diff --git a/views/pages/board.pug b/views/pages/board.pug index a1d39007..e95e6304 100644 --- a/views/pages/board.pug +++ b/views/pages/board.pug @@ -10,6 +10,8 @@ block content include ../includes/postform.pug .mode Posting mode: Thread [#[a.no-decoration(href=`/${board._id}/catalog`) Catalog]] hr(size=1) + include ../includes/pages.pug + hr(size=1) form(action='/forms/board/'+board._id+'/actions' method='POST' enctype='application/x-www-form-urlencoded') input(type='hidden' name='_csrf' value=csrf) if threads.length === 0 @@ -20,10 +22,7 @@ block content +post(thread, true) for post in thread.replies +post(post, true) - hr(size=1) - if pages > 0 - span.pages Page - - for(let i = 0; i < pages; i++) - span: a(href=`/${board._id}/${i+1}`) #{i+1} - hr(size=1) + hr(size=1) + include ../includes/pages.pug + hr(size=1) include ../includes/deletefooter.pug diff --git a/views/pages/manage.pug b/views/pages/manage.pug index 35fcc8a8..888fbf32 100644 --- a/views/pages/manage.pug +++ b/views/pages/manage.pug @@ -14,42 +14,24 @@ block content if posts.length === 0 p No reports. hr(size=1) - for post in posts - section.thread - +post(post, false, true) + else + for post in posts + section.thread + +post(post, false, true) + hr(size=1) + include ../includes/deletefooter.pug hr(size=1) - section.action-wrapper - label - input.post-check(type='checkbox', name='delete' value=1) - | Delete Post - label - input.post-check(type='checkbox', name='spoiler' value=1) - | Spoiler Images - label - input.post-check(type='checkbox', name='dismiss' value=1) - | Dismiss Reports - label - input.post-check(type='checkbox', name='ban' value=1) - | Ban Poster - label - input.post-check(type='checkbox', name='global_ban' value=1) - | Global Ban Poster - label - input.post-check(type='checkbox', name='preserve_post' value=1) - | Show Post In Ban - label - input#report(type='text', name='reason', placeholder='ban reason' autocomplete='off') - input(type='submit', value='submit') - hr(size=1) h4 Bans: form(action=`/forms/board/${board._id}/unban` method='POST' enctype='application/x-www-form-urlencoded') input(type='hidden' name='_csrf' value=csrf) if bans.length === 0 p No bans. hr(size=1) - for ban in bans - section.thread - +ban(ban) - hr(size=1) - section.action-wrapper - input(type='submit', value='unban') + else + for ban in bans + section.thread + +ban(ban) + hr(size=1) + section.action-wrapper + input(type='submit', value='unban') + diff --git a/wipe.js b/wipe.js index 09f59161..cf542eb8 100644 --- a/wipe.js +++ b/wipe.js @@ -10,11 +10,11 @@ const Mongo = require(__dirname+'/helpers/db.js') (async () => { console.log('connecting to db...') await Mongo.connect(); - const Boards = require(__dirname+'/db-models/boards.js') - , Posts = require(__dirname+'/db-models/posts.js') - , Bans = require(__dirname+'/db-models/bans.js') - , Trips = require(__dirname+'/db-models/trips.js') - , Accounts = require(__dirname+'/db-models/accounts.js'); + const Boards = require(__dirname+'/db/boards.js') + , Posts = require(__dirname+'/db/posts.js') + , Bans = require(__dirname+'/db/bans.js') + , Trips = require(__dirname+'/db/trips.js') + , Accounts = require(__dirname+'/db/accounts.js'); console.log('deleting accounts') await Accounts.deleteAll(); console.log('deleting posts') From e80e9e44529ebba88a487295e46defc2cb97a13d Mon Sep 17 00:00:00 2001 From: fatchan Date: Tue, 16 Apr 2019 06:43:17 +0000 Subject: [PATCH 15/20] differentiate between report reason and ban reason, improve actions form, renamed some stuff --- controllers/forms.js | 22 ++++++++++--- controllers/pages.js | 2 +- db/accounts.js | 2 +- db/bans.js | 4 +-- db/boards.js | 2 +- db/posts.js | 10 +----- db/trips.js | 2 +- models/forms/ban-poster.js | 2 +- models/forms/report-post.js | 2 +- models/pages/globalmanage.js | 14 +++++--- models/pages/manage.js | 8 ++--- server.js | 2 +- static/css/style.css | 36 ++++++++++++++------- views/includes/deletefooter.pug | 57 ++++++++++++++++++--------------- views/mixins/post.pug | 4 +-- views/pages/board.pug | 3 +- views/pages/globalmanage.pug | 30 +++++++++++++++-- views/pages/manage.pug | 7 ++-- views/pages/thread.pug | 1 - wipe.js | 2 +- 20 files changed, 130 insertions(+), 82 deletions(-) diff --git a/controllers/forms.js b/controllers/forms.js index da521304..66d6df9a 100644 --- a/controllers/forms.js +++ b/controllers/forms.js @@ -154,9 +154,12 @@ router.post('/board/:board/actions', Boards.exists, banCheck, numberConverter, a if (req.body.password && req.body.password.length > 50) { errors.push('Password must be 50 characters or less'); } - if (req.body.reason && req.body.reason.length > 50) { + if (req.body.report_reason && req.body.report_reason.length > 50) { errors.push('Report must be 50 characters or less'); } + if (req.body.ban_reason && req.body.ban_reason.length > 50) { + errors.push('Ban reason must be 50 characters or less'); + } if (!(req.body.report || req.body.delete || req.body.dismiss @@ -165,12 +168,9 @@ router.post('/board/:board/actions', Boards.exists, banCheck, numberConverter, a || req.body.global_ban)) { errors.push('Invalid actions selected') } - if (req.body.report && (!req.body.reason || req.body.reason.length === 0)) { + if (req.body.report && (!req.body.report_reason || req.body.report_reason.length === 0)) { errors.push('Reports must have a reason') } - if ((req.body.ban || req.body.global_ban) && (!req.body.reason || req.body.reason.length === 0)) { - errors.push('Bans must have a reason') - } if (errors.length > 0) { return res.status(400).render('message', { @@ -266,5 +266,17 @@ router.post('/board/:board/unban', Boards.exists, banCheck, hasPerms, numberConv }); +router.post('/global/actions', hasPerms, numberConverter, async(req, res, next) => { + + //TODO + +}); + +router.post('/global/unban', hasPerms, numberConverter, async(req, res, next) => { + + //TODO + +}); + module.exports = router; diff --git a/controllers/pages.js b/controllers/pages.js index e9a37d0a..b863aa89 100644 --- a/controllers/pages.js +++ b/controllers/pages.js @@ -2,7 +2,7 @@ const express = require('express') , router = express.Router() - , Boards = require(__dirname+'/../db-models/boards.js') + , Boards = require(__dirname+'/../db/boards.js') , hasPerms = require(__dirname+'/../helpers/haspermsmiddleware.js') , isLoggedIn = require(__dirname+'/../helpers/isloggedin.js') , numberConverter = require(__dirname+'/../helpers/number-converter.js') diff --git a/db/accounts.js b/db/accounts.js index 0d142d8b..9138c218 100644 --- a/db/accounts.js +++ b/db/accounts.js @@ -1,7 +1,7 @@ 'use strict'; -const Mongo = require(__dirname+'/../helpers/db.js') +const Mongo = require(__dirname+'/db.js') , db = Mongo.client.db('jschan').collection('accounts') , bcrypt = require('bcrypt'); diff --git a/db/bans.js b/db/bans.js index 21ef11bb..8977968f 100644 --- a/db/bans.js +++ b/db/bans.js @@ -1,7 +1,7 @@ 'use strict'; -const Mongo = require(__dirname+'/../helpers/db.js') +const Mongo = require(__dirname+'/db.js') , db = Mongo.client.db('jschan').collection('bans'); module.exports = { @@ -26,7 +26,7 @@ module.exports = { }).toArray(); }, - getAll: () => { // for a global ban manage page maybe? still TODO + getAllBans: () => { return db.find({}).toArray(); }, diff --git a/db/boards.js b/db/boards.js index eb5a0f37..b242bd28 100644 --- a/db/boards.js +++ b/db/boards.js @@ -1,6 +1,6 @@ 'use strict'; -const Mongo = require(__dirname+'/../helpers/db.js') +const Mongo = require(__dirname+'/db.js') , db = Mongo.client.db('jschan'); module.exports = { diff --git a/db/posts.js b/db/posts.js index dfbfab98..a0788c6f 100644 --- a/db/posts.js +++ b/db/posts.js @@ -1,6 +1,6 @@ 'use strict'; -const Mongo = require(__dirname+'/../helpers/db.js') +const Mongo = require(__dirname+'/db.js') , Boards = require(__dirname+'/boards.js') , db = Mongo.client.db('jschan').collection('posts'); @@ -246,14 +246,6 @@ module.exports = { 'reports.0': { '$exists': true } - }, { - 'projection': { - 'salt': 0, - 'password': 0, - 'ip': 0, - } - }).sort({ - 'board': 1 }).toArray(); }, diff --git a/db/trips.js b/db/trips.js index 22726dbe..c284388e 100644 --- a/db/trips.js +++ b/db/trips.js @@ -1,6 +1,6 @@ 'use strict'; -const Mongo = require(__dirname+'/../helpers/db.js') +const Mongo = require(__dirname+'/db.js') , db = Mongo.client.db('jschan').collection('tripcodes'); module.exports = { diff --git a/models/forms/ban-poster.js b/models/forms/ban-poster.js index 8ee14f41..25f4218a 100644 --- a/models/forms/ban-poster.js +++ b/models/forms/ban-poster.js @@ -24,7 +24,7 @@ module.exports = async (req, res, next, board, checkedPosts) => { const bans = posts.map(post => { return { 'ip': post.ip, - 'reason': req.body.reason || 'No reason specified', + 'reason': req.body.ban_reason || 'No reason specified', 'board': board, 'post': req.body.preserve_post ? post : null, 'issuer': req.session.user.username, diff --git a/models/forms/report-post.js b/models/forms/report-post.js index dad4bf94..c2866e35 100644 --- a/models/forms/report-post.js +++ b/models/forms/report-post.js @@ -6,7 +6,7 @@ module.exports = async (req, res, next) => { const ip = req.headers['x-real-ip'] || req.connection.remoteAddress; const report = { - 'reason': req.body.reason, + 'reason': req.body.report_reason, 'date': new Date(), 'ip': ip } diff --git a/models/pages/globalmanage.js b/models/pages/globalmanage.js index 30f775d1..97daf7ae 100644 --- a/models/pages/globalmanage.js +++ b/models/pages/globalmanage.js @@ -1,20 +1,24 @@ 'use strict'; -const Posts = require(__dirname+'/../../db/posts.js'); +const Posts = require(__dirname+'/../../db/posts.js') + , Bans = require(__dirname+'/../../db/bans.js'); module.exports = async (req, res, next) => { - let posts; + let reports; + let bans; try { - posts = await Posts.getAllReports(); + reports = await Posts.getAllReports(); + bans = await Bans.getAllBans(); } catch (err) { - return next(err); + return next(err) } //render the page res.render('globalmanage', { csrf: req.csrfToken(), - posts: posts + reports, + bans, }); } diff --git a/models/pages/manage.js b/models/pages/manage.js index a4b7cc02..962f5039 100644 --- a/models/pages/manage.js +++ b/models/pages/manage.js @@ -5,10 +5,10 @@ const Posts = require(__dirname+'/../../db/posts.js') module.exports = async (req, res, next) => { - let posts; + let reports; let bans; try { - posts = await Posts.getReports(req.params.board); + reports = await Posts.getReports(req.params.board); bans = await Bans.getBoardBans(req.params.board); } catch (err) { return next(err) @@ -17,8 +17,8 @@ module.exports = async (req, res, next) => { //render the page res.render('manage', { csrf: req.csrfToken(), - posts: posts, - bans: bans || [], + reports, + bans, }); } diff --git a/server.js b/server.js index 8773f802..932e84b2 100644 --- a/server.js +++ b/server.js @@ -13,7 +13,7 @@ const express = require('express') , bodyParser = require('body-parser') , cookieParser = require('cookie-parser') , configs = require(__dirname+'/configs/main.json') - , Mongo = require(__dirname+'/helpers/db.js') + , Mongo = require(__dirname+'/db/db.js') , upload = require('express-fileupload'); (async () => { diff --git a/static/css/style.css b/static/css/style.css index 2a45827a..04df4a8d 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -9,6 +9,10 @@ body { margin: 0; } +.pages a { + text-decoration: none; +} + object { object-fit: scale-down; } @@ -57,9 +61,9 @@ object { .catalog { display:flex; - flex-direction: row; align-items:flex-start; - flex-wrap: wrap; + justify-content: space-evenly; + flex-flow: row wrap; } .spoiler { @@ -126,13 +130,26 @@ input, textarea { margin: 10px 0; } +.actions { + background: #D6DAF0; + border-color: #B7C5D9; + border-width: 0 1px 1px 0; + border-style: none solid solid none; + max-width: 100%; + display: flex; + flex-direction: column; + margin: 2px 0; + padding: 2px; +} + .action-wrapper { display: flex; flex-direction: column; + align-items: flex-start; } -.action-wrapper * { - margin: 3px 0; +.actions label { + margin: 2px 0; } .form-post { @@ -211,7 +228,8 @@ input textarea { } .post-container, .ban { - margin: 1px; + box-sizing: border-box; + margin: 2px 0; padding: 2px; background: #D6DAF0; border-color: #B7C5D9; @@ -242,7 +260,7 @@ input textarea { font-weight: bold; } -.post-container.op .post-info { +.post-container.op .post-info, .catalog-tile-content .post-info { background: none; } @@ -320,10 +338,6 @@ hr { @media only screen and (max-width: 800px) { - body { - /*font-size: 14pt;*/ - } - input { height: 30px; } @@ -352,8 +366,6 @@ hr { .catalog-tile { overflow-y: hidden; - width: 49%; - justify-content: space-evenly; } .boards-table { diff --git a/views/includes/deletefooter.pug b/views/includes/deletefooter.pug index 0aba7b03..b86e6ddf 100644 --- a/views/includes/deletefooter.pug +++ b/views/includes/deletefooter.pug @@ -1,25 +1,32 @@ -.action-wrapper Actions: - label - input.post-check(type='checkbox', name='delete' value=1) - | Delete Post - label - input.post-check(type='checkbox', name='spoiler' value=1) - | Spoiler Images - label - input#password(type='text', name='password', placeholder='post password' autocomplete='off') -.action-wrapper Mod Actions: - label - input.post-check(type='checkbox', name='dismiss' value=1) - | Dismiss Reports - label - input.post-check(type='checkbox', name='ban' value=1) - | Ban Poster - label - input.post-check(type='checkbox', name='global_ban' value=1) - | Global Ban Poster - label - input.post-check(type='checkbox', name='preserve_post' value=1) - | Show Post In Ban - label - input#report(type='text', name='reason', placeholder='ban reason' autocomplete='off') -input(type='submit', value='submit') +.action-wrapper + .actions Actions: + label + input.post-check(type='checkbox', name='delete' value=1) + | Delete Post + label + input.post-check(type='checkbox', name='spoiler' value=1) + | Spoiler Images + label + input#password(type='text', name='password', placeholder='post password' autocomplete='off') + label + input.post-check(type='checkbox', name='report' value=1) + | Report Post + label + input#report(type='text', name='report_reason', placeholder='report reason' autocomplete='off') + .actions Mod Actions: + label + input.post-check(type='checkbox', name='dismiss' value=1) + | Dismiss Reports + label + input.post-check(type='checkbox', name='ban' value=1) + | Ban Poster + label + input.post-check(type='checkbox', name='global_ban' value=1) + | Global Ban Poster + label + input.post-check(type='checkbox', name='preserve_post' value=1) + | Show Post In Ban + label + input#report(type='text', name='ban_reason', placeholder='ban reason' autocomplete='off') + input(type='submit', value='submit') + diff --git a/views/mixins/post.pug b/views/mixins/post.pug index 3474f78f..f3a8ea49 100644 --- a/views/mixins/post.pug +++ b/views/mixins/post.pug @@ -12,7 +12,7 @@ mixin post(post, truncate, showreports) span.post-name #{post.name} span #{post.date.toLocaleString()} span.user-id(style=`background: #${post.userId}`) #{post.userId} - span: a(href=`/${post.board}/thread/${post.thread ? post.thread : post.postId}#${post.postId}`) ##{post.postId} + span: a(href=`/${post.board}/thread/${post.thread || post.postId}#${post.postId}`) ##{post.postId} if post.files.length > 0 .post-files each file in post.files @@ -40,7 +40,7 @@ mixin post(post, truncate, showreports) } if truncated blockquote.post-message !{truncatedMessage} - p Message too long. #[a(href=`/${post.board}/thread/${post.thread == null ? post.postId : post.thread}#${post.postId}`) Click here] to view the full text. + p Message too long. #[a(href=`/${post.board}/thread/${post.thread || post.postId}#${post.postId}`) Click here] to view the full text. else blockquote.post-message !{post.message} else diff --git a/views/pages/board.pug b/views/pages/board.pug index e95e6304..1fbf0369 100644 --- a/views/pages/board.pug +++ b/views/pages/board.pug @@ -6,7 +6,6 @@ block head block content include ../includes/boardheader.pug - hr(size=1) include ../includes/postform.pug .mode Posting mode: Thread [#[a.no-decoration(href=`/${board._id}/catalog`) Catalog]] hr(size=1) @@ -22,7 +21,7 @@ block content +post(thread, true) for post in thread.replies +post(post, true) - hr(size=1) + hr(size=1) include ../includes/pages.pug hr(size=1) include ../includes/deletefooter.pug diff --git a/views/pages/globalmanage.pug b/views/pages/globalmanage.pug index 75b0bb85..36eb1444 100644 --- a/views/pages/globalmanage.pug +++ b/views/pages/globalmanage.pug @@ -1,11 +1,35 @@ extends ../layout.pug include ../mixins/post.pug +include ../mixins/ban.pug block head title Manage block content h1.board-title Global Management - hr(size=1) - p under construction - + h4 All Reports: + form(action=`/forms/global/actions` method='POST' enctype='application/x-www-form-urlencoded') + input(type='hidden' name='_csrf' value=csrf) + if reports.length === 0 + p No reports. + hr(size=1) + else + for report in reports + section.thread + +post(report, false, true) + hr(size=1) + include ../includes/deletefooter.pug + hr(size=1) + h4 All Bans: + form(action=`/forms/global/unban` method='POST' enctype='application/x-www-form-urlencoded') + input(type='hidden' name='_csrf' value=csrf) + if bans.length === 0 + p No bans. + hr(size=1) + else + for ban in bans + section.thread + +ban(ban) + hr(size=1) + section.action-wrapper + input(type='submit', value='unban') diff --git a/views/pages/manage.pug b/views/pages/manage.pug index 888fbf32..e0816182 100644 --- a/views/pages/manage.pug +++ b/views/pages/manage.pug @@ -7,17 +7,16 @@ block head block content include ../includes/boardheader.pug - hr(size=1) h4 Reports: form(action=`/forms/board/${board._id}/actions` method='POST' enctype='application/x-www-form-urlencoded') input(type='hidden' name='_csrf' value=csrf) - if posts.length === 0 + if reports.length === 0 p No reports. hr(size=1) else - for post in posts + for report in reports section.thread - +post(post, false, true) + +post(report, false, true) hr(size=1) include ../includes/deletefooter.pug hr(size=1) diff --git a/views/pages/thread.pug b/views/pages/thread.pug index f0fcfbf4..001ad2cd 100644 --- a/views/pages/thread.pug +++ b/views/pages/thread.pug @@ -11,7 +11,6 @@ block head block content include ../includes/boardheader.pug - hr(size=1) include ../includes/postform.pug .mode Posting mode: Reply [#[a.no-decoration(href=`/${board._id}`) Go Back]] hr(size=1) diff --git a/wipe.js b/wipe.js index cf542eb8..220ce56c 100644 --- a/wipe.js +++ b/wipe.js @@ -1,6 +1,6 @@ 'use strict'; -const Mongo = require(__dirname+'/helpers/db.js') +const Mongo = require(__dirname+'/db/db.js') , util = require('util') , path = require('path') , fs = require('fs') From d5870187bc9fad9959df111df20aca80043159d0 Mon Sep 17 00:00:00 2001 From: fatchan Date: Tue, 16 Apr 2019 15:17:58 +0000 Subject: [PATCH 16/20] global and board bans/reports separated, improved action selection form --- controllers/forms.js | 119 +++++++++++++++++- db/bans.js | 6 + db/boards.js | 4 +- db/posts.js | 80 ++++++++---- helpers/number-converter.js | 5 + models/forms/delete-post.js | 18 ++- models/forms/dismiss-report.js | 4 +- models/forms/dismissglobalreport.js | 25 ++++ models/forms/globalreportpost.js | 23 ++++ models/forms/make-post.js | 1 + models/forms/report-post.js | 4 +- models/forms/spoiler-post.js | 19 +-- models/pages/board.js | 2 +- models/pages/globalmanage.js | 4 +- server.js | 2 +- static/css/style.css | 32 ++++- views/includes/actionfooter.pug | 31 +++++ views/includes/actionfooter_globalmanage.pug | 24 ++++ ...letefooter.pug => actionfooter_manage.pug} | 6 +- views/includes/postform.pug | 49 ++++---- views/mixins/post.pug | 14 ++- views/pages/board.pug | 2 +- views/pages/globalmanage.pug | 4 +- views/pages/manage.pug | 2 +- views/pages/thread.pug | 2 +- wipe.js | 9 ++ 26 files changed, 402 insertions(+), 89 deletions(-) create mode 100644 models/forms/dismissglobalreport.js create mode 100644 models/forms/globalreportpost.js create mode 100644 views/includes/actionfooter.pug create mode 100644 views/includes/actionfooter_globalmanage.pug rename views/includes/{deletefooter.pug => actionfooter_manage.pug} (90%) diff --git a/controllers/forms.js b/controllers/forms.js index 66d6df9a..5ebbff9f 100644 --- a/controllers/forms.js +++ b/controllers/forms.js @@ -12,7 +12,9 @@ const express = require('express') , deletePosts = require(__dirname+'/../models/forms/delete-post.js') , spoilerPosts = require(__dirname+'/../models/forms/spoiler-post.js') , reportPosts = require(__dirname+'/../models/forms/report-post.js') + , globalReportPosts = require(__dirname+'/../models/forms/globalreportpost.js') , dismissReports = require(__dirname+'/../models/forms/dismiss-report.js') + , dismissGlobalReports = require(__dirname+'/../models/forms/dismissglobalreport.js') , loginAccount = require(__dirname+'/../models/forms/login.js') , registerAccount = require(__dirname+'/../models/forms/register.js') , hasPerms = require(__dirname+'/../helpers/haspermsmiddleware.js') @@ -161,9 +163,11 @@ router.post('/board/:board/actions', Boards.exists, banCheck, numberConverter, a errors.push('Ban reason must be 50 characters or less'); } if (!(req.body.report + || req.body.global_report + || req.body.spoiler || req.body.delete || req.body.dismiss - || req.body.spoiler + || req.body.global_dismiss || req.body.ban || req.body.global_ban)) { errors.push('Invalid actions selected') @@ -191,24 +195,37 @@ router.post('/board/:board/actions', Boards.exists, banCheck, numberConverter, a const messages = []; try { + + // if getting global banned, board ban doesnt matter if (req.body.global_ban) { messages.push((await banPoster(req, res, next, null, posts))); } else if (req.body.ban) { messages.push((await banPoster(req, res, next, req.params.board, posts))); } + //ban before deleting if (req.body.delete) { messages.push((await deletePosts(req, res, next, posts))); } else { + // if it was getting deleted, we cant do any of these if (req.body.spoiler) { messages.push((await spoilerPosts(req, res, next, posts))); } + // cannot report and dismiss at same time if (req.body.report) { messages.push((await reportPosts(req, res, next))); } else if (req.body.dismiss) { messages.push((await dismissReports(req, res, next))); } + + // cannot report and dismiss at same time + if (req.body.global_report) { + messages.push((await globalReportPosts(req, res, next, posts))); + } else if (req.body.global_dismiss) { + messages.push((await dismissGlobalReports(req, res, next, posts))); + } } + } catch (err) { //something not right if (err.status) { @@ -268,13 +285,111 @@ router.post('/board/:board/unban', Boards.exists, banCheck, hasPerms, numberConv router.post('/global/actions', hasPerms, numberConverter, async(req, res, next) => { - //TODO + const errors = []; + + if (!req.body.globalcheckedposts || req.body.globalcheckedposts.length === 0 || req.body.globalcheckedposts.length > 10) { + errors.push('Must select 1-10 posts') + } + if (req.body.ban_reason && req.body.ban_reason.length > 50) { + errors.push('Ban reason must be 50 characters or less'); + } + if (!(req.body.spoiler + || req.body.delete + || req.body.global_dismiss + || req.body.global_ban)) { + errors.push('Invalid actions selected') + } + + if (errors.length > 0) { + return res.status(400).render('message', { + 'title': 'Bad request', + 'errors': errors, + 'redirect': '/globalmanage' + }) + } + + const posts = await Posts.globalGetPosts(req.body.globalcheckedposts, true); + if (!posts || posts.length === 0) { + return res.status(404).render('message', { + 'title': 'Not found', + 'errors': 'Selected posts not found', + 'redirect': '/globalmanage' + }) + } + + const messages = []; + try { + + if (req.body.global_ban) { + messages.push((await banPoster(req, res, next, null, posts))); + } + + //ban before deleting + if (req.body.delete) { + messages.push((await deletePosts(req, res, next, posts))); + } else { + // if it was getting deleted, we cant do any of these + if (req.body.spoiler) { + messages.push((await spoilerPosts(req, res, next, posts))); + } + if (req.body.global_dismiss) { + messages.push((await dismissGlobalReports(req, res, next, posts))); + } + } + + } catch (err) { + //something not right + if (err.status) { + // return out special error + return res.status(err.status).render('message', err.message); + } + //some other error, use regular error handler + return next(err); + } + + return res.render('message', { + 'title': 'Success', + 'messages': messages, + 'redirect': `/${req.params.board}` + }); }); router.post('/global/unban', hasPerms, numberConverter, async(req, res, next) => { //TODO + const errors = []; + + if (!req.body.checkedbans || req.body.checkedbans.length === 0 || req.body.checkedbans.length > 10) { + errors.push('Must select 1-10 bans') + } + + if (errors.length > 0) { + return res.status(400).render('message', { + 'title': 'Bad request', + 'errors': errors, + 'redirect': `/globalmanage` + }); + } + + const messages = []; + try { + messages.push((await removeBans(req, res, next))); + } catch (err) { + //something not right + if (err.status) { + // return out special error + return res.status(err.status).render('message', err.message); + } + //some other error, use regular error handler + return next(err); + } + + return res.render('message', { + 'title': 'Success', + 'messages': messages, + 'redirect': `/globalmanage` + }); }); diff --git a/db/bans.js b/db/bans.js index 8977968f..22fd57b1 100644 --- a/db/bans.js +++ b/db/bans.js @@ -30,6 +30,12 @@ module.exports = { return db.find({}).toArray(); }, + getGlobalBans: () => { + return db.find({ + 'board': null + }).toArray(); + }, + getBoardBans: (board) => { return db.find({ 'board': board, diff --git a/db/boards.js b/db/boards.js index b242bd28..81ee74a6 100644 --- a/db/boards.js +++ b/db/boards.js @@ -33,9 +33,9 @@ module.exports = { exists: async (req, res, next) => { - const board = await module.exports.findOne(req.params.board) + const board = await module.exports.findOne(req.params.board); if (!board) { - return res.status(404).render('404') + return res.status(404).render('404'); } res.locals.board = board; // can acces this in views or next route handlers next(); diff --git a/db/posts.js b/db/posts.js index a0788c6f..a8137378 100644 --- a/db/posts.js +++ b/db/posts.js @@ -40,7 +40,7 @@ module.exports = { } }).sort({ '_id': -1 - }).limit(3).toArray(); + }).limit(5).toArray(); thread.replies = replies.reverse(); })); @@ -172,6 +172,15 @@ module.exports = { }, + //takes array "ids" of mongo ids to get posts from any board + globalGetPosts: (ids) => { + return db.find({ + '_id': { + '$in': ids + }, + }).toArray(); + }, + insertOne: async (board, data) => { // bump thread if name not sage @@ -213,6 +222,27 @@ module.exports = { }); }, + globalReportMany: (ids, report) => { + return db.updateMany({ + '_id': { + '$in': ids + }, + }, { + '$push': { + 'globalreports': report + } + }); + }, + + getReports: (board) => { + return db.find({ + 'reports.0': { + '$exists': true + }, + 'board': board + }).toArray(); + }, + dismissReports: (board, ids) => { return db.updateMany({ 'postId': { @@ -226,58 +256,60 @@ module.exports = { }); }, - getReports: (board) => { + getGlobalReports: () => { return db.find({ - 'reports.0': { + 'globalreports.0': { '$exists': true - }, - 'board': board + } }, { 'projection': { 'salt': 0, 'password': 0, 'ip': 0, + 'reports': 0, } }).toArray(); }, - getAllReports: () => { - return db.find({ - 'reports.0': { - '$exists': true + dismissGlobalReports: (ids) => { + return db.updateMany({ + '_id': { + '$in': ids + }, + }, { + '$set': { + 'globalreports': [] } - }).toArray(); + }); }, deleteOne: (board, options) => { return db.deleteOne(options); }, - deleteMany: (board, ids) => { + deleteMany: (ids) => { return db.deleteMany({ - 'postId': { + '_id': { '$in': ids - }, - 'board': board + } }); }, - spoilerMany: (board, ids) => { + spoilerMany: (ids) => { - return db.updateMany({ - 'postId': { - '$in': ids - }, - 'board': board - }, { - '$set': { + return db.updateMany({ + '_id': { + '$in': ids + } + }, { + '$set': { 'spoiler': true } - }); + }); - }, + }, deleteAll: (board) => { return db.deleteMany({ diff --git a/helpers/number-converter.js b/helpers/number-converter.js index 6604e1ad..795cf377 100644 --- a/helpers/number-converter.js +++ b/helpers/number-converter.js @@ -1,5 +1,7 @@ 'use strict'; +const Mongo = require(__dirname+'/../db/db.js'); + module.exports = (req, res, next) => { //for body @@ -10,6 +12,9 @@ module.exports = (req, res, next) => { //syntax tries to convert all string to number req.body.checkedposts = req.body.checkedposts.map(Number); } + if (req.body.globalcheckedposts) { + req.body.globalcheckedposts = req.body.globalcheckedposts.map(Mongo.ObjectId) + } //and for params if (req.params.id) { diff --git a/models/forms/delete-post.js b/models/forms/delete-post.js index 85e08b00..c8552b82 100644 --- a/models/forms/delete-post.js +++ b/models/forms/delete-post.js @@ -6,6 +6,7 @@ const path = require('path') , unlink = util.promisify(fs.unlink) , uploadDirectory = require(__dirname+'/../../helpers/uploadDirectory.js') , hasPerms = require(__dirname+'/../../helpers/hasperms.js') + , Mongo = require(__dirname+'/../../db/db.js') , Posts = require(__dirname+'/../../db/posts.js'); module.exports = async (req, res, next, checkedPosts) => { @@ -33,12 +34,18 @@ module.exports = async (req, res, next, checkedPosts) => { } } - const threadIds = posts.filter(x => x.thread == null).map(x => x.postId); + //filter to threads, then get the board and thread for each + const boardThreads = posts.filter(x => x.thread == null).map(x => { + return { + board: x.board, + thread: x.postId + }; + }); //get posts from all threads let threadPosts = [] - await Promise.all(threadIds.map(async id => { - const currentThreadPosts = await Posts.getThreadPosts(req.params.board, id); + await Promise.all(boardThreads.map(async data => { + const currentThreadPosts = await Posts.getThreadPosts(data.board, data.thread); threadPosts = threadPosts.concat(currentThreadPosts); return; })) @@ -47,7 +54,8 @@ module.exports = async (req, res, next, checkedPosts) => { const allPosts = posts.concat(threadPosts) //delete posts from DB - const deletedPosts = await Posts.deleteMany(req.params.board, allPosts.map(x => x.postId)).then(result => result.deletedCount); + const postMongoIds = allPosts.map(post => Mongo.ObjectId(post._id)) + const deletedPosts = await Posts.deleteMany(postMongoIds).then(result => result.deletedCount); //get filenames from all the posts let fileNames = []; @@ -65,6 +73,6 @@ module.exports = async (req, res, next, checkedPosts) => { })); //hooray! - return `Deleted ${threadIds.length} threads and ${deletedPosts-threadIds.length} posts` + return `Deleted ${boardThreads.length} threads and ${deletedPosts-boardThreads.length} posts` } diff --git a/models/forms/dismiss-report.js b/models/forms/dismiss-report.js index fa4d1cd5..4fac5fb0 100644 --- a/models/forms/dismiss-report.js +++ b/models/forms/dismiss-report.js @@ -16,8 +16,8 @@ module.exports = async (req, res, next) => { }; } - await Posts.dismissReports(req.params.board, req.body.checkedposts); + const dismissedReports = await Posts.dismissReports(req.params.board, req.body.checkedposts).then(result => result.modifiedCount); - return `Dismissed report(s) successfully`; + return `Dismissed ${dismissedReports} reports successfully`; } diff --git a/models/forms/dismissglobalreport.js b/models/forms/dismissglobalreport.js new file mode 100644 index 00000000..3fbb1b2a --- /dev/null +++ b/models/forms/dismissglobalreport.js @@ -0,0 +1,25 @@ +'use strict'; + +const Mongo = require(__dirname+'/../../db/db.js') + , Posts = require(__dirname+'/../../db/posts.js') + , hasPerms = require(__dirname+'/../../helpers/hasperms.js'); + +module.exports = async (req, res, next, posts) => { + + if (!hasPerms(req, res)) { + throw { + 'status': 403, + 'message': { + 'title': 'Forbidden', + 'message': 'You are not authorised to dismiss global reports.', + 'redirect': '/' + } + }; + } + + const postMongoIds = posts.map(post => Mongo.ObjectId(post._id)) + const dismissedCount = await Posts.dismissGlobalReports(postMongoIds).then(result => result.modifiedCount); + + return `Dismissed ${dismissedCount} reports successfully`; + +} diff --git a/models/forms/globalreportpost.js b/models/forms/globalreportpost.js new file mode 100644 index 00000000..278f312c --- /dev/null +++ b/models/forms/globalreportpost.js @@ -0,0 +1,23 @@ +'use strict'; + +const Mongo = require(__dirname+'/../../db/db.js') + , Posts = require(__dirname+'/../../db/posts.js'); + +module.exports = async (req, res, next, posts) => { + + const ip = req.headers['x-real-ip'] || req.connection.remoteAddress; + const report = { + 'reason': req.body.report_reason, + 'date': new Date(), + 'ip': ip + } + + const ids = posts.map(p => Mongo.ObjectId(p._id)) + + //push the report to all checked posts + const reportedPosts = await Posts.globalReportMany(ids, report).then(result => result.modifiedCount); + + //hooray! + return `Global reported ${reportedPosts} posts successfully` + +} diff --git a/models/forms/make-post.js b/models/forms/make-post.js index 9839e99c..6ebd9709 100644 --- a/models/forms/make-post.js +++ b/models/forms/make-post.js @@ -168,6 +168,7 @@ module.exports = async (req, res, next, numFiles) => { 'files': files, 'salt': !req.body.thread ? salt : '', 'reports': [], + 'globalreports': [], 'spoiler': req.body.spoiler ? true : false, }; diff --git a/models/forms/report-post.js b/models/forms/report-post.js index c2866e35..49bb7efb 100644 --- a/models/forms/report-post.js +++ b/models/forms/report-post.js @@ -12,9 +12,9 @@ module.exports = async (req, res, next) => { } //push the report to all checked posts - await Posts.reportMany(req.params.board, req.body.checkedposts, report); + const reportedCount = await Posts.reportMany(req.params.board, req.body.checkedposts, report).then(result => result.modifiedCount); //hooray! - return `Reported post(s) successfully` + return `Reported ${reportedCount} posts successfully` } diff --git a/models/forms/spoiler-post.js b/models/forms/spoiler-post.js index f819ba2a..6dcabb93 100644 --- a/models/forms/spoiler-post.js +++ b/models/forms/spoiler-post.js @@ -6,6 +6,7 @@ const path = require('path') , unlink = util.promisify(fs.unlink) , uploadDirectory = require(__dirname+'/../../helpers/uploadDirectory.js') , hasPerms = require(__dirname+'/../../helpers/hasperms.js') + , Mongo = require(__dirname+'/../../db/db.js') , Posts = require(__dirname+'/../../db/posts.js'); module.exports = async (req, res, next, checkedPosts) => { @@ -13,17 +14,6 @@ module.exports = async (req, res, next, checkedPosts) => { //get all posts that were checked let posts = checkedPosts; - if (!posts || posts.length === 0) { - throw { - 'status': 400, - 'message': { - 'title': 'Bad requests', - 'message': 'No posts found', - 'redirect': `/${req.params.board}` - } - }; - } - //if user is not logged in OR if lgoged in but not authed, filter the posts by passwords that are not null if (!hasPerms(req, res)) { @@ -39,7 +29,7 @@ module.exports = async (req, res, next, checkedPosts) => { 'message': { 'title': 'Forbidden', 'message': 'Password did not match any selected posts', - 'redirect': `/${req.params.board}` + 'redirect': '/' } }; } @@ -56,13 +46,14 @@ module.exports = async (req, res, next, checkedPosts) => { 'message': { 'title': 'Conflict', 'message': 'Posts already spoilered', - 'redirect': `/${req.params.board}` + 'redirect': '/' } }; } // spoiler posts - const spoileredPosts = await Posts.spoilerMany(req.params.board, posts.map(x => x.postId)).then(result => result.modifiedCount); + const postMongoIds = posts.map(post => Mongo.ObjectId(post._id)); + const spoileredPosts = await Posts.spoilerMany(postMongoIds).then(result => result.modifiedCount); //hooray! return `Spoilered ${spoileredPosts} posts` diff --git a/models/pages/board.js b/models/pages/board.js index fa82182c..649ebae0 100644 --- a/models/pages/board.js +++ b/models/pages/board.js @@ -8,7 +8,7 @@ module.exports = async (req, res, next) => { let threads; let pages; try { - pages = Math.ceil((await Posts.getPages(req.params.board)) / 10); + pages = Math.ceil((await Posts.getPages(req.params.board)) / 10) || 1; if (page > pages) { return next(); } diff --git a/models/pages/globalmanage.js b/models/pages/globalmanage.js index 97daf7ae..f0dfa034 100644 --- a/models/pages/globalmanage.js +++ b/models/pages/globalmanage.js @@ -8,8 +8,8 @@ module.exports = async (req, res, next) => { let reports; let bans; try { - reports = await Posts.getAllReports(); - bans = await Bans.getAllBans(); + reports = await Posts.getGlobalReports(); + bans = await Bans.getGlobalBans(); } catch (err) { return next(err) } diff --git a/server.js b/server.js index 932e84b2..0bf0192d 100644 --- a/server.js +++ b/server.js @@ -53,7 +53,7 @@ const express = require('express') // use pug view engine app.set('view engine', 'pug'); app.set('views', path.join(__dirname, 'views/pages')); -// app.enable('view cache'); + app.enable('view cache'); // static files app.use('/css', express.static(__dirname + '/static/css')); diff --git a/static/css/style.css b/static/css/style.css index 04df4a8d..15e4b914 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -152,10 +152,40 @@ input, textarea { margin: 2px 0; } +.toggle-label:hover { + box-shadow: inset 0 0 100px 100px rgba(255,255,255,.25); +} + +.toggle-label { + background: #D6DAF0; + padding: 10px; + border-radius: 3px; + text-align: center; + border: 1px solid lightgray; + max-width: 100%; + box-sizing: border-box; + /*-webkit-appearance: button; + -moz-appearance: button; + appearance: button;*/ +} + +.toggle { + display: none; +} + +.toggle:checked + .form-post { + display: flex; +} + .form-post { display: flex; flex-direction: column; max-width: 100%; + margin-top: 10px; +} + +.togglable { + display: none; } .user-id { @@ -302,7 +332,7 @@ input textarea { } .nav-item:hover { - box-shadow: inset 0 0 100px 100px rgba(255,255,255,.1); + box-shadow: inset 0 0 100px 100px rgba(255,255,255,.25); } .footer { diff --git a/views/includes/actionfooter.pug b/views/includes/actionfooter.pug new file mode 100644 index 00000000..ec26a9b0 --- /dev/null +++ b/views/includes/actionfooter.pug @@ -0,0 +1,31 @@ +.action-wrapper + .actions Actions: + label + input.post-check(type='checkbox', name='delete' value=1) + | Delete + label + input.post-check(type='checkbox', name='spoiler' value=1) + | Spoiler Images + label + input#password(type='text', name='password', placeholder='post password' autocomplete='off') + label + input.post-check(type='checkbox', name='report' value=1) + | Report + label + input.post-check(type='checkbox', name='global_report' value=1) + | Global Report + label + input#report(type='text', name='report_reason', placeholder='report reason' autocomplete='off') + .actions Mod Actions: + label + input.post-check(type='checkbox', name='ban' value=1) + | Ban Poster + label + input.post-check(type='checkbox', name='global_ban' value=1) + | Global Ban Poster + label + input.post-check(type='checkbox', name='preserve_post' value=1) + | Show Post In Ban + label + input#report(type='text', name='ban_reason', placeholder='ban reason' autocomplete='off') + input(type='submit', value='submit') diff --git a/views/includes/actionfooter_globalmanage.pug b/views/includes/actionfooter_globalmanage.pug new file mode 100644 index 00000000..1d754996 --- /dev/null +++ b/views/includes/actionfooter_globalmanage.pug @@ -0,0 +1,24 @@ +.action-wrapper + .actions Actions: + label + input.post-check(type='checkbox', name='delete' value=1) + | Delete + label + input.post-check(type='checkbox', name='spoiler' value=1) + | Spoiler Images + label + input#report(type='text', name='report_reason', placeholder='report reason' autocomplete='off') + .actions Mod Actions: + label + input.post-check(type='checkbox', name='global_dismiss' value=1) + | Dismiss Global Reports + label + input.post-check(type='checkbox', name='global_ban' value=1) + | Global Ban Poster + label + input.post-check(type='checkbox', name='preserve_post' value=1) + | Show Post In Ban + label + input#report(type='text', name='ban_reason', placeholder='ban reason' autocomplete='off') + input(type='submit', value='submit') + diff --git a/views/includes/deletefooter.pug b/views/includes/actionfooter_manage.pug similarity index 90% rename from views/includes/deletefooter.pug rename to views/includes/actionfooter_manage.pug index b86e6ddf..7cbe4f2d 100644 --- a/views/includes/deletefooter.pug +++ b/views/includes/actionfooter_manage.pug @@ -2,15 +2,15 @@ .actions Actions: label input.post-check(type='checkbox', name='delete' value=1) - | Delete Post + | Delete label input.post-check(type='checkbox', name='spoiler' value=1) | Spoiler Images label input#password(type='text', name='password', placeholder='post password' autocomplete='off') label - input.post-check(type='checkbox', name='report' value=1) - | Report Post + input.post-check(type='checkbox', name='global_report' value=1) + | Global Report label input#report(type='text', name='report_reason', placeholder='report reason' autocomplete='off') .actions Mod Actions: diff --git a/views/includes/postform.pug b/views/includes/postform.pug index 4384dd1d..7277af5e 100644 --- a/views/includes/postform.pug +++ b/views/includes/postform.pug @@ -1,24 +1,29 @@ section.form-wrapper - form.form-post(action=`/forms/board/${board._id}/post`, enctype='multipart/form-data', method='POST') + label.toggle-label Show Post Form + input.toggle(type='checkbox') + form.form-post.togglable(action=`/forms/board/${board._id}/post`, enctype='multipart/form-data', method='POST') + + input(type='hidden' name='_csrf' value=csrf) + + input(type='hidden' name='thread' value=thread != null ? thread.postId : null) + + input#title(type='text', name='subject', placeholder='subject' autocomplete='off' maxlength='50') + + input#name(type='text', name='name', placeholder='name' autocomplete='off' maxlength='50') + + input#name(type='text', name='email', placeholder='email' autocomplete='off' maxlength='50') + + input#password(type='password', name='password', placeholder='post password' autocomplete='off' maxlength='50') + + textarea#message(name='message', rows='8', cols='50', placeholder='message' autocomplete='off' maxlength='2000') + + span + input#file(type='file', name='file' multiple) + label + input.post-check#spoiler(type='checkbox', name='spoiler', value='true') + | Spoiler + + input(type='submit', value='submit') + + - input(type='hidden' name='_csrf' value=csrf) - - input(type='hidden' name='thread' value=thread != null ? thread.postId : null) - - input#title(type='text', name='subject', placeholder='subject' autocomplete='off' maxlength='50') - - input#name(type='text', name='name', placeholder='name' autocomplete='off' maxlength='50') - - input#name(type='text', name='email', placeholder='email' autocomplete='off' maxlength='50') - - input#password(type='password', name='password', placeholder='post password' autocomplete='off' maxlength='50') - - textarea#message(name='message', rows='8', cols='50', placeholder='message' autocomplete='off' maxlength='2000') - - span - input#file(type='file', name='file' multiple) - label - input.post-check#spoiler(type='checkbox', name='spoiler', value='true') - | Spoiler - - input(type='submit', value='submit') diff --git a/views/mixins/post.pug b/views/mixins/post.pug index f3a8ea49..a4d31ba8 100644 --- a/views/mixins/post.pug +++ b/views/mixins/post.pug @@ -1,8 +1,11 @@ -mixin post(post, truncate, showreports) +mixin post(post, truncate, manage, globalmanage) article(id=post.postId class='post-container '+(post.thread ? '' : 'op')) header.post-info span - input.post-check(type='checkbox', name='checkedposts[]' value=post.postId) + if globalmanage + input.post-check(type='checkbox', name='globalcheckedposts[]' value=post._id) + else + input.post-check(type='checkbox', name='checkedposts[]' value=post.postId) if post.subject span.post-subject #{post.subject} if post.email @@ -45,8 +48,13 @@ mixin post(post, truncate, showreports) blockquote.post-message !{post.message} else blockquote.post-message !{post.message} - if showreports && post.reports + if manage === true each report in post.reports .reports.post-container span Date: #{report.date.toLocaleString()} span Reason: #{report.reason} + if globalmanage === true + each report in post.globalreports + .reports.post-container + span Date: #{report.date.toLocaleString()} + span Reason: #{report.reason} diff --git a/views/pages/board.pug b/views/pages/board.pug index 1fbf0369..daac336b 100644 --- a/views/pages/board.pug +++ b/views/pages/board.pug @@ -24,4 +24,4 @@ block content hr(size=1) include ../includes/pages.pug hr(size=1) - include ../includes/deletefooter.pug + include ../includes/actionfooter.pug diff --git a/views/pages/globalmanage.pug b/views/pages/globalmanage.pug index 36eb1444..faedc003 100644 --- a/views/pages/globalmanage.pug +++ b/views/pages/globalmanage.pug @@ -16,9 +16,9 @@ block content else for report in reports section.thread - +post(report, false, true) + +post(report, false, false, true) hr(size=1) - include ../includes/deletefooter.pug + include ../includes/actionfooter_globalmanage.pug hr(size=1) h4 All Bans: form(action=`/forms/global/unban` method='POST' enctype='application/x-www-form-urlencoded') diff --git a/views/pages/manage.pug b/views/pages/manage.pug index e0816182..f8236717 100644 --- a/views/pages/manage.pug +++ b/views/pages/manage.pug @@ -18,7 +18,7 @@ block content section.thread +post(report, false, true) hr(size=1) - include ../includes/deletefooter.pug + include ../includes/actionfooter_manage.pug hr(size=1) h4 Bans: form(action=`/forms/board/${board._id}/unban` method='POST' enctype='application/x-www-form-urlencoded') diff --git a/views/pages/thread.pug b/views/pages/thread.pug index 001ad2cd..465040f2 100644 --- a/views/pages/thread.pug +++ b/views/pages/thread.pug @@ -21,4 +21,4 @@ block content for post in thread.replies +post(post) hr(size=1) - include ../includes/deletefooter.pug + include ../includes/actionfooter.pug diff --git a/wipe.js b/wipe.js index 220ce56c..b9e39de3 100644 --- a/wipe.js +++ b/wipe.js @@ -66,6 +66,15 @@ const Mongo = require(__dirname+'/db/db.js') } } }); + await Posts.db.createIndex({ + 'globalreports.0': 1 + }, { + partialFilterExpression: { + 'globalreports.0': { + '$exists': true + } + } + }); await readdir('static/img/').then(async files => { await Promise.all(files.map(async file => { if (file != 'spoiler.png') From 54fac87e99c63a0b047fb4d3b5dd25099f98a253 Mon Sep 17 00:00:00 2001 From: fatchan Date: Tue, 16 Apr 2019 16:34:12 +0000 Subject: [PATCH 17/20] capturing groups to regexes, and add code block regex --- helpers/markdown.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/helpers/markdown.js b/helpers/markdown.js index 8e9435d9..e056568b 100644 --- a/helpers/markdown.js +++ b/helpers/markdown.js @@ -1,25 +1,24 @@ 'use strict'; const Posts = require(__dirname+'/../db/posts.js') - , greentextRegex = /^>[^>].+/gm - , redtextRegex = /^<[^<].+/gm - , boldRegex = /==.+==/gm - , italicRegex = /__.+__/gm + , greentextRegex = /^>([^>].+)/gm + , redtextRegex = /^<([^<].+)/gm + , boldRegex = /==(.+)==/gm + , italicRegex = /__(.+)__/gm , linkRegex = /https?\:\/\/[^\s]+/g - , spoilerRegex = /\|.+\|/gm; + , spoilerRegex = /\|\|(.+)\|\|/gm + , codeRegex = /^```\s([\s\S]+)\s```/gm; module.exports = (board, thread, text) => { //redtext - text = text.replace(redtextRegex, (match) => { - const red = match.substring(1); - return `<${red}`; + text = text.replace(redtextRegex, (match, redtext) => { + return `<${redtext}`; }); //greentext - text = text.replace(greentextRegex, (match) => { - const green = match.substring(1); - return `>${green}`; + text = text.replace(greentextRegex, (match, greentext) => { + return `>${greentext}`; }); //links @@ -28,23 +27,24 @@ module.exports = (board, thread, text) => { }); //bold - text = text.replace(boldRegex, (match) => { - const bold = match.substring(2, match.length-2); + text = text.replace(boldRegex, (match, bold) => { return `${bold}`; }); //italic - text = text.replace(italicRegex, (match) => { - const italic = match.substring(2, match.length-2); + text = text.replace(italicRegex, (match, italic) => { return `${italic}`; }); //spoilers - text = text.replace(spoilerRegex, (match) => { - const spoiler = match.substring(1, match.length-1); + text = text.replace(spoilerRegex, (match, spoiler) => { return `${spoiler}`; }); + text = text.replace(codeRegex, (match, code) => { + return `${code.trim()}`; + }); + return text; } From 91e828e768d142bafa34246d59d38a9576fb2246 Mon Sep 17 00:00:00 2001 From: fatchan Date: Wed, 17 Apr 2019 05:04:23 +0000 Subject: [PATCH 18/20] delete thumbs properly --- models/forms/delete-post.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/forms/delete-post.js b/models/forms/delete-post.js index c8552b82..1087d19e 100644 --- a/models/forms/delete-post.js +++ b/models/forms/delete-post.js @@ -68,7 +68,7 @@ module.exports = async (req, res, next, checkedPosts) => { //dont question it. return Promise.all([ unlink(uploadDirectory + filename), - unlink(uploadDirectory + 'thumb-' + filename) + unlink(`${uploadDirectory}thumb-${filename.split('.')[0]}.png`) ]) })); From 9792f123d5914bcd7ac4b9fb51c1dec122379c64 Mon Sep 17 00:00:00 2001 From: fatchan Date: Wed, 17 Apr 2019 05:04:56 +0000 Subject: [PATCH 19/20] put boards in a map --- db/boards.js | 16 +++++++++++++--- models/forms/make-post.js | 3 +-- server.js | 2 ++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/db/boards.js b/db/boards.js index 81ee74a6..cbe09a30 100644 --- a/db/boards.js +++ b/db/boards.js @@ -1,7 +1,8 @@ 'use strict'; const Mongo = require(__dirname+'/db.js') - , db = Mongo.client.db('jschan'); + , db = Mongo.client.db('jschan') + , boardCache = new Map(); module.exports = { @@ -11,7 +12,7 @@ module.exports = { return db.collection('boards').findOne({ '_id': name }); }, - find: (name) => { + find: () => { return db.collection('boards').find({}).toArray(); }, @@ -31,9 +32,18 @@ module.exports = { return db.collection('boards').deleteMany({}); }, + cache: async () => { + const boards = await module.exports.find(); + for (let i = 0; i < boards.length; i++) { + const board = boards[i]; + boardCache.set(board._id, board); + } + }, + exists: async (req, res, next) => { - const board = await module.exports.findOne(req.params.board); + //const board = await module.exports.findOne(req.params.board); + const board = boardCache.get(req.params.board); if (!board) { return res.status(404).render('404'); } diff --git a/models/forms/make-post.js b/models/forms/make-post.js index 6ebd9709..30e22da9 100644 --- a/models/forms/make-post.js +++ b/models/forms/make-post.js @@ -12,7 +12,7 @@ const uuidv4 = require('uuid/v4') , simpleMarkdown = require(__dirname+'/../../helpers/markdown.js') , sanitize = require('sanitize-html') , sanitizeOptions = { - allowedTags: [ 'span', 'a', 'code', 'em', 'strong' ], + allowedTags: [ 'span', 'a', 'em', 'strong' ], allowedAttributes: { 'a': [ 'href', 'class' ], 'span': [ 'class' ] @@ -115,7 +115,6 @@ module.exports = async (req, res, next, numFiles) => { if (Array.isArray(processedFile.geometryString)) { processedFile.geometryString = processedFile.geometryString[0]; } - files.push(processedFile); } catch (err) { diff --git a/server.js b/server.js index 0bf0192d..a91533fc 100644 --- a/server.js +++ b/server.js @@ -20,6 +20,8 @@ const express = require('express') // let db connect await Mongo.connect(); + const Boards = require(__dirname+'/db/boards.js'); + await Boards.cache(); // parse forms and allow file uploads app.use(bodyParser.urlencoded({extended: true})); From 21e6ce8874a0e4de2b9fe04265945d7e9a282eea Mon Sep 17 00:00:00 2001 From: fatchan Date: Wed, 17 Apr 2019 05:05:09 +0000 Subject: [PATCH 20/20] video thumbnails maintain aspect ratio --- helpers/files/video-thumbnail.js | 2 +- static/css/style.css | 10 +++++++++- views/mixins/catalogtile.pug | 5 ++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/helpers/files/video-thumbnail.js b/helpers/files/video-thumbnail.js index 1633b945..91ad1572 100644 --- a/helpers/files/video-thumbnail.js +++ b/helpers/files/video-thumbnail.js @@ -14,7 +14,7 @@ module.exports = (filename) => { count: 1, filename: `thumb-${filename.split('.')[0]}.png`, folder: uploadDirectory, - size: '128x128' + size: '128x?' }); }); diff --git a/static/css/style.css b/static/css/style.css index 15e4b914..04af046d 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -9,6 +9,14 @@ body { margin: 0; } +.code { + border-left: 10px solid #B7C5D9; + display: block; + padding-left: 5px; + font-family: monospace; + margin: 0.5em 0; +} + .pages a { text-decoration: none; } @@ -101,7 +109,7 @@ blockquote a { } blockquote { - /*word-break: break-all;*/ + word-break: break-all; white-space: pre-wrap; } diff --git a/views/mixins/catalogtile.pug b/views/mixins/catalogtile.pug index 462db87b..1fc4588f 100644 --- a/views/mixins/catalogtile.pug +++ b/views/mixins/catalogtile.pug @@ -4,7 +4,10 @@ mixin catalogtile(board, post, truncate) if post.files.length > 0 .post-file-src a(href=`/${board._id}/thread/${post.postId}#${post.postId}`) - object.catalog-thumb(data=`/img/thumb-${post.files[0].filename.split('.')[0]}.png` width='64' height='64') + if post.spoiler + object(data='/img/spoiler.png' width='64' height='64') + else + object.catalog-thumb(data=`/img/thumb-${post.files[0].filename.split('.')[0]}.png` width='64' height='64') header.post-info if post.subject span: a.no-decoration.post-subject(href=`/${board._id}/thread/${post.postId}#${post.postId}`) #{post.subject}