var _ = require('lodash') var db = require('../db') var util = require('../util/util') var upload = require('../util/upload') var bucky = module.exports = { /* INDEX */ ensureLatestThreads: function (req, res, next){ db.getLatestThreads().then(function(threads){ res.threads = threads res.threads_ids = res.threads.pluck("id").sort() res.keywords = _.uniq(res.threads.pluck("keyword")) next() }) }, ensureCommentCountsForThreads: function (req, res, next){ db.getCommentCounts(res.threads_ids).then(function(counts){ var lookup = {} counts.forEach(function(c){ lookup[c.thread] = c }) res.threads.forEach(function(thread){ if (lookup[thread.id]) { thread.set("comment_count", lookup[thread.id].count) } }) next() }) }, ensureFileCountsForThreads: function (req, res, next){ db.getFileSizes(res.threads_ids).then(function(counts){ var lookup = {} counts.forEach(function(c){ lookup[c.thread] = c }) res.threads.forEach(function(t){ var c = lookup[t.id] t.set("file_count", c ? c.count : 0) t.set("size", c ? c.size : 0) }) next() }) }, ensureKeywordsForThreads: function (req, res, next){ db.getKeywords(res.keywords).then(function(keywords){ var lookup = {} keywords.forEach(function(k){ lookup[k.get('keyword')] = k }) res.threads.forEach(function(t){ var kw = t.get('keyword') if (! kw) return var k = lookup[kw] if (! k) return if (! t.get("color")) { t.set("color", k.get("color")) } }) next() }) }, ensureHootbox: function (req, res, next){ db.getCommentsForThread(1, 15, 0, "desc").then(function(hootbox){ res.hootbox = hootbox next() }) }, ensureLastlog: function (req, res, next){ db.getLastlog(5).then(function(lastlog){ res.lastlog = lastlog next() }) }, createThread: function (req, res, next){ if (! req.body.title || ! req.body.title.length) { res.json({ error: "no title" }) return } var data = { title: req.body.title, keyword: req.body.keyword, username: req.user.get('username'), createdate: util.now(), lastmodified: util.now(), size: 0, privacy: false, color: req.body.color, viewed: 0, revision: 0, } db.createThread(data).then(function(thread){ res.thread = thread next() }) }, /* DETAILS */ ensureThread: function (req, res, next){ var id = req.params.id.replace(/\D/g, "") if (! id) { return res.sendStatus(404) } db.getThread(id).then(function(thread){ if (thread) { res.thread = thread next() } else { res.sendStatus(404) } }) }, ensureThreadById: function (req, res, next){ var id = req.params.thread_id.replace(/\D/g, "") if (! id) { return res.sendStatus(404) } db.getThread(id).then(function(thread){ if (thread) { res.thread = thread next() } else { res.sendStatus(404) } }) }, prepareThread: function (req, res, next){ var thread = res.thread if (thread) { var settings try { settings = JSON.parse(thread.get('settings') || '{}') } catch(e) { settings = {} } res.thread.set("settings", settings) } next() }, ensureCommentThread: function (req, res, next){ if (! res.comment) { return res.sendStatus(404) } var id = res.comment.get('thread') if (! id) { return res.sendStatus(404) } db.getThread(id).then(function(thread){ if (thread) { res.thread = thread next() } else { res.sendStatus(404) } }) }, ensureKeywordForThread: function (req, res, next){ var keyword = res.thread.get('keyword') if (! keyword) return next() db.getKeyword(keyword).then(function(keyword){ res.keyword = keyword next() }) }, ensureCommentsForThread: function (req, res, next){ db.getCommentsForThread(res.thread.get('id')).then(function(comments){ res.comments = comments || [] next() }) }, ensureFilesForThread: function (req, res, next){ db.getFilesForThread(res.thread.get('id')).then(function(files){ res.files = files || [] next() }) }, bumpViewCount: function(req, res, next) { res.thread.set('viewed', res.thread.get('viewed') + 1) res.thread.save().then( () => next() ) }, bumpThreadRevisions: function (req, res, next){ // don't bump the hootbox! if (res.thread.get('id') == 1) { return next() } res.thread.set('revision', res.thread.get('revision')+1) res.thread.set('lastmodified', util.now()) res.thread.save().then( () => next() ) }, updateThreadSettings: function (req, res, next){ var title = util.sanitize(req.body.title || "") if (! title || ! title.length) { return res.sendStatus(500) } var keyword = util.sanitize(req.body.keyword || "") var privacy = parseInt(req.body.privacy) || 0 var allowed = util.sanitize(req.body.allowed || "") var settings if (typeof req.body.settings === 'object') { try { settings = JSON.stringify(req.body.settings) } catch(e) { } } if (! settings) { return res.sendStatus(500) } res.thread.set('title', title) res.thread.set('keyword', keyword) res.thread.set('color', util.sanitize(req.body.color || 'blue')) res.thread.set('revision', res.thread.get('revision')+1) res.thread.set('settings', settings) res.thread.set('privacy', privacy) res.thread.set('allowed', allowed) res.thread.save() .then( () => next() ) .catch(err => { console.error(err) next() }) }, // ensureInterestedUsers: function(req, res, next){ // // given a thread, find people who might be interested in it // // - other people who have been in threads with you // // - other people who have posted on the keyword // // for now though, just show the last 20 people who have logged in.. // db.getLastlog(21).then( (users) => { // res.interestedUsers = users // next() // }).catch( () => { // res.interestedUsers = [] // next() // }) // }, // ensureThreadUsers: function(req, res, next) { // db.getThreadUsers(res.thread.get('id')).then(thread_users => { // res.thread_users = thread_users // next() // }) // }, checkUsernames: function(req, res, next) { if (! req.body.usernames) return res.sendStatus(500) db.checkUsernames(req.body.usernames).then( (users) => { res.usernames = users.map(user => user.username) next() }).catch((err) => { console.log(err) res.usernames = [] next() }) }, destroyThread: function (req, res, next) { console.log(">>> destroying thread", res.thread.get('id')) var commentPromises = res.comments.map((comment) => { return comment.destroy() }) var filePromises = db.destroyFiles(res.files) var threadPromise = res.thread.destroy() var promises = [ threadPromise ].concat(commentPromises).concat(filePromises) Promise.all(promises).then( () => { next() }).catch( (err) => { res.sendStatus(500) }) }, /* KEYWORDS */ ensureKeyword: function (req, res, next){ var keyword = req.params.keyword if (! keyword) { return res.sendStatus(404) } db.getKeyword(keyword).then(function(k){ if (! k) { return res.sendStatus(404) } res.keyword = k next() }) }, ensureKeywords: function (req, res, next){ db.getKeywords().then(function(k){ if (! k) { return res.sendStatus(404) } res.keywords = k next() }) }, ensureThreadGroups: function (res, res, next){ db.getThreadGroups().then(function(threadGroups){ res.threadGroups = threadGroups next() }) }, ensureThreadsForKeyword: function (req, res, next){ var keyword = req.params.keyword if (! keyword) { res.sendStatus(404) } db.getThreadsForKeyword(keyword).then(function(threads){ res.threads = threads res.threads_ids = res.threads.pluck("id").sort() res.keywords = _.uniq(res.threads.pluck("keyword")) next() }) }, ensureThreadsForUser: function (req, res, next){ var username = res.user.username var limit = parseInt(req.params.limit) || 10 var offset = parseInt(req.params.offset) || 0 if (! username) { res.sendStatus(404) } db.getThreadsForUser(username, limit, offset).then(function(threads){ res.threads = threads res.threads_ids = res.threads.pluck("id").sort() res.keywords = _.uniq(res.threads.pluck("keyword")) next() }) }, ensureTopThreadsForUser: function (req, res, next){ var username = res.user.username var limit = parseInt(req.params.limit) || 10 var offset = parseInt(req.params.offset) || 0 if (! username) { res.sendStatus(404) } db.getTopThreadsForUser(username, limit, offset).then(function(top_threads){ res.topThreads = top_threads res.topThreads_ids = res.topThreads.pluck("id").sort() res.topKeywords = _.uniq(res.topThreads.pluck("keyword")) next() }) }, ensureCommentsForUser: function (req, res, next){ db.getCommentsForUser(res.user.username).then(function(comments){ res.comments = comments || [] next() }) }, ensureFilesForUser: function (req, res, next){ db.getFilesForUser(res.user.username).then(function(files){ res.files = files || [] next() }) }, createKeyword: function (req, res, next){ if (! req.body.keyword || ! req.body.keyword.length) { res.json({ error: "no keyword" }) return } var data = { keyword: req.body.keyword, owner: req.user.get('username'), createdate: util.now(), public: 1, color: req.body.color || 'blue', } db.createKeyword(data).then(function(keyword){ res.keyword = keyword next() }) }, /* POSTING */ verifyFilesOrComment: function (req, res, next){ var hasComment = req.body.comment && req.body.comment.length var hasFile = req.files && req.files.length if (! hasComment && ! hasFile) { console.log(">>> no files or comment") return res.sendStatus(400) } next() }, /* COMMENTS */ ensureComment: function (req, res, next){ var id = req.params.id.replace(/\D/g, "") if (! id) { return res.sendStatus(404) } db.getCommentById(id).then(function(comment){ if (comment) { comment.set('comment', comment.get('comment').toString()) res.comment = comment next() } else { res.sendStatus(404) } }) }, createOptionalComment: function(req, res, next){ if (! req.body.comment || ! req.body.comment.length) { return next() } bucky.createComment(req, res, next) }, createComment: function (req, res, next){ if (! req.body.comment || ! req.body.comment.length) { res.json({ error: "no comment" }) return } var data = { thread: res.thread.get('id'), parent_id: req.body.parent_id || -1, username: req.user.get('username'), date: util.now(), comment: req.body.comment, } db.createComment(data).then(function(comment){ res.comment = comment next() }) }, updateComment: function(req, res, next){ if (! req.body.comment || ! req.body.comment.length) { return res.sendStatus(500) } res.comment.set('comment', req.body.comment) res.comment.set('date', util.now()) res.comment.save().then(() => { next() }).catch(err => { res.sendStatus(500) }) }, moveComment: function(req, res, next){ res.comment.set('thread', res.thread.get('id')) res.comment.save().then(() => { next() }).catch(err => { res.sendStatus(500) }) }, destroyComment: function(req, res, next){ res.comment.destroy().then(() => { next() }).catch(err => { res.sendStatus(500) }) }, /* FILES */ ensureFile: function (req, res, next){ var id = req.params.id.replace(/\D/g, "") if (! id) { return res.sendStatus(404) } db.getFileById(id).then(function(file){ if (file) { res.file = file next() } else { res.sendStatus(404) } }) }, createOptionalFiles: function(req, res, next){ if (! req.files || ! req.files.length) { return next() } bucky.createFiles(req, res, next) }, createFiles: function (req, res, next){ if (! req.files || ! req.files.length) { res.json({ error: "no files" }) return } var thread_id = res.thread.get('id') var dirname = '/bucky/data/' + thread_id + '/' var promises = req.files.map((file) => { return new Promise( (resolve, reject) => { upload.put({ file: file, preserveFilename: true, dirname: dirname, unacceptable: function(err){ reject(err) }, success: function(url){ console.log("file >", url) var data = { thread: res.thread.get('id'), username: req.user.get('username'), filename: file.originalname, date: util.now(), size: file.size, privacy: false, storage: 'i.asdf.us', } db.createFile(data).then(function(file){ resolve(file) }).catch( (err) => reject(err) ) } }) }) }) Promise.all(promises).then(values => { next() }).catch(err => { console.log(err) }) }, moveFile: function(req, res, next){ db.moveFile(res.file, res.thread.get('id')).then(() => { next() }).catch(err => { res.sendStatus(500) }) }, destroyFile: function(req, res, next){ var filePromises = db.destroyFiles([res.file]) Promise.all(filePromises).then( () => next() ) .catch(err => { console.error(err); next() }) }, /* PROFILE / USER */ ensureUser: function (req, res, next){ var username = util.sanitizeName(req.params.username) if (! username) { return res.sendStatus(404) } db.getUserByUsername(username).then(function(user){ if (user) { res.user = user next() } else { res.sendStatus(404) } }) }, sanitizeUser: function(req, res, next) { res.user = util.sanitizeUser(res.user) next() }, bumpLastSeen: function(req, res, next) { req.user.set('lastseen', util.now()) req.user.save().then( () => next() ) }, updateProfile: function(req, res, next) { var user = res.user "realname location email phone website twitter".split(" ").forEach( (field) => { res.user.set(field, req.body[field]) }) next() }, uploadAvatar: function(req, res, next) { if (! req.file) return next() var dirname = '/bucky/profile/' upload.put({ file: req.file, filename: req.user.get('username') + '.jpg', dirname: dirname, unacceptable: function(err){ res.sendStatus({ error: 'Problem uploading avatar.' }) }, success: function(url){ console.log("avatar >", url) res.user.set('avatar', url) next() } }) }, saveUser: function(req, res, next){ res.user.save().then( () => next() ) }, /* PRIVACY */ checkIsAdmin: function(req, res, next){ if (req.user.get('ulevel') !== 3) { return res.sendStatus(500) } next() }, checkUserPrivacy: function(req, res, next) { if (req.user.get('username') !== res.user.get('username')) { return res.sendStatus(500) } next() }, checkThreadPrivacy: function(req, res, next) { if (res.thread.get('id') !== 1 && ! res.thread.checkPrivacy(req.user)) { return res.sendStatus(500) } next() }, checkCommentPrivacy: function(req, res, next) { if (req.user.get('ulevel') !== 3 && req.user.get('username') !== res.comment.get('username')) { return res.sendStatus(500) } next() }, checkFilePrivacy: function(req, res, next) { if (req.user.get('ulevel') !== 3 && req.user.get('username') !== res.file.get('username')) { return res.sendStatus(500) } next() }, checkMessagePrivacy: function(req, res, next) { var username = req.user.get('username') if (username !== res.message.get('sender') && username !== res.message.get('recipient')) { return res.sendStatus(500) } next() }, filterPrivateThreads: function(req, res, next) { res.threads = res.threads.filter(thread => { return thread.checkPrivacy(req.user) }) next() }, /* MAIL */ ensureMailboxes: function (req, res, next){ var username = req.user.get('username') var box = req.params.box var mbox = username + "." + box if (! box) { res.sendStatus(404) } db.getMailboxes(username).then(function(boxes){ if (! boxes) { return res.sendStatus(404) } if (! boxes.models.some(function(box){ return box.get('mbox') == mbox })) { return res.sendStatus(404) } res.boxes = boxes next() }) }, ensureMailboxCounts: function (req, res, next){ db.getMailboxCounts(res.boxes.pluck("mbox")).then(function(counts){ var lookup = {} counts.forEach(function(c){ lookup[c.mbox] = c }) res.boxes.forEach(function(box){ var count = lookup[box.get('mbox')] ? lookup[box.get('mbox')].count : 0 box.set("count", count) }) next() }) }, ensureMessages: function (req, res, next){ const limit = parseInt(req.query.limit) || 50 const offset = parseInt(req.query.offset) || 0 db.getMessages(req.user.get('username'), req.params.box, limit, offset).then(function(messages){ res.messages = messages res.query = { limit, offset } next() }) }, ensureMessage: function(req, res, next){ db.getMessage(req.params.id).then(function(message){ if (! message) { return res.sendStatus(404) } var username = req.user.get('username') if (username !== message.get('recipient') && username !== message.get('sender')) { res.sendStatus(404) return } res.message = message next() }) }, checkMail: function(req, res, next){ db.checkMail(req.user.get('username')).then(function(mail){ res.mail = mail ? mail[0] : { count: 0 } next() }) }, markMessageUnread: function(req, res, next){ if (res.message.get('unread')) { res.message.set('unread', 0) res.message.save().then(() => next()) } else { next() } }, ensureRecipient: function(req, res, next){ db.getUserByUsername(util.sanitizeName(req.body.username)).then( (user) => { if (! user) { res.send({ error: "No such recipient" }) return } next() }) }, sendMessage: function(req, res, next){ var recipient = util.sanitizeName(req.body.username) var sender = req.user.get('username') var subject = util.sanitize(req.body.subject) var body = util.sanitize(req.body.body) res.mail = { sender: sender, recipient: recipient } var recipientMessage = { mbox: recipient + ".inbox", unread: true, sender: sender, recipient: recipient, date: util.now(), subject: subject, body: body, } var senderMessage = { mbox: sender + ".outbox", unread: false, sender: sender, recipient: recipient, date: util.now(), subject: subject, body: body, } Promise.all([ db.createMessage(recipientMessage), db.createMessage(senderMessage), ]).then( () => next() ) }, deleteDraft: function(req, res, next){ if (! req.body.draft_id) return next() db.getMessage(req.body.draft_id).then( (message) => { if (message.get('sender') === req.user.get('username')) { return message.destroy().then( () => next() ) } // erroneous draft message?? next() }) }, destroyMessage: function(req, res, next) { res.message.destroy().then( () => next() ) }, }