summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--bucky/app/api.js25
-rw-r--r--bucky/app/bucky.js1196
-rw-r--r--bucky/app/pages.js222
-rw-r--r--bucky/app/site.js5
-rw-r--r--bucky/bin/build-scripts.js43
-rw-r--r--bucky/db/index.js33
-rw-r--r--bucky/util/auth.js250
-rw-r--r--migrations/20150905232742_make_blob_fields_text.js91
-rw-r--r--package.json4
-rw-r--r--public/assets/css/bucky.css4
-rw-r--r--public/assets/js/lib/router.js212
-rw-r--r--public/assets/js/lib/views/details/files.js4
-rw-r--r--public/assets/js/lib/views/index/hootbox.js91
-rw-r--r--public/assets/js/lib/views/index/index.js78
-rw-r--r--public/assets/js/lib/views/stream/hootform.js42
-rw-r--r--public/assets/js/lib/views/stream/hootstream.js205
-rw-r--r--public/assets/js/lib/views/stream/index.js69
-rw-r--r--public/assets/js/util/format.js23
-rw-r--r--views/pages/stream.ejs9
-rw-r--r--views/partials/hootstream.ejs276
-rw-r--r--views/partials/scripts.ejs4
-rw-r--r--yarn.lock58
23 files changed, 1867 insertions, 1081 deletions
diff --git a/README.md b/README.md
index 26bd142..35af133 100644
--- a/README.md
+++ b/README.md
@@ -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="/">&lt; Home</a> &middot; <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="/">&lt; Home</a> &middot; <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="/">&lt; Home</a> &middot; <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>
diff --git a/yarn.lock b/yarn.lock
index a01b287..93068ec 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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"