import { NextRequest } from "next/server"; import { getServerSession } from "next-auth/next"; import { authOptions } from "@/lib/auth"; import { RecordingService } from "@/lib/services/recording.service"; import { ApiResponseHandler } from "@/lib/utils/api-response"; import { AuthenticationError, ValidationError } from "@/lib/errors/app-error"; import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"; const s3 = new S3Client({ region: process.env.AWS_REGION || "us-east-1", credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, }, }); export async function POST(request: NextRequest) { try { const session = await getServerSession(authOptions); if (!session?.user?.id) { throw new AuthenticationError(); } const formData = await request.formData(); const audioFile = formData.get("audio") as File; const audioUrl = formData.get("audioUrl") as string; const duration = formData.get("duration") as string; const title = formData.get("title") as string; const fileSize = formData.get("fileSize") as string; const mimeType = formData.get("mimeType") as string; let finalAudioUrl: string; let finalFileSize: number; let finalMimeType: string; // 如果有音频文件,先上传到 S3 if (audioFile) { if (!duration) { throw new ValidationError("缺少录音时长"); } // 验证文件类型 if (!audioFile.type.startsWith("audio/")) { throw new ValidationError("文件类型必须是音频"); } // 验证文件大小 (50MB 限制) const maxSize = 50 * 1024 * 1024; if (audioFile.size > maxSize) { throw new ValidationError("文件大小不能超过 50MB"); } // 生成唯一的文件名 const userId = session.user.id; const timestamp = Date.now(); const uniqueFileName = `recordings/${userId}/${timestamp}-recording.webm`; // 上传到 S3 const arrayBuffer = await audioFile.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); const command = new PutObjectCommand({ Bucket: process.env.AWS_S3_BUCKET!, Key: uniqueFileName, Body: buffer, ContentType: audioFile.type, }); await s3.send(command); // 生成 S3 URL - 修复格式 const region = process.env.AWS_REGION || "us-east-1"; const bucketName = process.env.AWS_S3_BUCKET!; // 根据区域生成正确的 S3 URL let s3Url: string; if (region === "us-east-1") { // us-east-1 使用特殊格式 s3Url = `https://${bucketName}.s3.amazonaws.com/${uniqueFileName}`; } else { // 其他区域使用标准格式 s3Url = `https://${bucketName}.s3.${region}.amazonaws.com/${uniqueFileName}`; } finalAudioUrl = s3Url; finalFileSize = audioFile.size; finalMimeType = audioFile.type; } else if (audioUrl) { // 如果提供了 S3 URL,直接使用 if (!fileSize || !mimeType) { throw new ValidationError("缺少文件信息"); } // 验证 S3 URL 格式 if ( !audioUrl.startsWith("https://") || !audioUrl.includes("amazonaws.com") ) { throw new ValidationError("无效的音频文件URL"); } finalAudioUrl = audioUrl; finalFileSize = parseInt(fileSize); finalMimeType = mimeType; } else { throw new ValidationError("缺少音频文件或URL"); } if (!duration) { throw new ValidationError("缺少录音时长"); } // 验证文件大小 (50MB 限制) const maxSize = 50 * 1024 * 1024; if (finalFileSize > maxSize) { throw new ValidationError("文件大小不能超过 50MB"); } // 验证标题 const recordingTitle = title?.trim() || `录音 ${new Date().toLocaleString("zh-CN")}`; if (recordingTitle.length > 100) { throw new ValidationError("录音标题不能超过100个字符"); } // 创建录音记录 const recording = await RecordingService.createRecording({ title: recordingTitle, audioUrl: finalAudioUrl, duration: parseInt(duration), fileSize: finalFileSize, mimeType: finalMimeType, userId: session.user.id, }); return ApiResponseHandler.created(recording); } catch (error) { return ApiResponseHandler.error(error as Error); } }