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

138 lines
3.5 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import connectDB from '@/lib/mongodb'
import TopicModel from '@/models/topic'
import { authMiddleware } from '@/lib/auth-middleware'
// Response schema for type safety
const DashboardStatsSchema = z.object({
success: z.boolean(),
data: z.object({
stats: z.object({
totalTopics: z.number(),
publishedTopics: z.number(),
draftTopics: z.number(),
totalViews: z.number(),
}),
userTopics: z.array(
z.object({
id: z.string(),
title: z.string(),
slug: z.string(),
publishedAt: z.number(),
isDraft: z.boolean(),
views: z.number().optional(),
excerpt: z.string(),
tags: z.array(
z.object({
name: z.string(),
})
),
})
),
}),
})
export type DashboardResponse = z.infer<typeof DashboardStatsSchema>
export async function GET(request: NextRequest) {
try {
// Authenticate user
const user = await authMiddleware(request)
if (!user) {
return NextResponse.json(
{
success: false,
error: { message: 'Authentication required', code: 'UNAUTHORIZED' },
},
{ status: 401 }
)
}
// Connect to database
await connectDB()
// MongoDB Aggregation Pipeline for user topic statistics
const userTopics = await TopicModel.aggregate([
{
// Match topics by user ownership
$match: {
authorId: user.id,
},
},
{
// Sort by publication date (newest first)
$sort: {
publishedAt: -1,
},
},
{
// Project only the fields we need for the dashboard
$project: {
id: '$id',
title: '$title',
slug: '$slug',
publishedAt: '$publishedAt',
isDraft: '$isDraft',
views: { $ifNull: ['$views', 0] }, // Default to 0 if views field doesn't exist
excerpt: '$excerpt',
tags: {
$map: {
input: '$tags',
as: 'tag',
in: {
name: '$$tag.name',
},
},
},
},
},
])
// Calculate statistics from the aggregated data
const totalTopics = userTopics.length
const publishedTopics = userTopics.filter((topic) => !topic.isDraft).length
const draftTopics = userTopics.filter((topic) => topic.isDraft).length
const totalViews = userTopics.reduce((sum, topic) => sum + (topic.views || 0), 0)
// Prepare response data
const responseData = {
success: true,
data: {
stats: {
totalTopics,
publishedTopics,
draftTopics,
totalViews,
},
userTopics,
},
}
// Validate response format matches frontend expectations
const validatedResponse = DashboardStatsSchema.parse(responseData)
return NextResponse.json(validatedResponse, { status: 200 })
} catch (error) {
console.error('Dashboard API error:', error)
if (error instanceof z.ZodError) {
return NextResponse.json(
{
success: false,
error: { message: 'Invalid response format', code: 'VALIDATION_ERROR' },
},
{ status: 500 }
)
}
return NextResponse.json(
{
success: false,
error: { message: 'Failed to fetch dashboard data', code: 'INTERNAL_ERROR' },
},
{ status: 500 }
)
}
}