142 lines
4.4 KiB
TypeScript
142 lines
4.4 KiB
TypeScript
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);
|
||
}
|
||
}
|