initial commit
This commit is contained in:
166
app/api/topic-content-image/route.ts
Normal file
166
app/api/topic-content-image/route.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
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 }
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user