Files
soul/app/api/upload/route.ts
卡若 4dd2f9f4a7 feat: 完善后台管理+搜索功能+分销系统
主要更新:
- 后台菜单精简(9项→6项)
- 新增搜索功能(敏感信息过滤)
- 分销绑定和提现系统完善
- 数据库初始化API(自动修复表结构)
- 用户管理:显示绑定关系详情
- 小程序:上下章导航优化、匹配页面重构
- 修复hydration和数据类型问题
2026-01-25 19:37:59 +08:00

135 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 图片上传API
* 支持上传图片到public/assets目录
*/
import { NextRequest, NextResponse } from 'next/server'
import { writeFile, mkdir } from 'fs/promises'
import { existsSync } from 'fs'
import path from 'path'
// 支持的图片格式
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml']
const MAX_SIZE = 5 * 1024 * 1024 // 5MB
/**
* POST - 上传图片
*/
export async function POST(request: NextRequest) {
try {
const formData = await request.formData()
const file = formData.get('file') as File | null
const folder = formData.get('folder') as string || 'uploads'
if (!file) {
return NextResponse.json({
success: false,
error: '请选择要上传的文件'
}, { status: 400 })
}
// 验证文件类型
if (!ALLOWED_TYPES.includes(file.type)) {
return NextResponse.json({
success: false,
error: '不支持的文件格式,仅支持 JPG、PNG、GIF、WebP、SVG'
}, { status: 400 })
}
// 验证文件大小
if (file.size > MAX_SIZE) {
return NextResponse.json({
success: false,
error: '文件大小不能超过5MB'
}, { status: 400 })
}
// 生成唯一文件名
const timestamp = Date.now()
const randomStr = Math.random().toString(36).substring(2, 8)
const ext = file.name.split('.').pop() || 'jpg'
const fileName = `${timestamp}_${randomStr}.${ext}`
// 确保上传目录存在
const uploadDir = path.join(process.cwd(), 'public', 'assets', folder)
if (!existsSync(uploadDir)) {
await mkdir(uploadDir, { recursive: true })
}
// 写入文件
const filePath = path.join(uploadDir, fileName)
const bytes = await file.arrayBuffer()
const buffer = Buffer.from(bytes)
await writeFile(filePath, buffer)
// 返回可访问的URL
const url = `/assets/${folder}/${fileName}`
return NextResponse.json({
success: true,
data: {
url,
fileName,
size: file.size,
type: file.type
}
})
} catch (error) {
console.error('[Upload API] 上传失败:', error)
return NextResponse.json({
success: false,
error: '上传失败: ' + (error as Error).message
}, { status: 500 })
}
}
/**
* DELETE - 删除图片
*/
export async function DELETE(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const filePath = searchParams.get('path')
if (!filePath) {
return NextResponse.json({
success: false,
error: '请指定要删除的文件路径'
}, { status: 400 })
}
// 安全检查确保只能删除assets目录下的文件
if (!filePath.startsWith('/assets/')) {
return NextResponse.json({
success: false,
error: '无权限删除此文件'
}, { status: 403 })
}
const fullPath = path.join(process.cwd(), 'public', filePath)
if (!existsSync(fullPath)) {
return NextResponse.json({
success: false,
error: '文件不存在'
}, { status: 404 })
}
const { unlink } = await import('fs/promises')
await unlink(fullPath)
return NextResponse.json({
success: true,
message: '文件删除成功'
})
} catch (error) {
console.error('[Upload API] 删除失败:', error)
return NextResponse.json({
success: false,
error: '删除失败: ' + (error as Error).message
}, { status: 500 })
}
}