// server/api/admin/users.post.ts - 超级管理员创建用户 // // 流程:鉴权 → 二次校验 superadmin → JSON 对象和字段白名单校验 → Better Auth Admin createUser // 安全边界:不使用公开注册接口,不返回密码、apiToken 或 Better Auth 原始错误。 import { readBody, setResponseStatus } from "h3"; import { ADMIN_USER_SELECT, getUnknownKeys, isUserRole, readTrimmedString, requireSuperAdmin, toAdminUser, validateEmail, validatePassword } from "~~/server/utils/adminUsers"; import { auth, getAuthHeaderSnapshot } from "~~/server/utils/auth"; import { prisma } from "~~/server/utils/db"; import { createApiLogger, toSafeLogError } from "~~/server/utils/logging"; import { apiErr, apiOk } from "~~/server/utils/response"; import type { IAdminUserCreateRequest, IAdminUserResponse } from "~~/shared/types/adminUsers"; const ALLOWED_KEYS = new Set(["name", "email", "password", "role"]); const isRecord = (value: unknown): value is Record => typeof value === "object" && value !== null && !Array.isArray(value); const adminUserErr = (code: number, msg: string) => apiErr(code, msg) as unknown as IAdminUserResponse; export default defineEventHandler( async (event): Promise => { const forbidden = requireSuperAdmin(event); if (forbidden) return forbidden as unknown as IAdminUserResponse; const logger = createApiLogger(event, "admin.users.create"); const body = await readBody(event).catch(() => null); if (!isRecord(body)) { setResponseStatus(event, 400); return adminUserErr(400, "请求体必须是 JSON 对象"); } const unknownKeys = getUnknownKeys(body, ALLOWED_KEYS); if (unknownKeys.length > 0) { setResponseStatus(event, 400); return adminUserErr(400, "请求体包含不支持的字段"); } const req: Partial = { name: readTrimmedString(body.name), email: readTrimmedString(body.email)?.toLowerCase(), password: typeof body.password === "string" ? body.password : undefined, role: isUserRole(body.role) ? body.role : undefined }; if (!req.name) { setResponseStatus(event, 400); return adminUserErr(400, "昵称不能为空"); } if (req.name.length > 20) { setResponseStatus(event, 400); return adminUserErr(400, "昵称不能超过 20 个字符"); } if (!req.email || !validateEmail(req.email)) { setResponseStatus(event, 400); return adminUserErr(400, "请输入有效的邮箱地址"); } if (!req.password) { setResponseStatus(event, 400); return adminUserErr(400, "密码不能为空"); } const passwordError = validatePassword(req.password); if (passwordError) { setResponseStatus(event, 400); return adminUserErr(400, passwordError); } if (!req.role) { setResponseStatus(event, 400); return adminUserErr(400, "用户角色无效"); } try { const existing = await prisma.user.findUnique({ where: { email: req.email }, select: { id: true } }); if (existing) { setResponseStatus(event, 400); return adminUserErr(400, "邮箱已被使用"); } await auth.api.createUser({ body: { name: req.name, email: req.email, password: req.password, role: req.role }, headers: getAuthHeaderSnapshot(event) }); const created = await prisma.user.findUnique({ where: { email: req.email }, select: ADMIN_USER_SELECT }); if (!created) { setResponseStatus(event, 500); return adminUserErr(500, "服务器内部错误"); } logger.info("finish_success", { targetUserId: created.id, role: created.role }); return apiOk(toAdminUser(created)); } catch (error) { logger.error("create_failed", { error: toSafeLogError(error) }); setResponseStatus(event, 500); return adminUserErr(500, "服务器内部错误"); } } );