147 lines
3.4 KiB
JavaScript
147 lines
3.4 KiB
JavaScript
// import knex from './knex';
|
|
import { verifyPrivilege } from './privileges';
|
|
import knex from './knex';
|
|
import { HttpError } from './errors';
|
|
|
|
import { fetchShelf } from './shelves';
|
|
import { fetchUsers } from './users';
|
|
|
|
function curateDatabasePost(post, {
|
|
shelf, users, votes,
|
|
}) {
|
|
const curatedPost = {
|
|
id: post.id,
|
|
title: post.title,
|
|
body: post.body,
|
|
link: post.link,
|
|
shelfId: post.shelf_id,
|
|
createdAt: post.created_at,
|
|
shelf,
|
|
user: users.find((user) => user.id === post.user_id),
|
|
votes: votes[post.id],
|
|
commentCount: Number(post.comment_count),
|
|
};
|
|
|
|
return curatedPost;
|
|
}
|
|
|
|
function curatePostVote(vote) {
|
|
return {
|
|
tally: Number(vote.tally),
|
|
total: Number(vote.total),
|
|
bump: !!vote.bump,
|
|
sink: !!vote.sink,
|
|
};
|
|
}
|
|
|
|
async function fetchPostVotes(postIds, user) {
|
|
const votes = await knex('posts_votes')
|
|
.select(
|
|
'post_id',
|
|
knex.raw('sum(value) as tally'),
|
|
knex.raw('count(id) as total'),
|
|
...(user ? [
|
|
knex.raw('bool_or(user_id = :userId and value = 1) as bump', { userId: knex.raw(user.id) }),
|
|
knex.raw('bool_or(user_id = :userId and value = -1) as sink', { userId: knex.raw(user.id) })]
|
|
: []),
|
|
)
|
|
.whereIn('post_id', postIds)
|
|
.groupBy('post_id');
|
|
|
|
return Object.fromEntries(votes.map((vote) => [vote.post_id, curatePostVote(vote)]));
|
|
}
|
|
|
|
async function fetchShelfPosts(shelfId, { user, limit = 100 } = {}) {
|
|
const shelf = await fetchShelf(shelfId);
|
|
|
|
const posts = await knex('posts')
|
|
.select('posts.*', knex.raw('count(comments.id) as comment_count'))
|
|
.leftJoin('comments', 'comments.post_id', 'posts.id')
|
|
.where('shelf_id', shelf.id)
|
|
.orderBy('created_at', 'desc')
|
|
.groupBy('posts.id')
|
|
.limit(limit);
|
|
|
|
const [users, votes] = await Promise.all([
|
|
fetchUsers(posts.map((post) => post.user_id)),
|
|
fetchPostVotes(posts.map((post) => post.id), user),
|
|
]);
|
|
|
|
return posts.map((post) => curateDatabasePost(post, { shelf, users, votes }));
|
|
}
|
|
|
|
async function fetchPost(postId, user) {
|
|
const post = await knex('posts')
|
|
.select('posts.*', knex.raw('count(comments.id) as comment_count'))
|
|
.leftJoin('comments', 'comments.post_id', 'posts.id')
|
|
.leftJoin('posts_votes', 'posts_votes.post_id', 'posts.id')
|
|
.where('posts.id', postId)
|
|
.groupBy('posts.id')
|
|
.first();
|
|
|
|
if (!post) {
|
|
throw new HttpError({
|
|
statusMessage: 'This post does not exist',
|
|
statusCode: 404,
|
|
});
|
|
}
|
|
|
|
const [shelf, users, votes] = await Promise.all([
|
|
fetchShelf(post.shelf_id),
|
|
fetchUsers([post.user_id]),
|
|
fetchPostVotes([post.id], user),
|
|
]);
|
|
|
|
return curateDatabasePost(post, { shelf, users, votes });
|
|
}
|
|
|
|
async function createPost(post, shelfId, user) {
|
|
await verifyPrivilege('createPost', user);
|
|
|
|
const shelf = await fetchShelf(shelfId);
|
|
|
|
if (!shelf) {
|
|
throw new HttpError({
|
|
statusMessage: 'The target shelf does not exist',
|
|
statusCode: 404,
|
|
});
|
|
}
|
|
|
|
const [postEntry] = await knex('posts')
|
|
.insert({
|
|
title: post.title,
|
|
body: post.body,
|
|
link: post.link,
|
|
shelf_id: shelf.id,
|
|
user_id: user.id,
|
|
})
|
|
.returning('*');
|
|
|
|
const users = await fetchUsers([postEntry.user_id]);
|
|
|
|
return curateDatabasePost(postEntry, { shelf, users });
|
|
}
|
|
|
|
async function votePost(postId, value, user) {
|
|
await knex('posts_votes')
|
|
.insert({
|
|
value,
|
|
post_id: postId,
|
|
user_id: user.id,
|
|
})
|
|
.onConflict(['post_id', 'user_id'])
|
|
.merge()
|
|
.returning('value');
|
|
|
|
const votes = await fetchPostVotes([postId], user);
|
|
|
|
return votes[postId];
|
|
}
|
|
|
|
export {
|
|
createPost,
|
|
fetchPost,
|
|
fetchShelfPosts,
|
|
votePost,
|
|
};
|