ai-wpa/app/api/topics/route.ts

142 lines
3.9 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import connectDB from '@/lib/mongodb'
import TopicModel, { transformToTopics } from '@/models/topic'
import { authMiddleware } from '@/lib/auth-middleware'
import { ZodError } from 'zod'
export async function GET(request: NextRequest) {
try {
await connectDB()
const searchParams = request.nextUrl.searchParams
const page = parseInt(searchParams.get('page') || '1')
const limit = parseInt(searchParams.get('limit') || '10')
const search = searchParams.get('q') || ''
const tag = searchParams.get('tag') || ''
const authorId = searchParams.get('authorId') || '' // For filtering by user's topics
const includeDrafts = searchParams.get('includeDrafts') === 'true' // For user's own topics
const fetchAll = searchParams.get('fetchAll') // Admin panel support
const skip = (page - 1) * limit
// Build query based on parameters
const query: any = {}
// Authentication check for private operations (drafts or admin)
let user = null
try {
user = await authMiddleware(request)
} catch (error) {
// Non-authenticated request - that's okay for public topic listing
}
// If requesting user's own topics with drafts, verify authentication
if (includeDrafts && !user) {
return NextResponse.json(
{
success: false,
error: { message: 'Authentication required to view drafts', code: 'AUTH_REQUIRED' },
},
{ status: 401 }
)
}
// Filter by author if specified
if (authorId) {
query.authorId = authorId
}
// Handle draft filtering
if (fetchAll === 'true') {
// Admin panel - show all posts (drafts and published)
// No draft filtering applied
} else if (includeDrafts && user && (authorId === user.id || !authorId)) {
// User requesting their own topics (including drafts)
if (!authorId) {
query.authorId = user.id
}
} else {
// Public topic listing - only published topics
query.isDraft = false
}
// Search functionality
if (search) {
query.$or = [
{ title: { $regex: search, $options: 'i' } },
{ excerpt: { $regex: search, $options: 'i' } },
{ content: { $regex: search, $options: 'i' } },
]
}
// Filter by tag
if (tag) {
query['tags.name'] = { $regex: new RegExp(tag, 'i') }
}
// Get total count with same filters
const totalTopics = await TopicModel.countDocuments(query)
// Get paginated topics sorted by publishedAt
const topics = await TopicModel.find(query)
.sort({ publishedAt: -1 })
.skip(skip)
.limit(limit)
.lean()
if (!topics || topics.length === 0) {
return NextResponse.json(
{
success: true,
data: [],
pagination: {
total: 0,
page,
limit,
totalPages: 0,
},
},
{ status: 200 }
)
}
// Transform the topics using our utility function
const transformedTopics = transformToTopics(topics)
return NextResponse.json(
{
success: true,
data: transformedTopics,
pagination: {
total: totalTopics,
page,
limit,
totalPages: Math.ceil(totalTopics / limit),
},
},
{ status: 200 }
)
} catch (error) {
console.error('Error fetching topics:', error)
if (error instanceof ZodError) {
return NextResponse.json(
{
success: false,
error: {
message: 'Data validation failed',
code: 'VALIDATION_ERROR',
details: error.issues,
},
},
{ status: 400 }
)
}
return NextResponse.json(
{
success: false,
error: { message: 'Failed to fetch topics', code: 'SERVER_ERROR' },
},
{ status: 500 }
)
}
}