webdev-service-backend / src /controllers /blog-related.controller.ts
underrate's picture
Upload 25 files
86cd98c verified
Raw
History Blame Contribute Delete
3.53 kB
import { Request, Response } from 'express';
import prisma from '../models/prisma';
// Type for blog post with author
type BlogPostWithAuthor = {
id: string;
title: string;
slug: string;
content: string;
excerpt: string | null;
featuredImage: string | null;
tags: string | null;
status: string;
publishedAt: Date | null;
createdAt: Date;
updatedAt: Date;
authorId: string;
author: { name: string | null } | null;
};
// GET /api/blog/:slug/related (Public)
export const getRelatedPosts = async (req: Request, res: Response) => {
try {
const { slug } = req.params;
// Find the current post to get its tags
const currentPost = await prisma.blogPost.findFirst({
where: { slug }
});
if (!currentPost) {
return res.status(404).json({ error: 'Post not found' });
}
// Parse tags
let tags: string[] = [];
try {
if (currentPost.tags) tags = JSON.parse(currentPost.tags);
} catch (e) {
// ignore
}
// If no tags, just return 3 latest posts excluding current
if (tags.length === 0) {
const latest = await prisma.blogPost.findMany({
where: {
status: 'PUBLISHED',
slug: { not: slug },
publishedAt: { lte: new Date() }
},
orderBy: { publishedAt: 'desc' },
take: 3,
include: { author: { select: { name: true } } }
});
return res.status(200).json(latest);
}
// SQLite doesn't natively support querying inside JSON arrays easily without raw queries or specific extensions,
// so we'll fetch recently published posts and manually filter/score them by tag overlap.
// For a production app with Postgres, you'd use a raw query or better JSON filtering.
const allOtherPosts = await prisma.blogPost.findMany({
where: {
status: 'PUBLISHED',
slug: { not: slug },
publishedAt: { lte: new Date() }
},
orderBy: {
publishedAt: 'desc'
},
take: 50, // Limit to recent 50 to avoid massive memory usage
include: { author: { select: { name: true } } }
});
// Score based on shared tags
const scoredPosts: { post: BlogPostWithAuthor; score: number }[] = allOtherPosts.map((post: BlogPostWithAuthor) => {
let postTags: string[] = [];
try {
if (post.tags) postTags = JSON.parse(post.tags);
} catch (e) { }
const intersection = tags.filter(t => postTags.includes(t));
return { post, score: intersection.length };
});
// Sort by score (desc), then date (desc) implicitly from the DB query
scoredPosts.sort((a: { post: BlogPostWithAuthor; score: number }, b: { post: BlogPostWithAuthor; score: number }) => b.score - a.score);
// Take top 3
const related = scoredPosts.slice(0, 3).map((s: { post: BlogPostWithAuthor; score: number }) => s.post);
return res.status(200).json(related);
} catch (error) {
console.error('Error fetching related posts:', error);
return res.status(500).json({ error: 'Internal server error' });
}
}