diff options
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | bucky/app/api.js | 25 | ||||
| -rw-r--r-- | bucky/app/bucky.js | 1196 | ||||
| -rw-r--r-- | bucky/app/pages.js | 222 | ||||
| -rw-r--r-- | bucky/app/site.js | 5 | ||||
| -rw-r--r-- | bucky/bin/build-scripts.js | 43 | ||||
| -rw-r--r-- | bucky/db/index.js | 33 | ||||
| -rw-r--r-- | bucky/util/auth.js | 250 | ||||
| -rw-r--r-- | migrations/20150905232742_make_blob_fields_text.js | 91 | ||||
| -rw-r--r-- | package.json | 4 | ||||
| -rw-r--r-- | public/assets/css/bucky.css | 4 | ||||
| -rw-r--r-- | public/assets/js/lib/router.js | 212 | ||||
| -rw-r--r-- | public/assets/js/lib/views/details/files.js | 4 | ||||
| -rw-r--r-- | public/assets/js/lib/views/index/hootbox.js | 91 | ||||
| -rw-r--r-- | public/assets/js/lib/views/index/index.js | 78 | ||||
| -rw-r--r-- | public/assets/js/lib/views/stream/hootform.js | 42 | ||||
| -rw-r--r-- | public/assets/js/lib/views/stream/hootstream.js | 205 | ||||
| -rw-r--r-- | public/assets/js/lib/views/stream/index.js | 69 | ||||
| -rw-r--r-- | public/assets/js/util/format.js | 23 | ||||
| -rw-r--r-- | views/pages/stream.ejs | 9 | ||||
| -rw-r--r-- | views/partials/hootstream.ejs | 276 | ||||
| -rw-r--r-- | views/partials/scripts.ejs | 4 | ||||
| -rw-r--r-- | yarn.lock | 58 |
23 files changed, 1867 insertions, 1081 deletions
@@ -40,10 +40,14 @@ mysql -u carbon -p bucky < bucky-20150903.sql ### MongoDB +DEPRECATED + MongoDB is used in production as session store, but another session middleware can be substituted (it would be nice to not use Mongo). By default `SESSIONS_IN_MEMORY=yes` in the .env will use the default memorystore from Express, but this is not ideal for something accessible publicly on the open internet. ### Berkeley DB +DEPRECATED + Used for the search, `libdb` should already be installed on your system. ## Building the search index diff --git a/bucky/app/api.js b/bucky/app/api.js index 857849e..a2f85d0 100644 --- a/bucky/app/api.js +++ b/bucky/app/api.js @@ -98,6 +98,31 @@ function route(app) { function (req, res) { res.json({ threads: res.threads, + hootstream: res.hootstream, + hootbox: res.hootbox, + lastlog: res.lastlog, + mail: res.mail, + }); + } + ); + app.get( + "/api/stream", + bucky.ensureLastlog, + middleware.ensureAuthenticated, + // bucky.ensureLatestThreads, + bucky.ensureHootbox, + bucky.ensureHootstream, + privacy.filterPrivateThreads, + bucky.ensureCommentCountsForThreads, + bucky.ensureFileCountsForThreads, + bucky.ensureKeywordsForThreads, + bucky.bumpLastSeen, + bucky.checkMail, + function (req, res) { + res.json({ + threads: res.threads, + files: res.files, + comments: res.comments, hootbox: res.hootbox, lastlog: res.lastlog, mail: res.mail, diff --git a/bucky/app/bucky.js b/bucky/app/bucky.js index b9d9a16..5dc7244 100644 --- a/bucky/app/bucky.js +++ b/bucky/app/bucky.js @@ -1,87 +1,107 @@ -var _ = require('lodash') -var db = require('../db') -var util = require('../util/util') -var upload = require('../util/upload') - -var bucky = module.exports = { +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() - }) + 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){ + 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) + thread.set("comment_count", lookup[thread.id].count); } - }) - next() - }) + }); + 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() - }) + 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")) + 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() - }) + }); + next(); + }); }, - ensureHootbox: function (req, res, next){ - db.getCommentsForThread(1, 20, 0, "desc").then(function(hootbox){ - res.hootbox = hootbox - next() - }) + ensureHootbox: function (req, res, next) { + db.getCommentsForThread(1, 20, 0, "desc").then(function (hootbox) { + res.hootbox = hootbox; + next(); + }); + }, + ensureHootstream: function (req, res, next) { + Promise.all([ + db.getHootstreamFiles({ + limit: req.query.limit || 10, + offset: req.query.offset || 0, + }), + db.getHootstreamComments({ + limit: req.query.limit || 10, + offset: req.query.offset || 0, + }), + ]).then(([files, comments]) => { + db.getHootstreamThreads({ files, comments }).then((threads) => { + res.files = files; + res.comments = comments; + res.threads = threads; + res.threads_ids = res.threads.pluck("id").sort(); + res.keywords = _.uniq(res.threads.pluck("keyword")); + next(); + }); + }); }, - ensureLastlog: function (req, res, next){ - db.getLastlog(5).then(function(lastlog){ - res.lastlog = lastlog - 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 + 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'), + username: req.user.get("username"), createdate: util.now(), lastmodified: util.now(), size: 0, @@ -89,666 +109,698 @@ var bucky = module.exports = { color: req.body.color, viewed: 0, revision: 0, - } - db.createThread(data).then(function(thread){ - res.thread = thread - next() - }) + }; + 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) + 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){ + db.getThread(id).then(function (thread) { if (thread) { - res.thread = thread - next() - } - else { - res.sendStatus(404) + 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) + 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){ + db.getThread(id).then(function (thread) { if (thread) { - res.thread = thread - next() - } - else { - res.sendStatus(404) + res.thread = thread; + next(); + } else { + res.sendStatus(404); } - }) + }); }, - prepareThread: function (req, res, next){ - var thread = res.thread + prepareThread: function (req, res, next) { + var thread = res.thread; if (thread) { - var settings + var settings; try { - settings = JSON.parse(thread.get('settings') || '{}') - } catch(e) { - settings = {} + settings = JSON.parse(thread.get("settings") || "{}"); + } catch (e) { + settings = {}; } - res.thread.set("settings", settings) + res.thread.set("settings", settings); } - next() + next(); }, - ensureCommentThread: function (req, res, next){ - if (! res.comment) { - return res.sendStatus(404) + ensureCommentThread: function (req, res, next) { + if (!res.comment) { + return res.sendStatus(404); } - var id = res.comment.get('thread') - if (! id) { - return res.sendStatus(404) + var id = res.comment.get("thread"); + if (!id) { + return res.sendStatus(404); } - db.getThread(id).then(function(thread){ + db.getThread(id).then(function (thread) { if (thread) { - res.thread = thread - next() + res.thread = thread; + next(); + } else { + res.sendStatus(404); } - 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() - }) + 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() - }) + 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() - }) + 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() ) + bumpViewCount: function (req, res, next) { + res.thread.set("viewed", res.thread.get("viewed") + 1); + res.thread.save().then(() => next()); }, - bumpThreadRevisions: function (req, res, next){ + bumpThreadRevisions: function (req, res, next) { // don't bump the hootbox! - if (res.thread.get('id') == 1) { - return next() + 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() ) + 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) + 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') { + 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) { - console.error("JSON error in thread settings!!!!") - return res.sendStatus(500) + settings = JSON.stringify(req.body.settings); + } catch (e) { + console.error("JSON error in thread settings!!!!"); + return res.sendStatus(500); } } - if (! settings) { - return res.sendStatus(500) + 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() - }) + 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(); + }); }, - buryThread: function (req, res, next){ - res.thread.set('lastmodified', util.now() - (14 * 86400)) - res.thread.save().then( () => next() ) + buryThread: function (req, res, next) { + res.thread.set("lastmodified", util.now() - 14 * 86400); + res.thread.save().then(() => 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() - }) + // 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')) + 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) - }) + 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) + 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) + db.getKeyword(keyword).then(function (k) { + if (!k) { + return res.sendStatus(404); } - res.keyword = k - next() - }) + res.keyword = k; + next(); + }); }, - ensureKeywords: function (req, res, next){ - db.getKeywords().then(function(k){ - if (! k) { - return res.sendStatus(404) + ensureKeywords: function (req, res, next) { + db.getKeywords().then(function (k) { + if (!k) { + return res.sendStatus(404); } - res.keywords = k - next() - }) + res.keywords = k; + next(); + }); }, - ensureLatestKeywordThreads: function (req, res, next){ - db.getLatestKeywordThreads().then(function(threads){ - res.threads = threads - next() - }) + ensureLatestKeywordThreads: function (req, res, next) { + db.getLatestKeywordThreads().then(function (threads) { + res.threads = threads; + next(); + }); }, - ensureThreadGroups: function (req, res, next){ - db.getThreadGroups().then(function(threadGroups){ - res.threadGroups = threadGroups - next() - }) + ensureThreadGroups: function (req, 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) + 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() - }) + 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) + 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() - }) + 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) + 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() - }) + 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() - }) + 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() - }) + 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 + 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'), + 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() - }) + 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) + 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() + next(); }, /* COMMENTS */ - ensureComment: function (req, res, next){ - var id = req.params.id.replace(/\D/g, "") - if (! id) { - return res.sendStatus(404) + 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){ + db.getCommentById(id).then(function (comment) { if (comment) { - comment.set('comment', comment.get('comment').toString()) - res.comment = comment - next() + comment.set("comment", comment.get("comment").toString()); + res.comment = comment; + next(); + } else { + res.sendStatus(404); } - else { - res.sendStatus(404) - } - }) + }); }, - createOptionalComment: function(req, res, next){ - if (! req.body.comment || ! req.body.comment.length) { - return next() + createOptionalComment: function (req, res, next) { + if (!req.body.comment || !req.body.comment.length) { + return next(); } - bucky.createComment(req, res, 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 + 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'), + thread: res.thread.get("id"), parent_id: req.body.parent_id || -1, - username: req.user.get('username'), + username: req.user.get("username"), date: util.now(), comment: req.body.comment, - } - db.createComment(data).then(function(comment){ - res.comment = comment - next() - }) + }; + 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) + 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) - }) + 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) - }) + 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) - }) + 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) + 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){ + db.getFileById(id).then(function (file) { if (file) { - res.file = file - next() + res.file = file; + next(); + } else { + res.sendStatus(404); } - else { - res.sendStatus(404) - } - }) + }); }, - createOptionalFiles: function(req, res, next){ - if (! req.files || ! req.files.length) { - return next() + createOptionalFiles: function (req, res, next) { + if (!req.files || !req.files.length) { + return next(); } - bucky.createFiles(req, res, next) + bucky.createFiles(req, res, next); }, - createFiles: function (req, res, next){ - if (! req.files || ! req.files.length) { - res.json({ error: "no files" }) - return + 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 = process.env.S3_PATH + '/data/' + thread_id + '/' + var thread_id = res.thread.get("id"); + var dirname = process.env.S3_PATH + "/data/" + thread_id + "/"; var promises = req.files.map((file) => { - return new Promise( (resolve, reject) => { + return new Promise((resolve, reject) => { upload.put({ file: file, preserveFilename: true, dirname: dirname, - unacceptable: function(err){ - reject(err) + unacceptable: function (err) { + reject(err); }, - success: function(url){ - console.log("file >", url) + success: function (url) { + console.log("file >", url); var data = { - thread: res.thread.get('id'), - username: req.user.get('username'), + thread: res.thread.get("id"), + username: req.user.get("username"), filename: file.originalname, date: util.now(), size: file.size, privacy: false, storage: process.env.S3_BUCKET, - } - db.createFile(data).then(function(file){ - resolve(file) - }).catch( (err) => reject(err) ) - } - }) + }; + db.createFile(data) + .then(function (file) { + resolve(file); + }) + .catch((err) => reject(err)); + }, + }); + }); + }); + Promise.all(promises) + .then((values) => { + res.files = values; + next(); }) - }) - Promise.all(promises).then(values => { - res.files = values - next() - }).catch(err => { - console.log(err) - }) + .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) - }) + 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() }) + 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) + ensureUser: function (req, res, next) { + var username = util.sanitizeName(req.params.username); + if (!username) { + return res.sendStatus(404); } - db.getUserByUsername(username).then(function(user){ + db.getUserByUsername(username).then(function (user) { if (user) { - res.user = user - next() - } - else { - res.sendStatus(404) + res.user = user; + next(); + } else { + res.sendStatus(404); } - }) + }); }, - ensureUserFromBody: function (req, res, next){ - var username = util.sanitizeName(req.body.username) - if (! username) { - return res.sendStatus(404) + ensureUserFromBody: function (req, res, next) { + var username = util.sanitizeName(req.body.username); + if (!username) { + return res.sendStatus(404); } - db.getUserByUsername(username).then(function(user){ + db.getUserByUsername(username).then(function (user) { if (user) { - res.user = user - next() + res.user = user; + next(); + } else { + console.log("no such user!!"); + res.sendStatus(404); } - else { - console.log('no such user!!') - res.sendStatus(404) - } - }) + }); }, - ensureUserlist: function (req, res, next){ - db.getUsers().then(function(users){ - if (! users) { - return res.sendStatus(404) + ensureUserlist: function (req, res, next) { + db.getUsers().then(function (users) { + if (!users) { + return res.sendStatus(404); } - res.users = users - next() - }) + res.users = users; + next(); + }); }, ensureUserThreadCounts: function (req, res, next) { - db.getUserThreadCounts().then(function(counts){ + db.getUserThreadCounts().then(function (counts) { if (!counts) { - return res.sendStatus(404) + return res.sendStatus(404); } - res.threadCounts = counts - next() - }) + res.threadCounts = counts; + next(); + }); }, ensureUserCommentCounts: function (req, res, next) { - db.getUserCommentCounts().then(function(counts){ + db.getUserCommentCounts().then(function (counts) { if (!counts) { - return res.sendStatus(404) + return res.sendStatus(404); } - res.commentCounts = counts - next() - }) + res.commentCounts = counts; + next(); + }); }, ensureUserFileCounts: function (req, res, next) { - db.getUserFileCounts().then(function(counts){ + db.getUserFileCounts().then(function (counts) { if (!counts) { - return res.sendStatus(404) + return res.sendStatus(404); } - res.fileCounts = counts - next() - }) + res.fileCounts = counts; + next(); + }); }, ensureUserStatistics: function (req, res, next) { - var stats = {} - res.threadCounts.forEach(function(user){ - stats[user.username] = stats[user.username] || {} - stats[user.username].threads = user.count - }) - res.commentCounts.forEach(function(user){ - stats[user.username] = stats[user.username] || {} - stats[user.username].comments = user.count - }) - res.fileCounts.forEach(function(user){ - stats[user.username] = stats[user.username] || {} - stats[user.username].files = user.count - stats[user.username].fileSize = user.size - }) - res.userStats = stats - next() + var stats = {}; + res.threadCounts.forEach(function (user) { + stats[user.username] = stats[user.username] || {}; + stats[user.username].threads = user.count; + }); + res.commentCounts.forEach(function (user) { + stats[user.username] = stats[user.username] || {}; + stats[user.username].comments = user.count; + }); + res.fileCounts.forEach(function (user) { + stats[user.username] = stats[user.username] || {}; + stats[user.username].files = user.count; + stats[user.username].fileSize = user.size; + }); + res.userStats = stats; + next(); }, - sanitizeUser: function(req, res, next) { - res.user = util.sanitizeUser(res.user) - next() + 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() ) + 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() + 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/' + 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', + filename: req.user.get("username") + ".jpg", dirname: dirname, - unacceptable: function(err){ - res.sendStatus({ error: 'Problem uploading avatar.' }) + unacceptable: function (err) { + res.sendStatus({ error: "Problem uploading avatar." }); }, - success: function(url){ - console.log("avatar >", url) - res.user.set('avatar', url) - next() - } - }) + success: function (url) { + console.log("avatar >", url); + res.user.set("avatar", url); + next(); + }, + }); }, - saveUser: function(req, res, next){ - res.user.save().then( () => next() ) + saveUser: function (req, res, next) { + res.user.save().then(() => 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) + 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) + 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) + if ( + !boxes.models.some(function (box) { + return box.get("mbox") == mbox; + }) + ) { + return res.sendStatus(404); } - res.boxes = boxes - next() - }) + 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() - }) + 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() - }) + 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) + 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 + var username = req.user.get("username"); + if ( + username !== message.get("recipient") && + username !== message.get("sender") + ) { + res.sendStatus(404); + return; } - res.message = message - next() - }) + 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() - }) + 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()) + markMessageUnread: function (req, res, next) { + if (res.message.get("unread")) { + res.message.set("unread", 0); + res.message.save().then(() => next()); } else { - next() + next(); } }, - ensureRecipient: function(req, res, next){ - db.getUserByUsername(util.sanitizeName(req.body.username)).then( (user) => { - if (! user) { - res.send({ error: "No such recipient" }) - return + ensureRecipient: function (req, res, next) { + db.getUserByUsername(util.sanitizeName(req.body.username)).then((user) => { + if (!user) { + res.send({ error: "No such recipient" }); + return; } - next() - }) + 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 } + 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, @@ -757,7 +809,7 @@ var bucky = module.exports = { date: util.now(), subject: subject, body: body, - } + }; var senderMessage = { mbox: sender + ".outbox", unread: false, @@ -766,23 +818,23 @@ var bucky = module.exports = { date: util.now(), subject: subject, body: body, - } + }; Promise.all([ db.createMessage(recipientMessage), db.createMessage(senderMessage), - ]).then( () => next() ) + ]).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() ) + 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() - }) + next(); + }); }, - destroyMessage: function(req, res, next) { - res.message.destroy().then( () => next() ) + destroyMessage: function (req, res, next) { + res.message.destroy().then(() => next()); }, -} +}); diff --git a/bucky/app/pages.js b/bucky/app/pages.js index 5d8551e..94ae46e 100644 --- a/bucky/app/pages.js +++ b/bucky/app/pages.js @@ -1,137 +1,145 @@ -var middleware = require('../util/middleware') -var util = require('../util/util') +var middleware = require("../util/middleware"); +var util = require("../util/util"); -var fortune = require('../db/fortune') +var fortune = require("../db/fortune"); -module.exports = { route } +module.exports = { route }; -function route (app){ - app.get("/", +function route(app) { + app.get("/", middleware.ensureAuthenticated, function (req, res) { + res.redirect("/index"); + }); + app.get("/index", middleware.ensureAuthenticated, function (req, res) { + res.render("pages/index", { + title: fortune("titles"), + hoot_text: fortune("hoots"), + }); + }); + app.get( + "/index/:keyword", middleware.ensureAuthenticated, - function(req, res){ - res.redirect('/index') - }) - app.get("/index", - middleware.ensureAuthenticated, - function(req, res){ - res.render("pages/index", { - title: fortune("titles"), - hoot_text: fortune("hoots"), - }) - }) - app.get("/index/:keyword", - middleware.ensureAuthenticated, - function(req, res){ + function (req, res) { res.render("pages/index", { title: fortune("titles"), hoot_text: fortune("hoots"), - }) - }) - app.get("/keywords", + }); + } + ); + app.get("/stream", middleware.ensureAuthenticated, function (req, res) { + res.render("pages/stream", { + title: fortune("titles"), + hoot_text: fortune("hoots"), + }); + }); + app.get("/keywords", middleware.ensureAuthenticated, function (req, res) { + res.render("pages/keywords", { title: "Bucky's keywords" }); + }); + app.get("/details/:id", middleware.ensureAuthenticated, function (req, res) { + res.render("pages/details", {}); + }); + app.get( + "/details/:id/settings", middleware.ensureAuthenticated, - function(req, res){ - res.render("pages/keywords", {title: "Bucky's keywords"}) - }) - app.get("/details/:id", - middleware.ensureAuthenticated, - function(req, res){ - res.render("pages/details", {}) - }) - app.get("/details/:id/settings", - middleware.ensureAuthenticated, - function(req, res){ - res.render("pages/details", {}) - }) + function (req, res) { + res.render("pages/details", {}); + } + ); - app.get("/post/", + app.get("/post/", middleware.ensureAuthenticated, function (req, res) { + res.render("pages/post", { title: "Start a new thread" }); + }); + app.get( + "/post/:keyword", middleware.ensureAuthenticated, - function(req, res){ - res.render("pages/post", {title: "Start a new thread"}) - }) - app.get("/post/:keyword", + function (req, res) { + res.render("pages/post", { title: "Start a new thread" }); + } + ); + app.get( + "/comment/:id/edit", middleware.ensureAuthenticated, - function(req, res){ - res.render("pages/post", {title: "Start a new thread"}) - }) - app.get("/comment/:id/edit", middleware.ensureAuthenticated, function(req, res){ - res.render("pages/comment_form", {title: "Edit comment"}) - }) + function (req, res) { + res.render("pages/comment_form", { title: "Edit comment" }); + } + ); - app.get("/profile", + app.get("/profile", middleware.ensureAuthenticated, function (req, res) { + res.render("pages/profile", { + title: "profile for " + util.sanitize(req.user.get("username")), + }); + }); + app.get( + "/profile/:username", middleware.ensureAuthenticated, - function(req, res){ - res.render("pages/profile", {title: "profile for " + util.sanitize(req.user.get('username'))}) - }) - app.get("/profile/:username", + function (req, res) { + res.render("pages/profile", { + title: "profile for " + util.sanitize(req.params.username), + }); + } + ); + app.get( + "/profile/:username/edit", middleware.ensureAuthenticated, - function(req, res){ - res.render("pages/profile", {title: "profile for " + util.sanitize(req.params.username)}) - }) - app.get("/profile/:username/edit", - middleware.ensureAuthenticated, - function(req, res){ - res.render("pages/profile_form", {title: "edit your profile"}) - }) + function (req, res) { + res.render("pages/profile_form", { title: "edit your profile" }); + } + ); - app.get("/users", - middleware.ensureAuthenticated, - function(req, res){ - res.render("pages/users", {}) - }) - app.get("/users/all", - middleware.ensureAuthenticated, - function(req, res){ - res.render("pages/users", {}) - }) + app.get("/users", middleware.ensureAuthenticated, function (req, res) { + res.render("pages/users", {}); + }); + app.get("/users/all", middleware.ensureAuthenticated, function (req, res) { + res.render("pages/users", {}); + }); - app.get("/search/", - middleware.ensureAuthenticated, - function(req, res){ - res.render("pages/search", {title: "search" }) - }) + app.get("/search/", middleware.ensureAuthenticated, function (req, res) { + res.render("pages/search", { title: "search" }); + }); - app.get("/mail/", - middleware.ensureAuthenticated, - function(req, res){ - res.render("pages/mailbox", {title: "your inbox" }) - }) - app.get("/mail/compose", - middleware.ensureAuthenticated, - function(req, res){ - res.render("pages/compose", { - title: "new message", - subject: fortune("subjects"), - verb: "wrote", - }) - }) - app.get("/mail/:box", - middleware.ensureAuthenticated, - function(req, res){ - res.render("pages/mailbox", { title: "your " + util.sanitize(req.params.box) }) - }) - app.get("/mail/compose/:username", + app.get("/mail/", middleware.ensureAuthenticated, function (req, res) { + res.render("pages/mailbox", { title: "your inbox" }); + }); + app.get("/mail/compose", middleware.ensureAuthenticated, function (req, res) { + res.render("pages/compose", { + title: "new message", + subject: fortune("subjects"), + verb: "wrote", + }); + }); + app.get("/mail/:box", middleware.ensureAuthenticated, function (req, res) { + res.render("pages/mailbox", { + title: "your " + util.sanitize(req.params.box), + }); + }); + app.get( + "/mail/compose/:username", middleware.ensureAuthenticated, - function(req, res){ + function (req, res) { res.render("pages/compose", { title: "new message", subject: fortune("subjects"), verb: "wrote", - }) - }) - app.get("/mail/read/:id", + }); + } + ); + app.get( + "/mail/read/:id", middleware.ensureAuthenticated, - function(req, res){ + function (req, res) { res.render("pages/message", { - title: "read message" - }) - }) - app.get("/mail/reply/:id", + title: "read message", + }); + } + ); + app.get( + "/mail/reply/:id", middleware.ensureAuthenticated, - function(req, res){ + function (req, res) { res.render("pages/compose", { title: "reply to message", subject: fortune("subjects"), verb: fortune("mail-verbs"), - }) - }) + }); + } + ); } diff --git a/bucky/app/site.js b/bucky/app/site.js index 69f0157..3627bac 100644 --- a/bucky/app/site.js +++ b/bucky/app/site.js @@ -42,7 +42,10 @@ site.init = function () { key: "bucky.sid", cookie: { secure: process.env.NODE_ENV === "production", - domain: "." + process.env.HOST_NAME, + domain: + process.env.HOST_NAME === "localhost" + ? process.env.HOST_NAME + : "." + process.env.HOST_NAME, maxAge: 43200000000, }, resave: true, diff --git a/bucky/bin/build-scripts.js b/bucky/bin/build-scripts.js index 43cb20e..b704129 100644 --- a/bucky/bin/build-scripts.js +++ b/bucky/bin/build-scripts.js @@ -1,33 +1,32 @@ -var fs = require('fs') -var UglifyJS = require("uglify-es") +var fs = require("fs"); +var UglifyJS = require("uglify-es"); -var index = fs.readFileSync('views/partials/scripts.ejs', "utf8") +var index = fs.readFileSync("views/partials/scripts.ejs", "utf8"); -var scripts = {} -index.split('\n') - .map(line => { - var pz = line.split('"') - if (pz.length < 3) return null - return 'public' + pz[1] - }) - .filter(fn => !! fn) - .forEach(fn => { - scripts[fn] = fs.readFileSync(fn, "utf8") +var scripts = {}; +index + .split("\n") + .map((line) => { + var pz = line.split('"'); + if (pz.length < 3) return null; + return "public" + pz[1]; }) + .filter((fn) => !!fn) + .forEach((fn) => { + scripts[fn] = fs.readFileSync(fn, "utf8"); + }); var result = UglifyJS.minify(scripts, { sourceMap: { filename: "app.js", - url: "app.js.map" - } -}) + url: "app.js.map", + }, +}); if (result.error) { - console.error(result.error) + console.error(result.error); } if (result.warnings) { - console.error(result.warnings) + console.error(result.warnings); } -fs.writeFileSync("public/assets/min/app.min.js", result.code, "utf8") -fs.writeFileSync("public/assets/min/app.min.js.map", result.map, "utf8") - - +fs.writeFileSync("public/assets/min/app.min.js", result.code, "utf8"); +fs.writeFileSync("public/assets/min/app.min.js.map", result.map, "utf8"); diff --git a/bucky/db/index.js b/bucky/db/index.js index 8ab29ff..36541f8 100644 --- a/bucky/db/index.js +++ b/bucky/db/index.js @@ -114,6 +114,38 @@ db.getLastlog = function (limit) { .limit(limit || 10); }; +/** HOOTSTREAM */ + +db.getHootstreamFiles = ({ limit, offset }) => + knex("files") + .join("threads", "threads.id", "=", "files.thread") + .select("files.*") + .where("threads.privacy", false) + .orderBy("files.id", "desc") + .offset(offset) + .limit(limit); +db.getHootstreamComments = ({ limit, offset }) => + knex("comments") + .join("threads", "threads.id", "=", "comments.thread") + .select("comments.*") + .where("threads.privacy", false) + .orderBy("comments.id", "desc") + .offset(offset) + .limit(limit); +db.getHootstreamThreads = ({ files, comments }) => + Thread.where((builder) => + builder.whereIn( + "id", + Array.from( + new Set( + [...comments, ...files] + .map((item) => item?.thread) + .filter((item) => !!item) + ) + ) + ) + ).fetchAll(); + /* THREADS */ db.getLatestThreads = function () { @@ -149,6 +181,7 @@ db.getThreadsById = function (ids) { return Thread.where("id", "in", ids).fetchAll(); }; db.createThread = function (data) { + console.log(data); return new db.Thread(data).save(); }; db.updateThread = function (data) {}; diff --git a/bucky/util/auth.js b/bucky/util/auth.js index d457704..2ca1b94 100644 --- a/bucky/util/auth.js +++ b/bucky/util/auth.js @@ -1,65 +1,61 @@ -var passport = require('passport') -var LocalStrategy = require('passport-local').Strategy -var crypto = require('crypto') -var crypt = require('unix-crypt-td-js') -var fs = require('fs') -var db = require('../db') -var util = require('./util') -var upload = require('./upload') +var passport = require("passport"); +var LocalStrategy = require("passport-local").Strategy; +var crypto = require("crypto"); +var crypt = require("unix-crypt-td-js"); +var fs = require("fs"); +var db = require("../db"); +var util = require("./util"); +var upload = require("./upload"); -var middleware = require('./middleware') +var middleware = require("./middleware"); -var auth = module.exports = { - - init: function(){ - passport.serializeUser(auth.serializeUser) - passport.deserializeUser(auth.deserializeUser) - passport.use(new LocalStrategy(auth.verifyLocalUser)) +var auth = (module.exports = { + init: function () { + passport.serializeUser(auth.serializeUser); + passport.deserializeUser(auth.deserializeUser); + passport.use(new LocalStrategy(auth.verifyLocalUser)); }, - route: function(app){ - app.get("/login", - function(req, res){ - res.render("pages/login", { - title: "login" - }) - }) - app.get("/signup", function(req, res){ + route: function (app) { + app.get("/login", function (req, res) { + res.render("pages/login", { + title: "login", + }); + }); + app.get("/signup", function (req, res) { res.render("pages/signup", { - title: "signup" - }) - }) - app.get("/logout", auth.logout) + title: "signup", + }); + }); + app.get("/logout", auth.logout); - app.put("/api/signup", + app.put( + "/api/signup", auth.checkIfUserExists, auth.createUser, passport.authenticate("local"), auth.createMailboxes, auth.uploadDefaultAvatar, - auth.login) - app.put("/api/login", - passport.authenticate("local"), - auth.login) - app.put("/api/checkin", - middleware.ensureAuthenticated, - auth.checkin - ) - }, + auth.login + ); + app.put("/api/login", passport.authenticate("local"), auth.login); + app.put("/api/checkin", middleware.ensureAuthenticated, auth.checkin); + }, checkIfUserExists: function (req, res, next) { - var username = util.sanitizeName(req.body.username) + var username = util.sanitizeName(req.body.username); db.getUserByUsername(username).then((user) => { if (user) { - return res.json({ error: "user exists" }) + console.log(user); + return res.json({ error: "user exists" }); } - next() - }) + next(); + }); }, createUser: function (req, res, next) { if (req.body.password !== req.body.password2) { - return res.json({ error: "passwords don't match" }) + return res.json({ error: "passwords don't match" }); } - var username = util.sanitizeName(req.body.username) + var username = util.sanitizeName(req.body.username); var data = { username: username, realname: util.sanitize(req.body.realname), @@ -68,137 +64,147 @@ var auth = module.exports = { firstseen: util.now(), lastseen: util.now(), // lastsession: util.now(), - } - db.createUser(data).then(() => next()) + }; + db.createUser(data).then(() => next()); }, createMailboxes: function (req, res, next) { - var username = req.user.get('username') + var username = req.user.get("username"); Promise.all([ db.createMailbox({ - mbox: username + '.inbox', + mbox: username + ".inbox", owner: username, editable: 0, }), db.createMailbox({ - mbox: username + '.outbox', + mbox: username + ".outbox", owner: username, editable: 0, }), db.createMailbox({ - mbox: username + '.drafts', + mbox: username + ".drafts", owner: username, editable: 1, }), - ]).then(() => next()) + ]).then(() => next()); }, - uploadDefaultAvatar: function(req, res, next){ - fs.readFile('public/assets/img/profile.jpg', (err, buf) => { + uploadDefaultAvatar: function (req, res, next) { + fs.readFile("public/assets/img/profile.jpg", (err, buf) => { if (err) throw err; - var username = req.user.get('username') + var username = req.user.get("username"); upload.put({ - file: { - buffer: buf, - size: buf.length, - mimetype: 'image/jpeg', - }, - dirname: '/bucky/profile/', - filename: username + '.jpg', - preserveFilename: true, - success: (url) => { - next() - }, - }) + file: { + buffer: buf, + size: buf.length, + mimetype: "image/jpeg", + }, + dirname: "/bucky/profile/", + filename: username + ".jpg", + preserveFilename: true, + success: (url) => { + next(); + }, + }); }); }, login: function (req, res) { if (req.isAuthenticated()) { - var returnTo = req.session.returnTo - delete req.session.returnTo - console.log(">> logged in", req.user.get('username')) + var returnTo = req.session.returnTo; + delete req.session.returnTo; + console.log(">> logged in", req.user.get("username")); return res.json({ status: "OK", user: util.sanitizeUser(req.user), returnTo: returnTo || "/index", - }) + }); } res.json({ - error: 'bad credentials', - }) + error: "bad credentials", + }); }, - serializeUser: function (user, done) { - done(null, user.id); - }, + serializeUser: function (user, done) { + done(null, user.id); + }, - deserializeUser: function (id, done) { - db.getUser(id).then(function(user){ - done(! user, user) - }) - }, + deserializeUser: function (id, done) { + db.getUser(id).then(function (user) { + done(!user, user); + }); + }, - makePassword: function(username, password) { - var salt = username.substr(0, 2) // lol - return crypt(password, salt) + makePassword: function (username, password) { + var salt = username.substr(0, 2); // lol + return crypt(password, salt); // var shasum = crypto.createHash('sha1') // shasum.update(password) // return shasum.digest('hex'); - }, + }, - validPassword: function(user, password){ - return user.get('password') === auth.makePassword(user.get('username'), password); + validPassword: function (user, password) { + return ( + user.get("password") === auth.makePassword(user.get("username"), password) + ); }, - changePassword: function(req, res, next) { - if (! req.body.oldpassword && ! req.body.newpassword) return next() + changePassword: function (req, res, next) { + if (!req.body.oldpassword && !req.body.newpassword) return next(); if (req.body.newpassword !== req.body.newpassword2) { - return res.send({ error: 'Passwords don\'t match.' }) + return res.send({ error: "Passwords don't match." }); } - if (! auth.validPassword(res.user, req.body.oldpassword)) { - return res.send({ error: 'Password is incorrect.' }) + if (!auth.validPassword(res.user, req.body.oldpassword)) { + return res.send({ error: "Password is incorrect." }); } - var username = req.user.get('username') - var newPassword = auth.makePassword(username, req.body.newpassword) - res.user.set('password', newPassword) - res.user.save().then(() => next()).catch(err => res.send({ error: err })) + var username = req.user.get("username"); + var newPassword = auth.makePassword(username, req.body.newpassword); + res.user.set("password", newPassword); + res.user + .save() + .then(() => next()) + .catch((err) => res.send({ error: err })); }, - changePasswordDangerously: function(req, res, next){ - if (! req.body.password && ! req.body.newpassword) return next() + changePasswordDangerously: function (req, res, next) { + if (!req.body.password && !req.body.newpassword) return next(); if (req.body.newpassword !== req.body.newpassword2) { - return res.send({ error: 'Passwords don\'t match.' }) + return res.send({ error: "Passwords don't match." }); } - if (! auth.validPassword(req.user, req.body.password)) { - return res.send({ error: 'Password is incorrect.' }) + if (!auth.validPassword(req.user, req.body.password)) { + return res.send({ error: "Password is incorrect." }); } - var username = res.user.get('username') - var newPassword = auth.makePassword(username, req.body.newpassword) - res.user.set('password', newPassword) - res.user.save().then(() => next()).catch(err => res.send({ error: err })) + var username = res.user.get("username"); + var newPassword = auth.makePassword(username, req.body.newpassword); + res.user.set("password", newPassword); + res.user + .save() + .then(() => next()) + .catch((err) => res.send({ error: err })); }, - verifyLocalUser: function (username, password, done) { + verifyLocalUser: function (username, password, done) { // handle passwords!! - db.getUserByUsername(username).then(function(user){ - - // if (err) { return done(err); } - if (! user) { return done("no user") } + db.getUserByUsername(username).then(function (user) { + // if (err) { return done(err); } + if (!user) { + return done("no user"); + } // return done(null, user) - if (! user || ! auth.validPassword(user, password)) { - return done(null, false, { error: { message: 'Bad username/password.' } }) - } - return done(null, user); - }) - }, + if (!user || !auth.validPassword(user, password)) { + return done(null, false, { + error: { message: "Bad username/password." }, + }); + } + return done(null, user); + }); + }, checkin: function (req, res) { - var user = util.sanitizeUser(req.user) - res.json({ user: user }) + var user = util.sanitizeUser(req.user); + res.json({ user: user }); }, - logout: function (req, res) { - req.logout(); - res.redirect('/'); - }, - -} + logout: function (req, res) { + req.logout(); + res.redirect("/"); + }, +}); diff --git a/migrations/20150905232742_make_blob_fields_text.js b/migrations/20150905232742_make_blob_fields_text.js index fe50ba2..0ee2dbd 100644 --- a/migrations/20150905232742_make_blob_fields_text.js +++ b/migrations/20150905232742_make_blob_fields_text.js @@ -1,50 +1,61 @@ // mysqlcheck -u root --auto-repair --optimize --all-databases -exports.up = function(knex, Promise) { - var promise - knex.raw("ALTER DATABASE bucky CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci").then(function(){ console.log("OK") }) +exports.up = function (knex, Promise) { + var promise; + knex + .raw( + "ALTER DATABASE bucky CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci" + ) + .then(function () { + console.log("OK"); + }); var sql = [ -// these ones dont workb ecause of weird unicode somewhere in the db! -// "comments comment text", -// "messages body text", - "invites grass tinytext", - "keywords threads text", - "keywords ops text", - "keywords display tinytext", - "tags ops text", - "tags display tinytext", - "threads allowed tinytext", - "threads display tinytext", + // these ones dont workb ecause of weird unicode somewhere in the db! + "comments comment text", + "messages body text", + // "invites grass tinytext", + // "keywords threads text", + // "keywords ops text", + // "keywords display tinytext", + // "tags ops text", + // "tags display tinytext", + "threads allowed text", + // "threads display tinytext", "users grass text", "users keywords text", "users stickies text", - "users sink text", - "users display text", - "users boxes text", - ].map(function(s){ - var ss = s.split(" ") - var sz = [ - "ALTER TABLE", - ss[0], - "MODIFY COLUMN", - ss[1], - ss[2], - ].join(" ") - console.log(sz + ";") - promise = knex.raw([ - "ALTER TABLE", - ss[0], - "MODIFY COLUMN", - ss[1], - ss[2], - "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci", - ].join(" ")).then(function(){ Promise.resolve(); console.log("OK") }) - }) - return promise + // "users sink text", + // "users display text", + // "users boxes text", + ].map(function (s) { + var ss = s.split(" "); + var sz = ["ALTER TABLE", ss[0], "MODIFY COLUMN", ss[1], ss[2]].join(" "); + console.log(sz + ";"); + promise = knex + .raw( + [ + "ALTER TABLE", + ss[0], + "MODIFY COLUMN", + ss[1], + ss[2], + "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci", + ].join(" ") + ) + .then(function () { + Promise.resolve(); + console.log("OK"); + }) + .catch(function (error) { + console.log("error", s, error.message); + }); + }); + return promise; }; -exports.down = function(knex, Promise) { -/* +exports.down = function (knex, Promise) { + return () => {}; + /* "comments comment blob", "invites grass tinyblob", "keywords threads blob", @@ -61,5 +72,5 @@ exports.down = function(knex, Promise) { "users sink blob", "users display blob", "users boxes blob", -*/ +*/ }; diff --git a/package.json b/package.json index 6414578..ac9713d 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ "type": "git", "url": "git@ghghgh.us:bucky.git" }, - "author": "", - "license": "LNT", + "author": "jules", + "license": "UNLICENSED", "dependencies": { "body-parser": "^1.19.0", "bookshelf": "^0.13.3", diff --git a/public/assets/css/bucky.css b/public/assets/css/bucky.css index 60c1e9a..5ecb837 100644 --- a/public/assets/css/bucky.css +++ b/public/assets/css/bucky.css @@ -28,6 +28,7 @@ body.black { background: #211; color: #eee; } + small { font-size: 10px; } @@ -1626,7 +1627,8 @@ audio { #search .search_hit a { color: #; } - a { + a, + a:link { color: #38f; } a:visited { diff --git a/public/assets/js/lib/router.js b/public/assets/js/lib/router.js index ebdfa78..bedf0b9 100644 --- a/public/assets/js/lib/router.js +++ b/public/assets/js/lib/router.js @@ -1,133 +1,139 @@ var SiteRouter = Router.extend({ + el: "body", - el: "body", - - routes: { - "/": 'login', - "/index/:keyword": 'index', - "/index": 'index', - "/login": 'login', - "/logout": 'logout', - "/signup": 'signup', - "/details/:id": 'details', - "/details/:id/settings": 'threadSettings', - "/post": 'post', - "/post/:keyword": 'post', - "/comment/:id/edit": 'editComment', - "/keywords": 'keywords', - "/search": 'search', - "/mail": 'mailbox', - "/mail/:mailbox": 'mailbox', - "/mail/compose": 'compose', - "/mail/compose/:username": 'compose', - "/mail/read/:id": 'message', - "/mail/reply/:id": 'compose', - "/users": 'users', - "/users/all": 'usersAll', - "/profile": 'profile', - "/profile/:username": 'profile', - "/profile/:username/edit": 'editProfile', - "/adminz": 'adminz', - }, - - initialize: function(){ - $(".logout").click(() => this.logout()) + routes: { + "/": "login", + "/index/:keyword": "index", + "/index": "index", + "/stream": "stream", + "/login": "login", + "/logout": "logout", + "/signup": "signup", + "/details/:id": "details", + "/details/:id/settings": "threadSettings", + "/post": "post", + "/post/:keyword": "post", + "/comment/:id/edit": "editComment", + "/keywords": "keywords", + "/search": "search", + "/mail": "mailbox", + "/mail/:mailbox": "mailbox", + "/mail/compose": "compose", + "/mail/compose/:username": "compose", + "/mail/read/:id": "message", + "/mail/reply/:id": "compose", + "/users": "users", + "/users/all": "usersAll", + "/profile": "profile", + "/profile/:username": "profile", + "/profile/:username/edit": "editProfile", + "/adminz": "adminz", }, - - index: function(keyword){ - app.view = new IndexView () - app.view.load(keyword) + + initialize: function () { + $(".logout").click(() => this.logout()); }, - - login: function(){ - app.view = new LoginView () + + index: function (keyword) { + app.view = new IndexView(); + app.view.load(keyword); }, - - logout: function(){ - auth.log_out() - window.location.href = "/logout" + + stream: function (keyword) { + app.view = new StreamView(); + app.view.load(keyword); }, - - signup: function(){ - app.view = new SignupView () + + login: function () { + app.view = new LoginView(); }, - - details: function(id){ - app.view = new DetailsView () - app.view.load(id) + + logout: function () { + auth.log_out(); + window.location.href = "/logout"; }, - threadSettings: function(id){ - app.view = new DetailsView ({ settings: true }) - app.view.load(id) + signup: function () { + app.view = new SignupView(); }, - - editComment: function(id){ - app.view = new EditCommentForm () - app.view.load(id) + + details: function (id) { + app.view = new DetailsView(); + app.view.load(id); }, - keywords: function(){ - app.view = new KeywordsView () - app.view.load() + threadSettings: function (id) { + app.view = new DetailsView({ settings: true }); + app.view.load(id); }, - mailbox: function(box){ - app.view = new MailboxView () - app.view.load(box) + editComment: function (id) { + app.view = new EditCommentForm(); + app.view.load(id); }, - - message: function(id){ - app.view = new MessageView () - app.view.load(id) + + keywords: function () { + app.view = new KeywordsView(); + app.view.load(); }, - post: function(keyword){ - app.view = new ThreadForm () - app.view.load(keyword || "") + mailbox: function (box) { + app.view = new MailboxView(); + app.view.load(box); }, - - users: function(username){ - app.view = new UsersView () - app.view.load() + + message: function (id) { + app.view = new MessageView(); + app.view.load(id); }, - usersAll: function(username){ - app.view = new UsersView ({ all: true }) - app.view.load() + post: function (keyword) { + app.view = new ThreadForm(); + app.view.load(keyword || ""); }, - - profile: function(username){ - app.view = new ProfileView () - app.view.load(username || auth.user.username) + + users: function (username) { + app.view = new UsersView(); + app.view.load(); }, - - editProfile: function(username){ - app.view = new ProfileForm () - app.view.load(username || auth.user.username) + + usersAll: function (username) { + app.view = new UsersView({ all: true }); + app.view.load(); }, - - compose: function(username){ - app.view = new ComposeView () - app.view.load(username) + + profile: function (username) { + app.view = new ProfileView(); + app.view.load(username || auth.user.username); }, - search: function(){ - app.view = new SearchResults () - app.view.load() + editProfile: function (username) { + app.view = new ProfileForm(); + app.view.load(username || auth.user.username); }, - adminz: function(){ - app.view = new AdminView () - app.view.load() + compose: function (username) { + app.view = new ComposeView(); + app.view.load(username); }, - - error404: function(){ - $("content").hide() - $("#error_404").show() - $("h1").html("404 not found") - $("body").removeClass("loading") - $("#error_404").append('<a href="/"><img src="/assets/img/castro.jpg"></a>') + + search: function () { + app.view = new SearchResults(); + app.view.load(); }, -})
\ No newline at end of file + adminz: function () { + app.view = new AdminView(); + app.view.load(); + }, + + error404: function () { + $("content").hide(); + $("#error_404").show(); + $("h1").html("404 not found"); + $("body").removeClass("loading"); + $("#error_404").append( + '<a href="/"><img src="/assets/img/castro.jpg"></a>' + ); + }, +}); diff --git a/public/assets/js/lib/views/details/files.js b/public/assets/js/lib/views/details/files.js index bbea7da..cd6b776 100644 --- a/public/assets/js/lib/views/details/files.js +++ b/public/assets/js/lib/views/details/files.js @@ -46,8 +46,8 @@ var FilesView = FormView.extend({ this.resort(sort); } }, - - add: function(files) { + + add: function (files) { this.files = this.files.concat(files); this.resort(this.currentSort, this.reversed ? "desc" : "asc"); }, diff --git a/public/assets/js/lib/views/index/hootbox.js b/public/assets/js/lib/views/index/hootbox.js index 72fbfde..a5d2270 100644 --- a/public/assets/js/lib/views/index/hootbox.js +++ b/public/assets/js/lib/views/index/hootbox.js @@ -1,56 +1,63 @@ var HootBox = FormView.extend({ - el: "#hootbox", - - events: { - }, - + + events: {}, + action: "/api/thread/1/comment", - - initialize: function(){ - this.__super__.initialize.call(this) - this.template = this.$(".template").html() - this.$hoots = this.$("#hoots") - this.$comment = this.$("[name=comment]") + + initialize: function () { + this.__super__.initialize.call(this); + this.template = this.$(".template").html(); + this.$hoots = this.$("#hoots"); + this.$comment = this.$("[name=comment]"); + }, + + show: function () { + this.$el.show(); }, - - load: function(comments){ - if (!comments || !comments.length && ! this.options.required) { - this.$el.hide() - return + + hide: function () { + this.$el.hide(); + }, + + load: function (comments) { + if (!comments || (!comments.length && !this.options.required)) { + this.$el.hide(); + return; } - comments.forEach(this.appendComment.bind(this)) + comments.forEach(this.appendComment.bind(this)); }, - - parse: function(comment){ - var t = this.template.replace(/{{image}}/g, profile_image(comment.username)) - .replace(/{{username}}/g, comment.username) - .replace(/{{comment}}/g, tidy_urls(comment.comment, true)) - var $t = $(t) - return $t + + parse: function (comment) { + var t = this.template + .replace(/{{image}}/g, profile_image(comment.username)) + .replace(/{{username}}/g, comment.username) + .replace(/{{comment}}/g, tidy_urls(comment.comment, true)); + var $t = $(t); + return $t; }, - - prependComment: function(comment){ - var $el = this.parse(comment) - this.$hoots.prepend($el) + + prependComment: function (comment) { + var $el = this.parse(comment); + this.$hoots.prepend($el); }, - appendComment: function(comment){ - var $el = this.parse(comment) - this.$hoots.append($el) + appendComment: function (comment) { + var $el = this.parse(comment); + this.$hoots.append($el); }, - validate: function(){ - var errors = [] - var comment = $("[name=comment]").val() - if (! comment || ! comment.length) { - errors.push("Please enter a comment.") + validate: function () { + var errors = []; + var comment = $("[name=comment]").val(); + if (!comment || !comment.length) { + errors.push("Please enter a comment."); } - return errors.length ? errors : null + return errors.length ? errors : null; }, - success: function(data){ - this.prependComment(data.comment) - this.$("[name=comment]").val("") - } -})
\ No newline at end of file + success: function (data) { + this.prependComment(data.comment); + this.$("[name=comment]").val(""); + }, +}); diff --git a/public/assets/js/lib/views/index/index.js b/public/assets/js/lib/views/index/index.js index 08da455..cd5f650 100644 --- a/public/assets/js/lib/views/index/index.js +++ b/public/assets/js/lib/views/index/index.js @@ -1,57 +1,59 @@ var IndexView = View.extend({ - - events: { - }, + events: {}, action: "/api/index", keywordAction: "/api/keyword/", - initialize: function(opt){ + initialize: function (opt) { // opt.parent = parent - this.hootbox = new HootBox ({ parent: this }) - this.threadbox = new ThreadBox ({ parent: this }) - this.lastlog = new LastLog ({ parent: this }) - this.countdown = new Countdown ({ parent: this }) + this.hootbox = new HootBox({ parent: this }); + this.threadbox = new ThreadBox({ parent: this }); + this.lastlog = new LastLog({ parent: this }); + this.countdown = new Countdown({ parent: this }); }, - load: function(keyword){ - $("body").addClass("index") + load: function (keyword) { + $("body").addClass("index"); if (keyword) { - $(".subtitle").html('<a href="/">< Home</a> · <a href="/keywords">Keywords</a>') - this.threadbox.options.latest = false - this.threadbox.options.welcome = false - $.get(this.keywordAction + keyword, this.populate.bind(this)) - } - else { - this.hootbox.options.required = true - this.threadbox.options.latest = true - this.threadbox.options.welcome = true - $.get(this.action, this.populate.bind(this)) + $(".subtitle").html( + '<a href="/">< Home</a> · <a href="/keywords">Keywords</a>' + ); + this.threadbox.options.latest = false; + this.threadbox.options.welcome = false; + $.get(this.keywordAction + keyword, this.populate.bind(this)); + } else { + this.hootbox.options.required = true; + this.threadbox.options.latest = true; + this.threadbox.options.welcome = true; + $.get(this.action, this.populate.bind(this)); } }, - populate: function(data){ - $("body").removeClass('loading') - this.hootbox.load(data.hootbox) - this.threadbox.load(data) - this.lastlog.load(data.lastlog) + populate: function (data) { + $("body").removeClass("loading"); + this.data = data; + this.hootbox.load(data.hootbox); + this.threadbox.load(data); + this.lastlog.load(data.lastlog); if (data.mail.count) { - $(".alert").show().html( - "<a href='/mail'>" + - "You have " + - data.mail.count + - " new message" + - courtesy_s(data.mail.count) + - "!</a>") + $(".alert") + .show() + .html( + "<a href='/mail'>" + + "You have " + + data.mail.count + + " new message" + + courtesy_s(data.mail.count) + + "!</a>" + ); if (is_mobile) { - $("#content").prepend( $(".alert") ) + $("#content").prepend($(".alert")); } } - $(".search_form input").focus() + $(".search_form input").focus(); }, - success: function(){ - window.location.href = "/index" + success: function () { + window.location.href = "/index"; }, - -}) +}); diff --git a/public/assets/js/lib/views/stream/hootform.js b/public/assets/js/lib/views/stream/hootform.js new file mode 100644 index 0000000..368e616 --- /dev/null +++ b/public/assets/js/lib/views/stream/hootform.js @@ -0,0 +1,42 @@ +var HootForm = FormView.extend({ + el: "#hootform", + + events: {}, + + action: "/api/thread/1/comment", + + initialize: function (opt) { + this.__super__.initialize.call(this); + this.$comment = this.$("[name=comment]"); + }, + + show: function () { + this.$el.show(); + }, + + hide: function () { + this.$el.hide(); + }, + + load: function (comments) { + if (!comments || (!comments.length && !this.options.required)) { + this.$el.hide(); + return; + } + comments.forEach(this.appendComment.bind(this)); + }, + + validate: function () { + var errors = []; + var comment = $("[name=comment]").val(); + if (!comment || !comment.length) { + errors.push("Please enter a comment."); + } + return errors.length ? errors : null; + }, + + success: function (data) { + this.parent.onComment(data.comment); + this.$("[name=comment]").val(""); + }, +}); diff --git a/public/assets/js/lib/views/stream/hootstream.js b/public/assets/js/lib/views/stream/hootstream.js new file mode 100644 index 0000000..cdc7acf --- /dev/null +++ b/public/assets/js/lib/views/stream/hootstream.js @@ -0,0 +1,205 @@ +const IMAGE_REGEXP = /(.gif|.jpg|.jpeg|.png)$/gi; + +var HootStream = View.extend({ + el: "#hootstream", + + initialize: function ({ parent }) { + this.parent = parent; + this.$hootevents = this.$("#hootevents"); + this.hootTemplate = this.$(".hootTemplate").html(); + this.lastlogTemplate = this.$(".lastlogTemplate").html(); + this.fileTemplate = this.$(".fileTemplate").html(); + }, + + load: function (data) { + this.$hootevents.empty(); + const { order, threadLookup } = this.agglutinate(data); + console.log(data, threadLookup, order); + const $els = order.map( + function (item) { + return item.type === "thread" + ? this.renderThread(threadLookup[item.thread_id]).reduce( + ($el, $item) => $el.append($item), + $("<div class='thread'>") + ) + : item.type === "hoot" + ? this.renderHoot(item.data) + : item.type === "lastlog" + ? this.renderLastlog(item.data) + : "Unknown item"; + }.bind(this) + ); + this.$hootevents.append($els); + }, + + render: (template, object) => { + const rendered = Object.entries(object).reduce( + (newTemplate, [key, value]) => + newTemplate.replace(new RegExp(`{{${key}}}`, "g"), value), + template + ); + return $(rendered); + }, + + renderLastlog: function ({ username, lastseen }) { + const age = get_age(lastseen); + const age_ago = age === "now" ? age : `${age} ago`; + return this.render(this.lastlogTemplate, { + className: "hoot streamLastlog", + username, + age, + opacity: 0.6, + showAvatar: 0, + hoot: "last seen " + age_ago, + }); + }, + + renderHoot: function ({ + id, + thread, + date, + username, + hoot, + comment, + hidden, + className, + showAvatar, + }) { + return this.render(this.hootTemplate, { + username, + className: className ? `hoot ${className}` : "hoot", + image: profile_image(username), + showAvatar: showAvatar === false ? 0 : 1, + hoot: hoot || tidy_urls(comment, true), + age: get_age(date), + age_opacity: get_age_opacity(date), + }); + }, + + renderHoots: function ({ hoots, className }) { + const els = []; + for (hoot of hoots) { + els.push(this.renderHoot({ ...hoot, className })); + } + return els; + }, + + renderThread: function ({ thread, comments, files, images }) { + thread = thread.shift(); + // console.log(thread, comments, files, images); + const postedToday = +new Date() / 1000 - thread.date < 86400; + + if (postedToday) { + return [ + "<div class='divider dark'></div>", + this.renderHoot({ + hoot: `<a class="threadLink" href="/details/${thread.id}">${thread.title}</a>`, + username: thread.username, + className: "isRecent", + date: thread.lastmodified, + ...this.renderFiles(files), + ...this.renderHoots({ + hoots: comments.slice(0, 1), + tag: "first_post", + }), + ...this.renderHoots({ hoots: comments.slice(1) }), + }), + "<div class='divider'></div>", + ]; + // display very proud headline + // title, avatar, first image, full file list, + } else { + return [ + "<div class='divider dark'></div>", + this.renderHoot({ + hoot: `<a class="threadLink" href="/details/${thread.id}">${thread.title}</a>`, + username: thread.username, + className: "", + date: thread.lastmodified, + }), + this.renderFiles(files.slice(0, 3)), + ...this.renderHoots({ hoots: comments.slice(0, 1), tag: "first_post" }), + ...this.renderHoots({ hoots: comments.slice(1).slice(-3) }), + "<div class='divider'></div>", + ]; + // say "in ... " + // audio player OR recent file list + // recent 3 comments + } + }, + + renderFiles: function (files) { + if (!files.length) { + return null; + } + const $table = $("<table>"); + for (const file of files) { + const $el = this.renderFile(file); + $table.append($el); + } + return $table; + }, + + renderFile: function (file) { + var size = hush_size(file.size); + var datetime = verbose_date(file.date, true); + var date_class = carbon_date(file.date); + var link = make_link(file); + return this.render(this.fileTemplate, { + id: file.id, + username: file.username, + link, + filename: file.filename, + age: get_age(file.date), + date_class, + // date: datetime[0], + // time: datetime[1], + size_class: size[0], + size: size[1], + }); + }, + + agglutinate: ({ threads, comments, files, hootbox, lastlog }) => + [ + ...threads.map((thread) => [ + thread.id, + thread.createdate, + "thread", + thread, + ]), + ...comments + .filter((comment) => comment.thread !== 1) + .map((comment) => [comment.thread, comment.date, "comments", comment]), + ...files.map((file) => [ + file.thread, + file.date, + IMAGE_REGEXP.test(file.filename) ? "images" : "files", + file, + ]), + ...hootbox.map((hoot) => [1, hoot.date, "hoot", hoot]), + ...lastlog.map((user) => [1, user.lastseen, "lastlog", user]), + ] + .sort((a, b) => b[1] - a[1]) + .reduce( + ({ threadLookup, order }, [thread_id, date, type, data]) => { + if (type === "hoot") { + order.push({ type: "hoot", date, data }); + } else if (type === "lastlog") { + order.push({ type: "lastlog", date, data }); + } else if (thread_id !== 1) { + if (!(thread_id in threadLookup)) { + threadLookup[thread_id] = { + thread: [], + comments: [], + files: [], + images: [], + }; + order.push({ type: "thread", date, thread_id }); + } + threadLookup[thread_id][type].push(data); + } + return { threadLookup, order }; + }, + { threadLookup: {}, order: [] } + ), +}); diff --git a/public/assets/js/lib/views/stream/index.js b/public/assets/js/lib/views/stream/index.js new file mode 100644 index 0000000..d55a9c3 --- /dev/null +++ b/public/assets/js/lib/views/stream/index.js @@ -0,0 +1,69 @@ +var StreamView = View.extend({ + events: {}, + + action: "/api/stream", + keywordAction: "/api/keyword/", + + initialize: function (opt) { + // opt.parent = parent + // this.hootbox = new HootBox({ parent: this }); + this.hootform = new HootForm({ parent: this }); + this.hootstream = new HootStream({ parent: this }); + // this.threadbox = new ThreadBox({ parent: this }); + // this.lastlog = new LastLog({ parent: this }); + // this.countdown = new Countdown({ parent: this }); + }, + + load: function (keyword) { + $("body").addClass("index"); + // if (keyword) { + // $(".subtitle").html( + // '<a href="/">< Home</a> · <a href="/keywords">Keywords</a>' + // ); + // this.threadbox.options.latest = false; + // this.threadbox.options.welcome = false; + // $.get(this.keywordAction + keyword, this.populate.bind(this)); + // } else { + // this.hootbox.options.required = true; + // this.threadbox.options.latest = true; + // this.threadbox.options.welcome = true; + // } + $.get(this.action, this.populate.bind(this)); + }, + + populate: function (data) { + $("body").removeClass("loading"); + this.data = data; + this.hootstream.load(data); + // this.hootbox.load(data.hootbox); + // this.hootbox.hide(); + // this.threadbox.load(data); + // this.lastlog.load(data.lastlog); + if (data.mail.count) { + $(".alert") + .show() + .html( + "<a href='/mail'>" + + "You have " + + data.mail.count + + " new message" + + courtesy_s(data.mail.count) + + "!</a>" + ); + if (is_mobile) { + $("#content").prepend($(".alert")); + } + } + $(".search_form input").focus(); + }, + + onComment: function (comment) { + this.data.hootbox.comments.push(comment); + this.data.hootstream.comments.push(comment); + this.populate(this.data); + }, + + success: function () { + window.location.href = "/index"; + }, +}); diff --git a/public/assets/js/util/format.js b/public/assets/js/util/format.js index 6a23430..e6104e5 100644 --- a/public/assets/js/util/format.js +++ b/public/assets/js/util/format.js @@ -257,6 +257,29 @@ function get_age(t, long) { age /= 12; return r(age) + (long ? " years" : "y"); } +const ages = { + newest: 60 * 10, + newer: 60 * 60, + new: 60 * 60 * 8, + cool: 60 * 60 * 24, + cold: 60 * 60 * 24 * 7, + colder: 60 * 60 * 24 * 7 * 4, + coldest: 60 * 60 * 24 * 7 * 4 * 12, +}; +function get_age_opacity(t) { + var age = Math.abs(Date.now() / 1000 - t); + if (age < ages.newest) return 1; + if (age < ages.newer) + return lerp(norm(age, ages.newest, ages.newer), 1.0, 0.95); + if (age < ages.new) return lerp(norm(age, ages.newer, ages.new), 0.95, 0.9); + if (age < ages.cool) return lerp(norm(age, ages.new, ages.cool), 0.9, 0.85); + if (age < ages.cold) return lerp(norm(age, ages.cool, ages.cold), 0.85, 0.6); + if (age < ages.colder) + return lerp(norm(age, ages.cold, ages.colder), 0.6, 0.5); + if (age < ages.coldest) + return lerp(norm(age, ages.colder, ages.coldest), 0.5, 0.4); + return 0.39; +} function tidy_urls(s, short_urls) { var ret = s diff --git a/views/pages/stream.ejs b/views/pages/stream.ejs new file mode 100644 index 0000000..575d88a --- /dev/null +++ b/views/pages/stream.ejs @@ -0,0 +1,9 @@ +<% include ../partials/header %> + +<div class="subtitle"></div> + +<div class="bluebox alert"></div> +<% include ../partials/hootstream %> +<% include ../partials/threads %> + +<% include ../partials/footer %> diff --git a/views/partials/hootstream.ejs b/views/partials/hootstream.ejs new file mode 100644 index 0000000..988df01 --- /dev/null +++ b/views/partials/hootstream.ejs @@ -0,0 +1,276 @@ +<div id="hootstream"> + <div id="hootform"> + <form autocomplete="off"> + <input type="text" name="comment" autocomplete="off" required> + <button class="hoot"><%= hoot_text %></button> + </form> + <div class="errors"></div> + </div> + <div id="hootevents"> + + <script class="hootTemplate" type="text/html"> + <div class="{{className}}"> + <a class="userLink" href="/profile/{{username}}"> + {{username}} + </a> + <a class="avatarLink" href="/profile/{{username}}"> + <div class="avatar" title="{{username}}" style="background-image:url({{image}});opacity:{{showAvatar}};"></div> + </a> + <div class="text">{{hoot}}</div> + <div class="age date" style="opacity: {{age_opacity}}">{{age}}</div> + </div> + </script> + + <script class="lastlogTemplate" type="text/html"> + <div class="{{className}}" style="opacity: {{opacity}}"> + <a class="userLink" href="/profile/{{username}}"> + {{username}} + </a> + <div class="text"> + {{hoot}} + </div> + <div class="age date">{{age}}</div> + </div> + </script> + + <script class="fileTemplate" type="text/html"> + <tr class="file"> + <td class="filename"> + <a href="{{link}}" title="{{id}}" class="file">{{filename}}</a> + </td> + <td class="size"> + {{size}} + </td> + <td class="age {{date_class}}"> + {{age}} + </td> + </tr> + </script> + + <script class="imageTemplate" type="text/html"> + <div class="image"><a href="{{link}}"><img src="{{src}}"></a></div> + </script> + + <script class="hootFirstPost" type="text/html"> + </script> + + <script class="hootImages" type="text/html"> + </script> + + </div> +</div> + +<style> + #hootstream { + margin-top: 3rem; + font-size: 16px; + } + #hootstream form { + display: flex; + width: 100%; + justify-content: space-between; + align-items: center; + flex-direction: row; + margin-bottom: 0.75rem; + } + #hootstream form input { + flex: 1; + font-size: 16px; + padding: 0.25rem; + margin-bottom: 0; + } + #hootstream form button { + margin: 0; + padding: 0.25rem; + margin-left: 0.25rem; + min-width: 2.5rem; + } + #hootevents { + font-size: 14px; + } + #hootevents .hoot { + display: flex; + flex-flow: row wrap; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 0.5rem; + width: 100%; + } + #hootevents .age { + width: 40px; + display: flex; + justify-content: flex-end; + align-items: flex-start; + text-align: right; + margin: 0px 0.5rem 0px 0px; + } + #hootevents .userLink { + color: #def; + opacity: 0.8; + text-align: right; + width: 50px; + whitespace: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-right: 0.5rem; + } + #hootevents .avatar { + width: 1.5rem; + height: 1.5rem; + background-size: cover; + border-radius: 2px; + border: 1px solid #000; + margin: 0px 0.5rem 0px 0px; + box-shadow: 0 0 1px 1px rgba(128,128,128,0.1); + position: relative; + top: -0.25rem; + } + #hootevents .hoot .text { + text-align: left; + flex: 1; + margin: 0px 0.5rem 0px 0px; + } + + #hootevents .hoot.isRecent .threadLink { + font-weight: bold; + text-shadow: 0 0 3px #fed; + } + #hootevents .hoot .threadLink { + color: #38f; + font-size: 16px; + } + + /** DiViDErZ */ + + #hootevents .divider { + border-top: 0; + border-bottom: 1px solid rgba(88,88,88,0.4); + margin: 0 auto; + width: 80%; + height: 0rem; + margin-bottom: 1rem; + } + #hootevents .divider.dark { + border-top: 1px solid rgba(88,88,88,0.4); + border-bottom: 0; + height: 1rem; + margin-bottom: 0rem; + margin-top: 0rem; + } + #hootevents .thread { + background: linear-gradient(90deg, rgba(127,127,127,0.0), rgba(127,127,127,0.05), rgba(127,127,127,0.0)); + } + + /** LastLog */ + + #hootevents .hoot.streamLastlog { + font-size: 13px; + margin-bottom: 1rem; + } + #hootevents .hoot.streamLastlog .userLink { + margin-right: 0.375rem; + } + + /** Files */ + #hootevents table, + #hootevents tr, + #hootevents td { + padding: 0; + margin: 0; + } + #hootevents table { + width: calc(100% - 24px); + margin-left: 24px; + margin-bottom: 0.5rem; + } + #hootevents table td:first-of-type { + border-left: 1px solid; + padding-right: 0.5rem; + } + #hootevents table td.filename { + overflow: hidden; + text-overflow: ellipsis; + } + #hootevents table td { + border-top: 1px solid; + border-bottom: 1px solid; + padding: 0.5rem; + white-space: nowrap; + } + #hootevents table td:last-of-type { + border-right: 1px solid; + } + + #hootevents table { + margin-top: 1rem; + margin-bottom: 1rem; + } + #hootevents table td { + color: rgba(192,192,192,1.0); + border-color: rgba(96,96,96,0.5); + background: transparent; + } + #hootevents table td:first-of-type { + border-color: rgba(96,96,96,0.5); + } + #hootevents table td:last-of-type { + border-color: rgba(96,96,96,0.5); + } + #hootevents table td { height: 45px; position: relative; } + #hootevents table td::after { + content: ''; + width: 100%; + height: 100%; + background-color: #def; + opacity: 0.05; + position: absolute; + top: 0; + left: 0; + z-index: -1; + } + #hootevents table td.age { + align-items: center; + } + + /** HOOT FORM */ + + #hootform { + margin-bottom: 2rem; + } + #hootform input[type="text"] { + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid rgba(127,127,127,0.2); + padding-bottom: 3px; + font-size: 14px; + font-family: "Trebuchet MS", sans-serif; + background: transparent; + outline: 0; + width: 270px; + transition: background 200ms; + background: white; + } + #hootform input[type="text"]:invalid { + background: transparent; + } + #hootform input[type="text"]:focus { + border-bottom: 1px solid rgba(40,20,20,0.2); + border-radius: 2px 2px 0 0; + color: #211; + } + #hootform input[type="text"]:focus { + border-bottom-color: rgba(127,127,127,0.2); + } + #hootform input[type="text"]:invalid { + caret-color: #888; + } + @media (prefers-color-scheme: dark) { + .search_form input[type="text"]:focus { + border-bottom-color: rgba(127,127,127,0.2); + } + .search_form input[type="text"]:invalid { + caret-color: #888; + } + } +</style>
\ No newline at end of file diff --git a/views/partials/scripts.ejs b/views/partials/scripts.ejs index 765bf61..6cfaf4d 100644 --- a/views/partials/scripts.ejs +++ b/views/partials/scripts.ejs @@ -32,6 +32,10 @@ <script src="/assets/js/lib/views/search/results.js"></script> +<script src="/assets/js/lib/views/stream/index.js"></script> +<script src="/assets/js/lib/views/stream/hootstream.js"></script> +<script src="/assets/js/lib/views/stream/hootform.js"></script> + <script src="/assets/js/lib/views/keywords/keywords.js"></script> <script src="/assets/js/lib/views/keywords/newkeyword.js"></script> @@ -153,8 +153,8 @@ ansi-styles@^4.1.0: ansicolors@~0.2.1: version "0.2.1" - resolved "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz" - integrity sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8= + resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.2.1.tgz#be089599097b74a5c9c4a84a0cdbcdb62bd87aef" + integrity sha512-tOIuy1/SK/dr94ZA0ckDohKXNeBNqZ4us6PjMVLs5h1w2GBB6uPtOknp2+VF4F/zcy9LI70W+Z+pE2Soajky1w== anymatch@~3.1.2: version "3.1.2" @@ -312,8 +312,8 @@ bluebird@^3.4.3, bluebird@^3.5.1: bn.js@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-2.0.0.tgz" - integrity sha1-glxBB/f6eJN47hsNhr4FA9esdDs= + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-2.0.0.tgz#825c4107f7fa789378ee1b0d86be0503d7ac743b" + integrity sha512-NmOLApC80+n+P28y06yHgwGlOCkq/X4jRh5s590959FZXSrM+I/61h0xxuIaYsg0mD44mEAZYG/rnclWuRoz+A== bodec@^0.1.0: version "0.1.0" @@ -456,8 +456,8 @@ call-bind@^1.0.0: cardinal@0.4.4: version "0.4.4" - resolved "https://registry.npmjs.org/cardinal/-/cardinal-0.4.4.tgz" - integrity sha1-ylu2iltRG5D+k7ms6km97lwyv+I= + resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-0.4.4.tgz#ca5bb68a5b511b90fe93b9acea49bdee5c32bfe2" + integrity sha512-3MxV0o9wOpQcobrcSrRpaSxlYkohCcZu0ytOjJUww/Yo/223q4Ecloo7odT+M0SI5kPgb1JhvSaF4EEuVXOLAQ== dependencies: ansicolors "~0.2.1" redeyed "~0.4.0" @@ -666,9 +666,9 @@ core-js@^2.4.0: integrity sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw== core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== create-error@~0.3.1: version "0.3.1" @@ -841,8 +841,8 @@ dotenv@^1.2.0: double-ended-queue@2.0.0-0: version "2.0.0-0" - resolved "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.0.0-0.tgz" - integrity sha1-eEf9ocAPtyIkWv+DZDpIh2cO/Sw= + resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.0.0-0.tgz#7847fda1c00fb722245aff83643a4887670efd2c" + integrity sha512-t5ouWOpItmHrm0J0+bX/cFrIjBFWnJkk5LbIJq6bbU/M4aLX2c3LrM4QYsBptwvlPe3WzdpQefQ0v1pe/A5wjg== ee-first@1.1.1: version "1.1.1" @@ -919,8 +919,8 @@ esprima@^4.0.0, esprima@^4.0.1: esprima@~1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" - integrity sha1-n1V+CPw7TSbs6d00+Pv0drYlha0= + resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad" + integrity sha512-rp5dMKN8zEs9dfi9g0X1ClLmV//WRyk/R15mppFNICIFRG5P92VP7Z04p8pk++gABo9W2tY+kHyu6P1mEHgmTA== estraverse@^4.2.0: version "4.3.0" @@ -1481,12 +1481,12 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.3, inherits@~2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -inherits@2.0.4: +inherits@2.0.4, inherits@~2.0.1: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -1670,8 +1670,8 @@ is-windows@^1.0.1, is-windows@^1.0.2: isarray@0.0.1: version "0.0.1" - resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== isarray@1.0.0, isarray@~1.0.0: version "1.0.0" @@ -1847,8 +1847,8 @@ log-driver@^1.2.7: lru-cache@2.5.0: version "2.5.0" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" - integrity sha1-2COIrpyWC+y+oMc7uet5tsbOmus= + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.5.0.tgz#d82388ae9c960becbea0c73bb9eb79b6c6ce9aeb" + integrity sha512-dVmQmXPBlTgFw77hm60ud//l2bCuDKkqC2on1EBoM7s9Urm9IQDrnujwZ93NFnAq0dVZ0HBXTS7PwEG+YE7+EQ== lru-cache@^5.1.1: version "5.1.1" @@ -2071,8 +2071,8 @@ mute-stream@~0.0.4: mysql2@^0.15.8: version "0.15.8" - resolved "https://registry.npmjs.org/mysql2/-/mysql2-0.15.8.tgz" - integrity sha1-8WZQtvfFu1aLNFEeIbr+NtWonvE= + resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-0.15.8.tgz#f16650b6f7c5bb568b34511e21bafe36d5a89ef1" + integrity sha512-3x5o6C20bfwJYPSoT74MOoad7/chJoq4qXHDL5VAuRBBrIyErovLoj04Dz/5EY9X2kTxWSGNiTegtxpROTd2YQ== dependencies: bn.js "2.0.0" cardinal "0.4.4" @@ -2082,8 +2082,8 @@ mysql2@^0.15.8: named-placeholders@0.1.3: version "0.1.3" - resolved "https://registry.npmjs.org/named-placeholders/-/named-placeholders-0.1.3.tgz" - integrity sha1-NTd27iWa0QUifhOFLu9CFaxjHoQ= + resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-0.1.3.tgz#353776ee259ad105227e13852eef4215ac631e84" + integrity sha512-Mt79RtxZ6MYTIEemPGv/YDKpbuavcAyGHb0r37xB2mnE5jej3uBzc4+nzOeoZ4nZiii1M32URKt9IjkSTZAmTA== dependencies: lru-cache "2.5.0" @@ -2567,8 +2567,8 @@ read@^1.0.4: readable-stream@1.0.33: version "1.0.33" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz" - integrity sha1-OjYN1mwbHX/UcFOJhg7aHQ9hEmw= + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.33.tgz#3a360dd66c1b1d7fd4705389860eda1d0f61126c" + integrity sha512-72KxhcKi8bAvHP/cyyWSP+ODS5ef0DIRs0OzrhGXw31q41f19aoELCbvd42FjhpyEDxQMRiiC5rq9rfE5PzTqg== dependencies: core-util-is "~1.0.0" inherits "~2.0.1" @@ -2614,8 +2614,8 @@ rechoir@^0.6.2: redeyed@~0.4.0: version "0.4.4" - resolved "https://registry.npmjs.org/redeyed/-/redeyed-0.4.4.tgz" - integrity sha1-N+mQpvKyGyoRwuakj9QTVpjLqX8= + resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-0.4.4.tgz#37e990a6f2b21b2a11c2e6a48fd4135698cba97f" + integrity sha512-pnk1vsaNLu1UAAClKsImKz9HjBvg9i8cbRqTRzJbiCjGF0fZSMqpdcA5W3juO3c4etFvTrabECkq9wjC45ZyxA== dependencies: esprima "~1.0.4" @@ -3080,8 +3080,8 @@ streamsearch@0.1.2: string_decoder@0.10.31, string_decoder@~0.10.x: version "0.10.31" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== string_decoder@~1.0.0: version "1.0.3" |
