262 lines
6.5 KiB
TypeScript
262 lines
6.5 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
|
import TopicModel, { transformToTopic } from '@/models/topic'
|
|
import { authMiddleware } from '@/lib/auth-middleware'
|
|
import { uploadFile, moveToPermStorage, getFileUrl, generateUniqueFilename } from '@/lib/file-vault'
|
|
import { z } from 'zod'
|
|
|
|
// Validation schema for topic updates
|
|
const topicUpdateSchema = z.object({
|
|
title: z.string().min(1).max(100).optional(),
|
|
author: z.string().min(1).optional(),
|
|
excerpt: z.string().min(1).max(200).optional(),
|
|
content: z.string().min(1).optional(),
|
|
contentRTE: z.unknown().optional(),
|
|
contentImages: z.array(z.string()).optional(),
|
|
tags: z
|
|
.array(
|
|
z.object({
|
|
id: z.string().optional(),
|
|
name: z.string(),
|
|
})
|
|
)
|
|
.min(1)
|
|
.optional(),
|
|
featured: z.boolean().optional(),
|
|
isDraft: z.boolean().optional(),
|
|
coverImage: z.string().url().optional(),
|
|
coverImageKey: z.string().optional(),
|
|
})
|
|
|
|
// GET /api/topic/[id] - Get topic by ID
|
|
export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
|
try {
|
|
const { id } = await params
|
|
|
|
const topic = await TopicModel.findOne({ id }).lean()
|
|
|
|
if (!topic) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: { message: 'Topic not found', code: 'NOT_FOUND' },
|
|
},
|
|
{ status: 404 }
|
|
)
|
|
}
|
|
|
|
// Check if topic is draft and user is not the owner
|
|
if (topic.isDraft) {
|
|
const user = await authMiddleware(request)
|
|
if (!user || user.id !== topic.authorId) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: { message: 'Topic not found', code: 'NOT_FOUND' },
|
|
},
|
|
{ status: 404 }
|
|
)
|
|
}
|
|
}
|
|
|
|
const transformedTopic = transformToTopic(topic)
|
|
|
|
return NextResponse.json(
|
|
{
|
|
success: true,
|
|
data: transformedTopic,
|
|
},
|
|
{ status: 200 }
|
|
)
|
|
} catch (error) {
|
|
console.error('Error fetching topic:', error)
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: { message: 'Failed to fetch topic', code: 'SERVER_ERROR' },
|
|
},
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
}
|
|
|
|
// PUT /api/topic/[id] - Update topic by ID
|
|
export async function PUT(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
|
try {
|
|
// Authentication required for updating topics
|
|
const user = await authMiddleware(request)
|
|
if (!user) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: { message: 'Authentication required', code: 'AUTH_REQUIRED' },
|
|
},
|
|
{ status: 401 }
|
|
)
|
|
}
|
|
|
|
const { id } = await params
|
|
|
|
// Find the topic first
|
|
const existingTopic = await TopicModel.findOne({ id })
|
|
|
|
if (!existingTopic) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: { message: 'Topic not found', code: 'NOT_FOUND' },
|
|
},
|
|
{ status: 404 }
|
|
)
|
|
}
|
|
|
|
// Check ownership - users can only update their own topics
|
|
if (existingTopic.authorId !== user.id) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: { message: 'You can only edit your own topics', code: 'FORBIDDEN' },
|
|
},
|
|
{ status: 403 }
|
|
)
|
|
}
|
|
|
|
const body = await request.json()
|
|
|
|
// Validate request body
|
|
const validatedData = topicUpdateSchema.parse(body)
|
|
|
|
console.log('Updating topic for user:', user.id, 'Topic ID:', id)
|
|
|
|
try {
|
|
// Update the topic with new data
|
|
Object.assign(existingTopic, validatedData)
|
|
existingTopic.publishedAt = Date.now() // Update timestamp
|
|
|
|
const updatedTopic = await existingTopic.save()
|
|
|
|
console.log('Topic updated successfully:', updatedTopic.id)
|
|
|
|
return NextResponse.json(
|
|
{
|
|
success: true,
|
|
data: updatedTopic,
|
|
},
|
|
{ status: 200 }
|
|
)
|
|
} catch (error) {
|
|
console.error('Failed to update topic:', error)
|
|
|
|
if (error.code === 11000) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: { message: 'A topic with this slug already exists', code: 'DUPLICATE_SLUG' },
|
|
},
|
|
{ status: 409 }
|
|
)
|
|
}
|
|
|
|
throw error
|
|
}
|
|
} catch (error) {
|
|
console.error('Error updating topic:', error)
|
|
|
|
if (error instanceof z.ZodError) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: {
|
|
message: 'Validation failed',
|
|
code: 'VALIDATION_ERROR',
|
|
details: error.issues,
|
|
},
|
|
},
|
|
{ status: 400 }
|
|
)
|
|
}
|
|
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: { message: 'Failed to update topic', code: 'SERVER_ERROR' },
|
|
},
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
}
|
|
|
|
// DELETE /api/topic/[id] - Delete topic by ID
|
|
export async function DELETE(
|
|
request: NextRequest,
|
|
{ params }: { params: Promise<{ id: string }> }
|
|
) {
|
|
try {
|
|
// Authentication required for deleting topics
|
|
const user = await authMiddleware(request)
|
|
if (!user) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: { message: 'Authentication required', code: 'AUTH_REQUIRED' },
|
|
},
|
|
{ status: 401 }
|
|
)
|
|
}
|
|
|
|
const { id } = await params
|
|
|
|
// Find the topic first
|
|
const existingTopic = await TopicModel.findOne({ id })
|
|
|
|
if (!existingTopic) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: { message: 'Topic not found', code: 'NOT_FOUND' },
|
|
},
|
|
{ status: 404 }
|
|
)
|
|
}
|
|
|
|
// Check ownership - users can only delete their own topics
|
|
if (existingTopic.authorId !== user.id) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: { message: 'You can only delete your own topics', code: 'FORBIDDEN' },
|
|
},
|
|
{ status: 403 }
|
|
)
|
|
}
|
|
|
|
console.log('Deleting topic for user:', user.id, 'Topic ID:', id)
|
|
|
|
try {
|
|
await TopicModel.deleteOne({ id })
|
|
|
|
console.log('Topic deleted successfully:', id)
|
|
|
|
return NextResponse.json(
|
|
{
|
|
success: true,
|
|
message: 'Topic deleted successfully',
|
|
},
|
|
{ status: 200 }
|
|
)
|
|
} catch (error) {
|
|
console.error('Failed to delete topic:', error)
|
|
throw error
|
|
}
|
|
} catch (error) {
|
|
console.error('Error deleting topic:', error)
|
|
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: { message: 'Failed to delete topic', code: 'SERVER_ERROR' },
|
|
},
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
}
|