Files
OCS_service/server/utils/auth.ts
T
2026-05-23 22:23:29 +08:00

89 lines
2.5 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.
// server/utils/auth.ts - Better Auth 服务端实例,邮箱密码登录 + 用户 API token
import { randomBytes } from "node:crypto";
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import type { H3Event } from "h3";
import { prisma } from "~~/server/utils/db";
/**
* Better Auth 全局实例
*
* - 使用 Prisma MySQL/MariaDB 适配器
* - 只启用邮箱/密码登录,无邮箱验证和密码重置(保持最简)
* - 注册时通过 databaseHook 自动生成 32 位 hex apiToken
* apiToken 供 OCS 油猴脚本跨域调用 /api/search 时在 body 中携带
* - apiToken 不允许客户端直接设置(input: false),但随 session 返回给登录用户
*/
export const auth = betterAuth({
database: prismaAdapter(prisma, { provider: "mysql" }),
session: {
expiresIn: 60 * 60 * 24 * 14, // 14 天过期
updateAge: 60 * 60 * 24 // 每天自动续期
},
emailAndPassword: {
enabled: true
},
user: {
additionalFields: {
apiToken: {
type: "string",
required: false,
// 客户端(signup/updateUser)不能设置此字段,只由 databaseHooks 写入
input: false
},
role: {
type: "string",
required: false,
// 角色只由服务端管理员设置,不允许客户端修改
input: false
}
}
},
databaseHooks: {
user: {
create: {
// 注册时自动生成 apiToken,格式与旧 ACCESS_TOKEN 字段一致(32 位 hex
before: async (user) => {
return {
data: {
...user,
apiToken: randomBytes(16).toString("hex")
}
};
}
}
}
}
});
/**
* 从 H3 事件中读取 Better Auth session
*
* 只复制 headers,不调用 toWebRequest(event)。
* 对 POST/PUT 请求,toWebRequest 会包装 body stream;全局鉴权中调用后,
* 后续 handler 再 readBody(event) 可能长时间挂起。
* 未登录或 session 无效时返回 null
*/
const getHeaderSnapshot = (event: H3Event) => {
const headers = new Headers();
for (const [key, value] of Object.entries(event.node.req.headers)) {
if (Array.isArray(value)) {
for (const item of value) headers.append(key, item);
} else if (value !== undefined) {
headers.set(key, value);
}
}
return headers;
};
export const getAuthSession = (event: H3Event) =>
auth.api.getSession({ headers: getHeaderSnapshot(event) });