ai-wpa/app/api/topic-content-image/route.ts

167 lines
4.6 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { authMiddleware } from '@/lib/auth-middleware'
import { uploadFile, moveToPermStorage, getFileUrl, generateUniqueFilename } from '@/lib/file-vault'
// POST /api/topic-content-image - Upload images for topic content (rich text editor)
export async function POST(request: NextRequest) {
try {
// Authentication required for image uploads
const user = await authMiddleware(request)
if (!user) {
return NextResponse.json(
{
success: false,
error: { message: 'Authentication required', code: 'AUTH_REQUIRED' },
},
{ status: 401 }
)
}
const formData = await request.formData()
const image = formData.get('file') as File
if (!image) {
return NextResponse.json(
{
success: false,
error: { message: 'Image file is required', code: 'MISSING_IMAGE' },
},
{ status: 400 }
)
}
// Validate file type
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp']
if (!allowedTypes.includes(image.type)) {
return NextResponse.json(
{
success: false,
error: {
message: 'Invalid file type. Only JPEG, PNG, GIF, and WebP are allowed',
code: 'INVALID_FILE_TYPE',
},
},
{ status: 400 }
)
}
// Validate file size (5MB limit)
const maxSize = 5 * 1024 * 1024 // 5MB
if (image.size > maxSize) {
return NextResponse.json(
{
success: false,
error: {
message: 'File size too large. Maximum size is 5MB',
code: 'FILE_TOO_LARGE',
},
},
{ status: 400 }
)
}
console.log('Processing topic content image upload for user:', user.id)
try {
// Convert file to buffer (exactly like dev-portfolio)
const bytes = await image.arrayBuffer()
const buffer = Buffer.from(bytes)
// Upload directly to external API (no temp storage needed)
const uploadResult = await uploadFile(buffer, image.name, image.type, user.id)
const imageUrl = uploadResult.url
const uniqueFilename = uploadResult.filename
const permanentPath = uploadResult.url
console.log('Topic content image uploaded successfully:', {
originalName: image.name,
permanentPath,
uploadedBy: user.id,
})
// Return URL format expected by BlockNote editor
return NextResponse.json(
{
success: true,
data: {
url: imageUrl, // BlockNote expects URL here
fileName: uniqueFilename,
path: permanentPath,
size: image.size,
type: image.type,
uploadedBy: user.id,
uploadedAt: new Date().toISOString(),
},
},
{ status: 200 }
)
} catch (error) {
console.error('Failed to upload topic content image:', error)
throw error
}
} catch (error) {
console.error('Error uploading topic content image:', error)
return NextResponse.json(
{
success: false,
error: { message: 'Failed to upload image', code: 'UPLOAD_ERROR' },
},
{ status: 500 }
)
}
}
// GET /api/topic-content-image - Get image metadata or URL by path (like dev-portfolio)
export async function GET(request: NextRequest) {
try {
// Authentication required to access image metadata
const user = await authMiddleware(request)
if (!user) {
return NextResponse.json(
{
success: false,
error: { message: 'Authentication required', code: 'AUTH_REQUIRED' },
},
{ status: 401 }
)
}
const { searchParams } = new URL(request.url)
const imagePath = searchParams.get('path')
if (!imagePath) {
return NextResponse.json(
{
success: false,
error: { message: 'Image path parameter is required', code: 'MISSING_PATH' },
},
{ status: 400 }
)
}
// Get accessible URL for the image (like dev-portfolio)
const imageUrl = await getFileUrl(imagePath)
return NextResponse.json(
{
success: true,
data: {
path: imagePath,
url: imageUrl,
},
},
{ status: 200 }
)
} catch (error) {
console.error('Error serving topic content image:', error)
return NextResponse.json(
{
success: false,
error: { message: 'Failed to retrieve image', code: 'SERVER_ERROR' },
},
{ status: 500 }
)
}
}