266 lines
7.4 KiB
JavaScript
266 lines
7.4 KiB
JavaScript
const { where } = require("sequelize");
|
|
const asyncHandler = require("../middlewares/asyncHandler");
|
|
const Article = require("../models/Article");
|
|
const Tag = require("../models/Tag");
|
|
const User = require("../models/User");
|
|
const ErrorResponse = require("../util/errorResponse");
|
|
const slugify = require("slugify");
|
|
|
|
const {
|
|
appendFollowers,
|
|
appendFavorites,
|
|
appendTagList,
|
|
} = require("../util/helpers");
|
|
|
|
const includeOptions = [
|
|
{
|
|
model: Tag,
|
|
as: "tagLists",
|
|
attributes: ["name"],
|
|
through: { attributes: [] },
|
|
},
|
|
{ model: User, as: "author", attributes: { exclude: ["email", "password"] } },
|
|
];
|
|
|
|
module.exports.getArticles = asyncHandler(async (req, res, next) => {
|
|
const { tag, author, favorited, limit = 20, offset = 0 } = req.query;
|
|
const { loggedUser } = req;
|
|
|
|
const searchOptions = {
|
|
include: [
|
|
{
|
|
model: Tag,
|
|
as: "tagLists",
|
|
attributes: ["name"],
|
|
through: { attributes: [] }, // ? this will remove the rows from the join table
|
|
// where: tag ? { name: tag } : {},
|
|
...(tag && { where: { name: tag } }),
|
|
},
|
|
{
|
|
model: User,
|
|
as: "author",
|
|
attributes: { exclude: ["password", "email"] },
|
|
// where: author ? { username: author } : {},
|
|
...(author && { where: { username: author } }),
|
|
},
|
|
],
|
|
limit: parseInt(limit),
|
|
offset: parseInt(offset),
|
|
order: [["createdAt", "DESC"]],
|
|
distinct: true,
|
|
};
|
|
|
|
let articles = { rows: [], count: 0 };
|
|
|
|
if (favorited) {
|
|
const user = await User.findOne({ where: { username: favorited } });
|
|
|
|
articles.rows = await user.getFavorites(searchOptions);
|
|
articles.count = await user.countFavorites();
|
|
} else {
|
|
articles = await Article.findAndCountAll(searchOptions);
|
|
}
|
|
|
|
for (let article of articles.rows) {
|
|
const articleTags = await article.getTagLists();
|
|
// const articleTags = article.tagLists;
|
|
appendTagList(articleTags, article);
|
|
await appendFollowers(loggedUser, article);
|
|
await appendFavorites(loggedUser, article);
|
|
|
|
delete article.dataValues.Favorites;
|
|
}
|
|
|
|
res
|
|
.status(200)
|
|
.json({ articles: articles.rows, articlesCount: articles.count });
|
|
});
|
|
|
|
module.exports.createArticle = asyncHandler(async (req, res, next) => {
|
|
const { loggedUser } = req;
|
|
|
|
fieldValidation(req.body.article.title, next);
|
|
fieldValidation(req.body.article.description, next);
|
|
fieldValidation(req.body.article.body, next);
|
|
|
|
const { title, description, body, tagList } = req.body.article;
|
|
const slug = slugify(title, { lower: true });
|
|
const slugInDB = await Article.findOne({ where: { slug: slug } });
|
|
if (slugInDB) next(new ErrorResponse("Title already exists", 400));
|
|
|
|
const article = await Article.create({
|
|
title: title,
|
|
description: description,
|
|
body: body,
|
|
});
|
|
|
|
for (const tag of tagList) {
|
|
const tagInDB = await Tag.findByPk(tag.trim());
|
|
|
|
if (tagInDB) {
|
|
await article.addTagList(tagInDB);
|
|
} else if (tag.length > 2) {
|
|
const newTag = await Tag.create({ name: tag.trim() });
|
|
await article.addTagList(newTag);
|
|
}
|
|
}
|
|
delete loggedUser.dataValues.token;
|
|
|
|
article.dataValues.tagList = tagList;
|
|
article.setAuthor(loggedUser);
|
|
article.dataValues.author = loggedUser;
|
|
await appendFollowers(loggedUser, loggedUser);
|
|
await appendFavorites(loggedUser, article);
|
|
|
|
res.status(201).json({ article });
|
|
});
|
|
|
|
module.exports.deleteArticle = asyncHandler(async (req, res, next) => {
|
|
const { slug } = req.params;
|
|
const { loggedUser } = req;
|
|
|
|
const article = await Article.findOne({
|
|
where: { slug: slug },
|
|
include: includeOptions,
|
|
});
|
|
|
|
if (!article) next(new ErrorResponse("Article not found", 404));
|
|
|
|
if (article.authorId !== loggedUser.id)
|
|
return next(new ErrorResponse("Unauthorized", 401));
|
|
|
|
await article.destroy();
|
|
|
|
res.status(200).json({ article });
|
|
});
|
|
|
|
module.exports.updateArticle = asyncHandler(async (req, res, next) => {
|
|
const { slug } = req.params;
|
|
const { loggedUser } = req;
|
|
|
|
const article = await Article.findOne({
|
|
where: { slug: slug },
|
|
include: includeOptions,
|
|
});
|
|
|
|
if (!article) next(new ErrorResponse("Article not found", 404));
|
|
|
|
if (article.authorId !== loggedUser.id)
|
|
return next(new ErrorResponse("Unauthorized", 401));
|
|
|
|
const { title, description, body } = req.body.article;
|
|
|
|
const slugInDB = await Article.findOne({
|
|
where: { slug: slugify(title ? title : article.title, { lower: true }) },
|
|
});
|
|
|
|
if (slugInDB && slugInDB.slug !== slug)
|
|
return next(new ErrorResponse("Title already exists", 400));
|
|
|
|
await article.update({
|
|
title: title ? title : article.title,
|
|
description: description ? description : article.description,
|
|
body: body ? body : article.body,
|
|
});
|
|
|
|
const articleTags = await article.getTagLists();
|
|
appendTagList(articleTags, article);
|
|
await appendFollowers(loggedUser, article);
|
|
await appendFavorites(loggedUser, article);
|
|
|
|
res.status(200).json({ article });
|
|
});
|
|
|
|
module.exports.articlesFeed = asyncHandler(async (req, res, next) => {
|
|
const { loggedUser } = req;
|
|
|
|
const { limit = 3, offset = 0 } = req.query;
|
|
const authors = await loggedUser.getFollowing();
|
|
|
|
const articles = await Article.findAndCountAll({
|
|
include: includeOptions,
|
|
limit: parseInt(limit),
|
|
offset: offset * limit,
|
|
order: [["createdAt", "DESC"]],
|
|
where: { authorId: authors.map((author) => author.id) },
|
|
distinct: true,
|
|
});
|
|
|
|
for (const article of articles.rows) {
|
|
const articleTags = await article.getTagLists();
|
|
|
|
appendTagList(articleTags, article);
|
|
await appendFollowers(loggedUser, article);
|
|
await appendFavorites(loggedUser, article);
|
|
}
|
|
|
|
res.json({ articles: articles.rows, articlesCount: articles.count });
|
|
});
|
|
|
|
module.exports.getArticle = asyncHandler(async (req, res, next) => {
|
|
const { loggedUser } = req;
|
|
const { slug } = req.params;
|
|
|
|
const article = await Article.findOne({
|
|
where: { slug: slug },
|
|
include: includeOptions,
|
|
});
|
|
|
|
if (!article) return next(new ErrorResponse("Article not found", 404));
|
|
|
|
const articleTags = await article.getTagLists();
|
|
appendTagList(articleTags, article);
|
|
await appendFollowers(loggedUser, article);
|
|
await appendFavorites(loggedUser, article);
|
|
|
|
res.status(200).json({ article });
|
|
});
|
|
|
|
module.exports.addFavoriteArticle = asyncHandler(async (req, res, next) => {
|
|
const { loggedUser } = req;
|
|
const { slug } = req.params;
|
|
|
|
const article = await Article.findOne({
|
|
where: { slug: slug },
|
|
include: includeOptions,
|
|
});
|
|
|
|
if (!article) return next(new ErrorResponse("Article not found", 404));
|
|
|
|
await loggedUser.addFavorite(article);
|
|
|
|
const articleTags = await article.getTagLists();
|
|
appendTagList(articleTags, article);
|
|
await appendFollowers(loggedUser, article);
|
|
await appendFavorites(loggedUser, article);
|
|
|
|
res.status(200).json({ article });
|
|
});
|
|
|
|
module.exports.deleteFavoriteArticle = asyncHandler(async (req, res, next) => {
|
|
const { loggedUser } = req;
|
|
const { slug } = req.params;
|
|
|
|
const article = await Article.findOne({
|
|
where: { slug: slug },
|
|
include: includeOptions,
|
|
});
|
|
|
|
if (!article) return next(new ErrorResponse("Article not found", 404));
|
|
|
|
await loggedUser.removeFavorite(article);
|
|
|
|
const articleTags = await article.getTagLists();
|
|
appendTagList(articleTags, article);
|
|
await appendFollowers(loggedUser, article);
|
|
await appendFavorites(loggedUser, article);
|
|
|
|
res.status(200).json({ article });
|
|
});
|
|
|
|
const fieldValidation = (field, next) => {
|
|
if (!field) {
|
|
return next(new ErrorResponse(`Missing fields`, 400));
|
|
}
|
|
};
|