Initial commit

This commit is contained in:
theshy
2025-07-31 17:05:07 +08:00
parent 8fab3b19cc
commit 24f21144ab
91 changed files with 16311 additions and 159 deletions

View File

@ -0,0 +1,71 @@
import { NextRequest } from "next/server";
import { getServerSession } from "next-auth/next";
import { authOptions } from "@/lib/auth";
import { RecordingService } from "@/lib/services/recording.service";
import { S3Client, HeadObjectCommand } 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 GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return Response.json({ error: "未授权" }, { status: 401 });
}
const { id: recordingId } = await params;
const recording = await RecordingService.getRecordingById(recordingId);
if (!recording) {
return Response.json({ error: "录音不存在" }, { status: 404 });
}
// 检查用户权限
if (recording.userId !== session.user.id) {
return Response.json({ error: "无权限访问" }, { status: 403 });
}
// 从 S3 URL 提取 bucket 和 key
const url = new URL(recording.audioUrl);
const pathParts = url.pathname.split("/");
const bucket = url.hostname.split(".")[0];
const key = pathParts.slice(1).join("/");
try {
// 检查文件是否存在且可访问
const command = new HeadObjectCommand({
Bucket: bucket,
Key: key,
});
await s3.send(command);
return Response.json({
accessible: true,
url: recording.audioUrl,
size: recording.fileSize,
mimeType: recording.mimeType,
});
} catch (error) {
console.error("S3 文件访问检查失败:", error);
return Response.json({
accessible: false,
error: "文件无法访问",
url: recording.audioUrl,
});
}
} catch (error) {
console.error("检查文件访问失败:", error);
return Response.json({ error: "检查文件访问失败" }, { status: 500 });
}
}

View File

@ -0,0 +1,98 @@
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,
NotFoundError,
ValidationError,
} from "@/lib/errors/app-error";
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
throw new AuthenticationError();
}
const { id } = await params;
if (!id) {
throw new NotFoundError("录音ID不能为空");
}
const body = await request.json();
const { title } = body;
console.log(`PUT /api/recordings/${id} - 请求数据:`, {
title,
userId: session.user.id,
});
if (!title || typeof title !== "string" || title.trim().length === 0) {
throw new ValidationError("录音标题不能为空");
}
if (title.length > 100) {
throw new ValidationError("录音标题不能超过100个字符");
}
console.log(
`Attempting to update recording: ${id} for user: ${
session.user.id
} with title: "${title.trim()}"`
);
const updatedRecording = await RecordingService.updateRecording(
id,
session.user.id,
{ title: title.trim() }
);
console.log(
`Successfully updated recording: ${id}, new title: "${updatedRecording.title}"`
);
return ApiResponseHandler.success(updatedRecording);
} catch (error) {
console.error(`Failed to update recording ${params}:`, error);
return ApiResponseHandler.error(error as Error);
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
throw new AuthenticationError();
}
const { id } = await params;
if (!id) {
throw new NotFoundError("录音ID不能为空");
}
console.log(
`Attempting to delete recording: ${id} for user: ${session.user.id}`
);
await RecordingService.deleteRecording(id, session.user.id);
console.log(`Successfully deleted recording: ${id}`);
return ApiResponseHandler.noContent();
} catch (error) {
console.error(`Failed to delete recording ${params}:`, error);
return ApiResponseHandler.error(error as Error);
}
}

View File

@ -0,0 +1,75 @@
import { NextRequest } from "next/server";
import { getServerSession } from "next-auth/next";
import { authOptions } from "@/lib/auth";
import { RecordingService } from "@/lib/services/recording.service";
import { S3Client, GetObjectCommand } 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 GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return Response.json({ error: "未授权" }, { status: 401 });
}
const { id: recordingId } = await params;
const recording = await RecordingService.getRecordingById(recordingId);
if (!recording) {
return Response.json({ error: "录音不存在" }, { status: 404 });
}
// 检查用户权限
if (recording.userId !== session.user.id) {
return Response.json({ error: "无权限访问" }, { status: 403 });
}
// 从 S3 URL 提取 bucket 和 key
const url = new URL(recording.audioUrl);
const pathParts = url.pathname.split("/");
const bucket = url.hostname.split(".")[0];
const key = pathParts.slice(1).join("/");
try {
// 从 S3 获取文件
const command = new GetObjectCommand({
Bucket: bucket,
Key: key,
});
const response = await s3.send(command);
const stream = response.Body as ReadableStream;
if (!stream) {
return Response.json({ error: "文件不存在" }, { status: 404 });
}
// 返回音频流
return new Response(stream, {
headers: {
"Content-Type": recording.mimeType,
"Content-Length": recording.fileSize.toString(),
"Accept-Ranges": "bytes",
"Cache-Control": "public, max-age=3600",
},
});
} catch (error) {
console.error("S3 文件获取失败:", error);
return Response.json({ error: "文件无法访问" }, { status: 500 });
}
} catch (error) {
console.error("音频流获取失败:", error);
return Response.json({ error: "音频流获取失败" }, { status: 500 });
}
}