docs(skills): 添加 Better Auth 最佳实践与安全配置指南
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
import { auth } from "@/lib/auth";
|
||||
import { toNextJsHandler } from "better-auth/next-js";
|
||||
|
||||
export const { GET, POST } = toNextJsHandler(auth);
|
||||
@@ -0,0 +1,85 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { headers } from "next/headers";
|
||||
|
||||
import { auth } from "@/lib/auth";
|
||||
import { db } from "@/db";
|
||||
import { upload } from "@/db/schema";
|
||||
import {
|
||||
getOssClient,
|
||||
getOssBucket,
|
||||
buildCdnUrl,
|
||||
generateOssKey,
|
||||
} from "@/lib/oss";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
// 鉴权:必须登录且是 admin
|
||||
const session = await auth.api.getSession({
|
||||
headers: await headers(),
|
||||
});
|
||||
|
||||
if (!session || session.user.role !== "admin") {
|
||||
return NextResponse.json({ error: "未授权" }, { status: 401 });
|
||||
}
|
||||
|
||||
const formData = await request.formData();
|
||||
const file = formData.get("file");
|
||||
|
||||
if (!file || !(file instanceof File)) {
|
||||
return NextResponse.json({ error: "请上传文件" }, { status: 400 });
|
||||
}
|
||||
|
||||
// 限制文件大小 50MB
|
||||
const MAX_SIZE = 50 * 1024 * 1024;
|
||||
if (file.size > MAX_SIZE) {
|
||||
return NextResponse.json(
|
||||
{ error: "文件大小不能超过 50MB" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const originalName = file.name;
|
||||
const mimeType = file.type || "application/octet-stream";
|
||||
const ossKey = generateOssKey(originalName);
|
||||
const buffer = Buffer.from(await file.arrayBuffer());
|
||||
|
||||
try {
|
||||
const ossClient = getOssClient();
|
||||
|
||||
// 上传到 OSS
|
||||
await ossClient.put(ossKey, buffer, {
|
||||
mime: mimeType,
|
||||
});
|
||||
|
||||
// 生成 CDN URL
|
||||
const cdnUrl = buildCdnUrl(ossKey);
|
||||
|
||||
// 生成记录 ID
|
||||
const id = crypto.randomUUID();
|
||||
|
||||
// 写入数据库记录
|
||||
await db.insert(upload).values({
|
||||
id,
|
||||
originalName,
|
||||
ossKey,
|
||||
url: cdnUrl,
|
||||
mimeType,
|
||||
size: file.size,
|
||||
bucket: getOssBucket(),
|
||||
userId: session.user.id,
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
id,
|
||||
url: cdnUrl,
|
||||
originalName,
|
||||
size: file.size,
|
||||
mimeType,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("[上传失败]", error);
|
||||
return NextResponse.json(
|
||||
{ error: "上传失败,请稍后重试" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { headers } from "next/headers";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
import { auth } from "@/lib/auth";
|
||||
import { db } from "@/db";
|
||||
import { upload } from "@/db/schema";
|
||||
import { getOssClient } from "@/lib/oss";
|
||||
|
||||
export async function DELETE(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
// 鉴权:必须登录且是 admin
|
||||
const session = await auth.api.getSession({
|
||||
headers: await headers(),
|
||||
});
|
||||
|
||||
if (!session || session.user.role !== "admin") {
|
||||
return NextResponse.json({ error: "未授权" }, { status: 401 });
|
||||
}
|
||||
|
||||
const { id } = await params;
|
||||
|
||||
// 查询上传记录
|
||||
const [record] = await db
|
||||
.select()
|
||||
.from(upload)
|
||||
.where(eq(upload.id, id))
|
||||
.limit(1);
|
||||
|
||||
if (!record) {
|
||||
return NextResponse.json({ error: "记录不存在" }, { status: 404 });
|
||||
}
|
||||
|
||||
try {
|
||||
const ossClient = getOssClient();
|
||||
|
||||
// 先从 OSS 删除文件
|
||||
await ossClient.delete(record.ossKey);
|
||||
|
||||
// 再从数据库删除记录
|
||||
await db.delete(upload).where(eq(upload.id, id));
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error("[删除文件失败]", error);
|
||||
return NextResponse.json(
|
||||
{ error: "删除失败,请稍后重试" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user