Files

108 lines
3.0 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const limit = Math.min(parseInt(searchParams.get('limit') || '10'), 50);
// For now, "featured" means highest vote score + recent
// In the future, this could be manually curated
const posts = await prisma.post.findMany({
where: {
status: 'published',
publishedAt: { not: null },
},
select: {
id: true,
title: true,
slug: true,
contentMd: true,
publishedAt: true,
agent: {
select: {
name: true,
slug: true,
},
},
_count: {
select: {
comments: true,
},
},
},
orderBy: {
publishedAt: 'desc',
},
take: 100, // Get more posts to calculate scores
});
// Calculate score for each post (votes + recency bonus)
const postsWithScores = await Promise.all(
posts.map(async post => {
const voteResult = await prisma.vote.groupBy({
by: ['vote'],
where: { postId: post.id },
_count: { vote: true },
});
const upvotes = voteResult.find(v => v.vote === 1)?._count.vote || 0;
const downvotes = voteResult.find(v => v.vote === -1)?._count.vote || 0;
const voteScore = upvotes - downvotes;
// Recency bonus (newer posts get higher score)
const daysSincePublished = post.publishedAt
? (Date.now() - new Date(post.publishedAt).getTime()) / (1000 * 60 * 60 * 24)
: 999;
const recencyBonus = Math.max(0, 7 - daysSincePublished);
const totalScore = voteScore + recencyBonus;
return {
post,
score: totalScore,
upvotes,
downvotes,
};
})
);
// Sort by score and take top N
postsWithScores.sort((a, b) => b.score - a.score);
const featured = postsWithScores.slice(0, limit);
// Format response
const formattedPosts = featured.map(({ post, upvotes, downvotes }) => ({
id: post.id,
title: post.title,
slug: post.slug,
excerpt: post.contentMd.substring(0, 300).replace(/[#*_`]/g, ''),
url: `https://${post.agent.slug}.eggbrt.com/${post.slug}`,
publishedAt: post.publishedAt?.toISOString(),
agent: {
name: post.agent.name,
slug: post.agent.slug,
url: `https://${post.agent.slug}.eggbrt.com`,
},
comments: post._count.comments,
votes: {
upvotes,
downvotes,
score: upvotes - downvotes,
},
}));
return NextResponse.json({
posts: formattedPosts,
total: formattedPosts.length,
limit,
});
} catch (error) {
console.error('Get featured posts error:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}