mirror of https://gitgud.io/fatchan/jschan.git
Merge pull request #13 from fatchan/bans
add bans + numerous other improvementsmerge-requests/208/head
commit
530dd7d1c5
64 changed files with 1257 additions and 590 deletions
@ -1,7 +1,7 @@ |
|||||||
|
|
||||||
'use strict'; |
'use strict'; |
||||||
|
|
||||||
const Mongo = require(__dirname+'/../helpers/db.js') |
const Mongo = require(__dirname+'/db.js') |
||||||
, db = Mongo.client.db('jschan').collection('accounts') |
, db = Mongo.client.db('jschan').collection('accounts') |
||||||
, bcrypt = require('bcrypt'); |
, bcrypt = require('bcrypt'); |
||||||
|
|
@ -0,0 +1,66 @@ |
|||||||
|
|
||||||
|
'use strict'; |
||||||
|
|
||||||
|
const Mongo = require(__dirname+'/db.js') |
||||||
|
, db = Mongo.client.db('jschan').collection('bans'); |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
|
||||||
|
db, |
||||||
|
|
||||||
|
find: (ip, board) => { |
||||||
|
return db.find({ |
||||||
|
'ip': ip, |
||||||
|
'board': { |
||||||
|
'$in': [board, null] |
||||||
|
} |
||||||
|
}).toArray(); |
||||||
|
}, |
||||||
|
|
||||||
|
findMany: (board, ids) => { |
||||||
|
return db.find({ |
||||||
|
'_id': { |
||||||
|
'$in': ids |
||||||
|
}, |
||||||
|
'board': board |
||||||
|
}).toArray(); |
||||||
|
}, |
||||||
|
|
||||||
|
getAllBans: () => { |
||||||
|
return db.find({}).toArray(); |
||||||
|
}, |
||||||
|
|
||||||
|
getGlobalBans: () => { |
||||||
|
return db.find({ |
||||||
|
'board': null |
||||||
|
}).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); |
||||||
|
}, |
||||||
|
|
||||||
|
insertMany: (bans) => { |
||||||
|
return db.insertMany(bans); |
||||||
|
}, |
||||||
|
|
||||||
|
deleteAll: () => { |
||||||
|
return db.deleteMany({}); |
||||||
|
}, |
||||||
|
|
||||||
|
} |
@ -1,6 +1,6 @@ |
|||||||
'use strict'; |
'use strict'; |
||||||
|
|
||||||
const Mongo = require(__dirname+'/../helpers/db.js') |
const Mongo = require(__dirname+'/db.js') |
||||||
, db = Mongo.client.db('jschan').collection('tripcodes'); |
, db = Mongo.client.db('jschan').collection('tripcodes'); |
||||||
|
|
||||||
module.exports = { |
module.exports = { |
@ -0,0 +1,20 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
const Bans = require(__dirname+'/../db/bans.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 ? res.locals.board._id : null); |
||||||
|
if (bans && bans.length > 0) { |
||||||
|
//TODO: show posts banned for, expiry, etc
|
||||||
|
return res.status(403).render('ban', { |
||||||
|
bans: bans |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
next(); |
||||||
|
|
||||||
|
} |
@ -1,6 +0,0 @@ |
|||||||
'use strict'; |
|
||||||
|
|
||||||
module.exports = (req, res, next) => { |
|
||||||
if (req.session.authenticated === true) return next() |
|
||||||
res.redirect('/login') |
|
||||||
} |
|
@ -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
|
|
||||||
} |
|
@ -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
|
||||||
|
) |
||||||
|
) |
||||||
|
) |
||||||
|
} |
@ -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(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
module.exports = (req, res, next) => { |
||||||
|
if (req.session.authenticated === true) { |
||||||
|
return next(); |
||||||
|
} |
||||||
|
res.redirect('/login'); |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
const Posts = require(__dirname+'/../db/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++; |
||||||
|
} |
||||||
|
|
||||||
|
//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 `<a class='quote' href='/${board}/thread/${postThreadObject[quotenum]}#${quotenum}'>>>${quotenum}</a>`; |
||||||
|
} |
||||||
|
return match; |
||||||
|
}); |
||||||
|
|
||||||
|
return text; |
||||||
|
|
||||||
|
} |
@ -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) |
|
||||||
} |
|
@ -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) |
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
@ -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) |
|
||||||
} |
|
@ -0,0 +1,40 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
const uploadDirectory = require(__dirname+'/../../helpers/uploadDirectory.js') |
||||||
|
, hasPerms = require(__dirname+'/../../helpers/hasperms.js') |
||||||
|
, Bans = require(__dirname+'/../../db/bans.js') |
||||||
|
, Posts = require(__dirname+'/../../db/posts.js'); |
||||||
|
|
||||||
|
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)) { |
||||||
|
throw { |
||||||
|
'status': 403, |
||||||
|
'message': { |
||||||
|
'title': 'Forbidden', |
||||||
|
'message': 'You do not have permission to issue bans', |
||||||
|
'redirect': `/${req.params.board}` |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
const bans = posts.map(post => { |
||||||
|
return { |
||||||
|
'ip': post.ip, |
||||||
|
'reason': req.body.ban_reason || 'No reason specified', |
||||||
|
'board': board, |
||||||
|
'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
|
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
const bannedIps = await Bans.insertMany(bans).then(result => result.insertedCount); |
||||||
|
|
||||||
|
return `Banned ${bannedIps} ips`; |
||||||
|
|
||||||
|
} |
@ -1,31 +1,23 @@ |
|||||||
'use strict'; |
'use strict'; |
||||||
|
|
||||||
const Posts = require(__dirname+'/../../db-models/posts.js') |
const Posts = require(__dirname+'/../../db/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)) { |
if (!hasPerms(req, res)) { |
||||||
return res.status(403).render('message', { |
throw { |
||||||
'title': 'Forbidden', |
'status': 403, |
||||||
'message': `You are not authorised to dismiss reports.`, |
'message': { |
||||||
'redirect': `/${req.params.board}` |
'title': 'Forbidden', |
||||||
}); |
'message': `You are not authorised to dismiss reports.`, |
||||||
|
'redirect': `/${req.params.board}` |
||||||
|
} |
||||||
|
}; |
||||||
} |
} |
||||||
|
|
||||||
try { |
const dismissedReports = await Posts.dismissReports(req.params.board, req.body.checkedposts).then(result => result.modifiedCount); |
||||||
//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'); |
|
||||||
} |
|
||||||
|
|
||||||
//hooray!
|
return `Dismissed ${dismissedReports} reports successfully`; |
||||||
return res.render('message', { |
|
||||||
'title': 'Success', |
|
||||||
'message': `Dismissed report(s) successfully`, |
|
||||||
'redirect': `/${req.params.board}/manage` |
|
||||||
}); |
|
||||||
|
|
||||||
} |
} |
||||||
|
@ -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`; |
||||||
|
|
||||||
|
} |
@ -1,34 +1,9 @@ |
|||||||
'use strict'; |
'use strict'; |
||||||
|
|
||||||
const uuidv4 = require('uuid/v4') |
const Posts = require(__dirname+'/../../db/posts.js'); |
||||||
, path = require('path') |
|
||||||
, Posts = require(__dirname+'/../../db-models/posts.js') |
|
||||||
|
|
||||||
module.exports = async (req, res, numFiles) => { |
module.exports = async (req, res, next) => { |
||||||
|
|
||||||
// get the post that we are trying to edit
|
throw new Error('Not implemented'); |
||||||
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'); |
|
||||||
} |
|
||||||
if (!thread || thread.thread != null) { |
|
||||||
return res.status(400).render('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
|
|
||||||
} |
|
||||||
|
|
||||||
const post = await Posts.updateOne(req.params.board, data) |
|
||||||
const successRedirect = `/${req.params.board}/thread/${req.body.thread || post.insertedId}`; |
|
||||||
|
|
||||||
return res.redirect(successRedirect); |
|
||||||
} |
} |
||||||
|
@ -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` |
||||||
|
|
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
const Bans = require(__dirname+'/../../db/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`; |
||||||
|
|
||||||
|
} |
@ -1,29 +1,20 @@ |
|||||||
'use strict'; |
'use strict'; |
||||||
|
|
||||||
const Posts = require(__dirname+'/../../db-models/posts.js'); |
const Posts = require(__dirname+'/../../db/posts.js'); |
||||||
|
|
||||||
module.exports = async (req, res) => { |
module.exports = async (req, res, next) => { |
||||||
|
|
||||||
const ip = req.headers['x-real-ip'] || req.connection.remoteAddress; |
const ip = req.headers['x-real-ip'] || req.connection.remoteAddress; |
||||||
const report = { |
const report = { |
||||||
'reason': req.body.reason, |
'reason': req.body.report_reason, |
||||||
'date': new Date(), |
'date': new Date(), |
||||||
'ip': ip |
'ip': ip |
||||||
} |
} |
||||||
|
|
||||||
try { |
//push the report to all checked posts
|
||||||
//push the report to all checked posts
|
const reportedCount = await Posts.reportMany(req.params.board, req.body.checkedposts, report).then(result => result.modifiedCount); |
||||||
await Posts.reportMany(req.params.board, req.body.checked, report); |
|
||||||
} catch (err) { |
|
||||||
console.error(err); |
|
||||||
return res.status(500).render('error'); |
|
||||||
} |
|
||||||
|
|
||||||
//hooray!
|
//hooray!
|
||||||
return res.render('message', { |
return `Reported ${reportedCount} posts successfully` |
||||||
'title': 'Success', |
|
||||||
'message': `Reported post(s) successfully`, |
|
||||||
'redirect': `/${req.params.board}` |
|
||||||
}); |
|
||||||
|
|
||||||
} |
} |
||||||
|
@ -1,23 +1,28 @@ |
|||||||
'use strict'; |
'use strict'; |
||||||
|
|
||||||
const Posts = require(__dirname+'/../../db-models/posts.js'); |
const Posts = require(__dirname+'/../../db/posts.js'); |
||||||
|
|
||||||
module.exports = async (req, res, next) => { |
module.exports = async (req, res, next) => { |
||||||
//get the recently bumped thread & preview posts
|
//get the recently bumped thread & preview posts
|
||||||
|
const page = req.query.p || 1; |
||||||
let threads; |
let threads; |
||||||
let pages; |
let pages; |
||||||
try { |
try { |
||||||
threads = await Posts.getRecent(req.params.board, req.params.page || 1); |
pages = Math.ceil((await Posts.getPages(req.params.board)) / 10) || 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) { |
} catch (err) { |
||||||
console.error(err) |
return next(err); |
||||||
return next(); |
|
||||||
} |
} |
||||||
|
|
||||||
|
|
||||||
//render the page
|
//render the page
|
||||||
res.render('board', { |
res.render('board', { |
||||||
csrf: req.csrfToken(), |
csrf: req.csrfToken(), |
||||||
threads: threads || [], |
threads: threads || [], |
||||||
pages: pages |
pages, |
||||||
|
page, |
||||||
}); |
}); |
||||||
} |
} |
||||||
|
@ -0,0 +1,24 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
const Posts = require(__dirname+'/../../db/posts.js') |
||||||
|
, Bans = require(__dirname+'/../../db/bans.js'); |
||||||
|
|
||||||
|
module.exports = async (req, res, next) => { |
||||||
|
|
||||||
|
let reports; |
||||||
|
let bans; |
||||||
|
try { |
||||||
|
reports = await Posts.getGlobalReports(); |
||||||
|
bans = await Bans.getGlobalBans(); |
||||||
|
} catch (err) { |
||||||
|
return next(err) |
||||||
|
} |
||||||
|
|
||||||
|
//render the page
|
||||||
|
res.render('globalmanage', { |
||||||
|
csrf: req.csrfToken(), |
||||||
|
reports, |
||||||
|
bans, |
||||||
|
}); |
||||||
|
|
||||||
|
} |
@ -1,10 +1,11 @@ |
|||||||
'use strict'; |
'use strict'; |
||||||
|
|
||||||
module.exports = (req, res) => { |
module.exports = (req, res, next) => { |
||||||
|
|
||||||
//render the page
|
//render the page
|
||||||
res.render('login', { |
res.render('login', { |
||||||
csrf: req.csrfToken() |
csrf: req.csrfToken(), |
||||||
|
redirect: req.query.redirect, |
||||||
}); |
}); |
||||||
|
|
||||||
} |
} |
||||||
|
@ -1,21 +1,24 @@ |
|||||||
'use strict'; |
'use strict'; |
||||||
|
|
||||||
const Posts = require(__dirname+'/../../db-models/posts.js'); |
const Posts = require(__dirname+'/../../db/posts.js') |
||||||
|
, Bans = require(__dirname+'/../../db/bans.js'); |
||||||
|
|
||||||
module.exports = async (req, res) => { |
module.exports = async (req, res, next) => { |
||||||
|
|
||||||
let posts; |
let reports; |
||||||
|
let bans; |
||||||
try { |
try { |
||||||
posts = await Posts.getReports(req.params.board); |
reports = await Posts.getReports(req.params.board); |
||||||
|
bans = await Bans.getBoardBans(req.params.board); |
||||||
} catch (err) { |
} catch (err) { |
||||||
console.error(err); |
return next(err) |
||||||
return res.status(500).render('error'); |
|
||||||
} |
} |
||||||
|
|
||||||
//render the page
|
//render the page
|
||||||
res.render('manage', { |
res.render('manage', { |
||||||
csrf: req.csrfToken(), |
csrf: req.csrfToken(), |
||||||
posts: posts |
reports, |
||||||
|
bans, |
||||||
}); |
}); |
||||||
|
|
||||||
} |
} |
||||||
|
@ -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') |
@ -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') |
||||||
|
|
@ -0,0 +1,32 @@ |
|||||||
|
.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='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='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') |
||||||
|
|
@ -1,3 +1,4 @@ |
|||||||
a.no-decoration(href=`/${board._id}`) |
section.board-header |
||||||
h1.board-title /#{board._id}/ - #{board.name} |
a.no-decoration(href=`/${board._id}`) |
||||||
h4.board-description #{board.description} |
h1.board-title /#{board._id}/ - #{board.name} |
||||||
|
h4.board-description #{board.description} |
||||||
|
@ -1,15 +0,0 @@ |
|||||||
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') |
|
@ -1,5 +1,9 @@ |
|||||||
nav.navbar |
nav.navbar |
||||||
a.nav-item(href='/') Home |
a.nav-item(href='/') Home |
||||||
a.nav-item(href='/login') Login |
|
||||||
if board |
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=`/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='/logout') Logout |
||||||
|
@ -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} |
@ -1,25 +1,29 @@ |
|||||||
section.form-wrapper |
section.form-wrapper |
||||||
form.form-post(action='/forms/board/'+board._id, 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='_csrf' value=csrf) |
||||||
|
|
||||||
input(type='hidden' name='thread' value=thread != null ? thread.postId : null) |
input(type='hidden' name='thread' value=thread != null ? thread.postId : null) |
||||||
|
|
||||||
input#title(type='text', name='subject', placeholder='subject' autocomplete='off' maxlength='50') |
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='name', placeholder='name' autocomplete='off' maxlength='50') |
||||||
|
|
||||||
input#name(type='text', name='email', placeholder='email' 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') |
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') |
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') |
||||||
|
|
||||||
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') |
|
||||||
|
|
||||||
|
@ -0,0 +1,18 @@ |
|||||||
|
include ./post.pug |
||||||
|
|
||||||
|
mixin ban(ban) |
||||||
|
.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) |
||||||
|
|
@ -0,0 +1,14 @@ |
|||||||
|
extends ../layout.pug |
||||||
|
include ../mixins/ban.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 |
||||||
|
+ban(ban) |
||||||
|
hr(size=1) |
@ -0,0 +1,35 @@ |
|||||||
|
extends ../layout.pug |
||||||
|
include ../mixins/post.pug |
||||||
|
include ../mixins/ban.pug |
||||||
|
|
||||||
|
block head |
||||||
|
title Manage |
||||||
|
|
||||||
|
block content |
||||||
|
h1.board-title Global Management |
||||||
|
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, false, true) |
||||||
|
hr(size=1) |
||||||
|
include ../includes/actionfooter_globalmanage.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') |
@ -1,35 +1,36 @@ |
|||||||
extends ../layout.pug |
extends ../layout.pug |
||||||
include ../mixins/post.pug |
include ../mixins/post.pug |
||||||
|
include ../mixins/ban.pug |
||||||
|
|
||||||
block head |
block head |
||||||
title Login |
title Manage |
||||||
|
|
||||||
block content |
block content |
||||||
include ../includes/boardheader.pug |
include ../includes/boardheader.pug |
||||||
hr(size=1) |
h4 Reports: |
||||||
h4.board-description Management Panel |
form(action=`/forms/board/${board._id}/actions` method='POST' enctype='application/x-www-form-urlencoded') |
||||||
form(action='/forms/board/'+board._id+'/posts' method='POST' enctype='application/x-www-form-urlencoded') |
|
||||||
input(type='hidden' name='_csrf' value=csrf) |
input(type='hidden' name='_csrf' value=csrf) |
||||||
if posts.length === 0 |
if reports.length === 0 |
||||||
p No reports. |
p No reports. |
||||||
hr(size=1) |
hr(size=1) |
||||||
for post in posts |
else |
||||||
section.thread |
for report in reports |
||||||
+post(board, post) |
section.thread |
||||||
|
+post(report, false, true) |
||||||
|
hr(size=1) |
||||||
|
include ../includes/actionfooter_manage.pug |
||||||
hr(size=1) |
hr(size=1) |
||||||
section.action-wrapper |
h4 Bans: |
||||||
span |
form(action=`/forms/board/${board._id}/unban` method='POST' enctype='application/x-www-form-urlencoded') |
||||||
label |
input(type='hidden' name='_csrf' value=csrf) |
||||||
input.post-check(type='checkbox', name='delete' value=1) |
if bans.length === 0 |
||||||
| Delete |
p No bans. |
||||||
span |
hr(size=1) |
||||||
label |
else |
||||||
input.post-check(type='checkbox', name='spoiler' value=1) |
for ban in bans |
||||||
| Spoiler |
section.thread |
||||||
span |
+ban(ban) |
||||||
label |
hr(size=1) |
||||||
input.post-check(type='checkbox', name='dismiss' value=1) |
section.action-wrapper |
||||||
| Dismiss |
input(type='submit', value='unban') |
||||||
input(type='submit', value='submit') |
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in new issue