Files
record-app-next/app/api/recordings/upload/route.ts
2025-07-31 17:05:07 +08:00

142 lines
4.4 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.

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);
}
}