docs(skills): 添加 Better Auth 最佳实践与安全配置指南

This commit is contained in:
asgerzhou
2026-07-17 15:59:38 +08:00
commit 57628c04d6
72 changed files with 14239 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
export const { GET, POST } = toNextJsHandler(auth);
+85
View File
@@ -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 }
);
}
}
+53
View File
@@ -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 }
);
}
}