Files
oldvine_cms/routes/blog.js
yotakii 13a004fa40 edit
2026-01-13 16:08:18 +03:00

315 lines
7.2 KiB
JavaScript

const express = require('express');
const router = express.Router();
const BlogPost = require('../models/BlogPost');
const adminAuth = require('../middleware/adminAuth');
const { body, validationResult } = require('express-validator');
function makeSlug(input = "") {
return input
.toString()
.trim()
.toLowerCase()
.replace(/[^\p{Letter}\p{Number}]+/gu, "-")
.replace(/(^-|-$)+/g, "");
}
async function ensureUniqueSlug(baseSlug) {
let slug = baseSlug || `post-${Date.now()}`;
const exists = await BlogPost.findOne({ slug }).select("_id");
if (!exists) return slug;
return `${slug}-${Date.now()}`;
}
// @route GET /api/blog
// @desc Get published blog posts
// @access Public
router.get('/', async (req, res) => {
try {
const {
category,
tag,
page = 1,
limit = 10,
featured = false
} = req.query;
const skip = (page - 1) * limit;
const posts = await BlogPost.getPublished({
category,
tag,
limit: parseInt(limit),
skip,
featured: featured === 'true'
});
const total = await BlogPost.countDocuments({
status: 'published',
publishedAt: { $lte: new Date() },
...(category && { category }),
...(tag && { tags: tag }),
...(featured === 'true' && { isFeatured: true })
});
res.json({
success: true,
data: {
posts,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
pages: Math.ceil(total / limit)
}
}
});
} catch (error) {
console.error('Get blog posts error:', error);
res.status(500).json({
success: false,
message: 'Error fetching blog posts'
});
}
});
// @route GET /api/blog/:slug
// @desc Get single blog post by slug
// @access Public
router.get('/:slug', async (req, res) => {
try {
const { slug } = req.params;
const post = await BlogPost.findOne({
slug,
status: 'published',
publishedAt: { $lte: new Date() }
}).populate('author', 'firstName lastName avatar');
if (!post) {
return res.status(404).json({
success: false,
message: 'Blog post not found'
});
}
// Increment views
await BlogPost.incrementViews(post._id);
// Get related posts
const relatedPosts = await post.getRelatedPosts(3);
res.json({
success: true,
data: {
post,
relatedPosts
}
});
} catch (error) {
console.error('Get blog post error:', error);
res.status(500).json({
success: false,
message: 'Error fetching blog post'
});
}
});
// @route GET /api/blog/admin/all
// @desc Get all blog posts (admin)
// @access Private (Admin)
router.get('/admin/all', adminAuth, async (req, res) => {
try {
const { status, category, page = 1, limit = 20 } = req.query;
const skip = (page - 1) * limit;
const query = {};
if (status) query.status = status;
if (category) query.category = category;
const posts = await BlogPost.find(query)
.populate('author', 'firstName lastName avatar')
.sort({ createdAt: -1 })
.skip(skip)
.limit(parseInt(limit));
const total = await BlogPost.countDocuments(query);
res.json({
success: true,
data: {
posts,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
pages: Math.ceil(total / limit)
}
}
});
} catch (error) {
console.error('Get all blog posts error:', error);
res.status(500).json({
success: false,
message: 'Error fetching blog posts'
});
}
});
// @route POST /api/blog
// @desc Create new blog post
// @access Private (Admin)
router.post('/', adminAuth, [
body('title').notEmpty().trim().withMessage('Title is required'),
body('excerpt').notEmpty().trim().withMessage('Excerpt is required'),
body('content').notEmpty().withMessage('Content is required'),
body('category').notEmpty().withMessage('Category is required')
], async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
errors: errors.array()
});
}
const postData = {
...req.body,
author: req.admin.id
};
if (!postData.slug || !postData.slug.trim()) {
postData.slug = makeSlug(postData.title || "");
}
if (!postData.slug) {
postData.slug = `post-${Date.now()}`;
}
postData.slug = await ensureUniqueSlug(postData.slug);
const post = new BlogPost(postData);
await post.save();
res.status(201).json({
success: true,
message: 'Blog post created successfully',
data: { post }
});
} catch (error) {
console.error('Create blog post error:', error);
res.status(500).json({
success: false,
message: 'Error creating blog post'
});
}
});
// @route PUT /api/blog/:id
// @desc Update blog post
// @access Private (Admin)
router.put('/:id', adminAuth, async (req, res) => {
try {
const { id } = req.params;
const post = await BlogPost.findByIdAndUpdate(
id,
{ $set: req.body },
{ new: true, runValidators: true }
).populate('author', 'firstName lastName avatar');
if (!post) {
return res.status(404).json({
success: false,
message: 'Blog post not found'
});
}
res.json({
success: true,
message: 'Blog post updated successfully',
data: { post }
});
} catch (error) {
console.error('Update blog post error:', error);
res.status(500).json({
success: false,
message: 'Error updating blog post'
});
}
});
// @route DELETE /api/blog/:id
// @desc Delete blog post
// @access Private (Admin)
router.delete('/:id', adminAuth, async (req, res) => {
try {
const { id } = req.params;
const post = await BlogPost.findByIdAndDelete(id);
if (!post) {
return res.status(404).json({
success: false,
message: 'Blog post not found'
});
}
res.json({
success: true,
message: 'Blog post deleted successfully'
});
} catch (error) {
console.error('Delete blog post error:', error);
res.status(500).json({
success: false,
message: 'Error deleting blog post'
});
}
});
// @route GET /api/blog/categories/list
// @desc Get list of all categories
// @access Public
router.get('/categories/list', async (req, res) => {
try {
const categories = await BlogPost.distinct('category');
res.json({
success: true,
data: { categories }
});
} catch (error) {
console.error('Get categories error:', error);
res.status(500).json({
success: false,
message: 'Error fetching categories'
});
}
});
// @route GET /api/blog/tags/list
// @desc Get list of all tags
// @access Public
router.get('/tags/list', async (req, res) => {
try {
const tags = await BlogPost.distinct('tags');
res.json({
success: true,
data: { tags }
});
} catch (error) {
console.error('Get tags error:', error);
res.status(500).json({
success: false,
message: 'Error fetching tags'
});
}
});
module.exports = router;