Files
jiawei 90412a3688
ocs-nuxt / deploy (push) Successful in 1m46s
feat: 新增用户管理
2026-05-24 23:26:43 +08:00

14 KiB
Raw Permalink Blame History

CLAUDE.md

项目定位

本项目是基于 Nuxt 4 / Nitro 的 OCS AI 答题服务,兼容 OCS AnswererWrapper 的题库接口,同时提供网页登录、答题工作台、Dashboard、个人 API 配置和 superadmin 系统配置后台。

核心技术栈:

  • Nuxt 4、Vue 3、Pinia、VueUse、Tailwind CSS v4、shadcn-vue / reka-ui、lucide-vue-next。
  • Better Auth 邮箱密码登录,Prisma 7 + @prisma/adapter-mariadb 连接 MySQL/MariaDB。
  • OpenAI 兼容 Chat Completions 流式调用;对 OCS 客户端仍返回普通 JSON。
  • pnpm 10.33.0Node 22 Alpine 用于 Docker 镜像。

核心原则

  • 默认最小改动,优先复用现有目录结构、工具函数、共享类型、store 和 service。
  • 前端不接触数据库连接串、session 细节、完整 API Key、token、cookie 或上游响应体。
  • API 返回给前端或 OCS 的 msg 必须是本地业务表达,不暴露 OpenAI、Better Auth、Prisma、token、key、连接串或调试细节。
  • 内部异常统一返回“服务器内部错误”;具体异常只能进入服务端安全日志。
  • 日志记录 requestId、阶段、用户/记录标识、分页、耗时、状态等排查信息;不得记录密码、cookie、完整 key、token、完整 prompt、图片二进制、base64、完整请求体或响应体。
  • 不写可能阻塞整个进程的代码;流、DB、外部请求都要有明确边界或错误出口。
  • 不为未来不确定需求预建兼容路由、抽象层、配置项或多套入口。

目录结构约定

  • app/:前端应用代码。
    • app/pages/:页面路由,当前包括首页答题、Dashboard、登录、注册。
    • app/components/:业务组件;app/components/ui/ 是 shadcn-vue 组件,尽量不要手改生成件。
    • app/services/:前端 API 封装,组件不要直接写 $fetch 调业务接口。
    • app/stores/:Pinia 状态与动作;组件通过 store 消费状态和发起业务操作。
    • app/interfaces/:前端类型出口,主要重导出 shared/types
    • app/utils/:前端工具,例如 Better Auth client、错误文案、剪贴板等。
  • server/Nitro 服务端代码。
    • server/api/JSON API 与 Better Auth 路由。
    • server/middleware/:服务端中间件,当前包含 API 统一鉴权和注册开关守卫。
    • server/plugins/:Nitro 启动插件,当前用于补全默认系统配置。
    • server/utils/:服务端业务工具、DB、鉴权、OpenAI、日志、配置、答题逻辑。
  • shared/:前后端共享类型和纯函数。新增或修改接口契约时优先更新这里。
  • prisma/schema.prisma:数据库结构唯一来源;Prisma Client 输出到 prisma/generated,不要改回 app/generated
  • public/:静态公开文件,不经过构建处理。

认证与权限

  • Better Auth 实例在 server/utils/auth.ts,使用 Prisma MySQL/MariaDB 适配器,只启用邮箱密码登录。
  • 注册用户时通过 Better Auth databaseHooks 自动生成 32 位 hex apiToken
  • apiToken 供 OCS 油猴脚本跨域调用 /api/search 使用;客户端不能自行写入,刷新走 POST /api/user/refresh-token
  • server/middleware/api-auth.ts 默认保护所有 /api/* 路由;只有 publicApiRoutes 中声明的公开路由放行。
  • 当前公开路由:
    • /api/auth/**Better Auth 登录、注册、退出、get-session 等。
    • GET /api/health:健康检查,不含敏感信息。
    • /api/search:有自己的 session/apiToken 双通道鉴权。
  • OPTIONS 预检请求在中间件中直接返回 204。
  • superadmin 接口必须在 handler 内二次校验 role === "superadmin",不能只依赖通用 middleware。
  • 注册开关由 allow_registration 系统配置控制,server/middleware/registration-guard.ts 拦截 POST /api/auth/sign-up/email

API 规范

  • 新增接口默认需要登录;只有确实公开的接口才能加入 server/utils/api-auth-rules.ts
  • Handler 只负责入参校验、鉴权/权限边界、调用业务函数和统一响应;复用逻辑放到 server/utils
  • 非 OCS 接口使用 server/utils/response.ts
    • 成功:apiOk(data),结构为 { code: 0, data, msg: "请求成功" }
    • 失败:apiErr(code, msg)data 固定为 null
  • /api/search 是 OCS 兼容接口,不使用 apiOk/apiErr
    • 成功固定 { code: 1, question, answer }
    • 失败固定 { code: 0, msg }
  • 所有 JSON body 先校验为普通对象或 null;数组、字符串等无效 body 返回 400。
  • 对客户端传入字段必须白名单过滤,只接受当前接口声明的字段和正确类型。
  • 新增或修改接口时,同步维护 shared/types、服务端实现、前端 service、Pinia store 和组件消费。
  • API 响应只返回前端需要的字段,不透出内部响应体、数据库异常、第三方错误详情或完整调试对象。

答题流程与缓存

  • /api/search 兼容 GET、JSON POST、urlencoded POST、multipart/form-data。
  • 鉴权顺序:优先读取网页登录 session;没有 session 时使用请求中的 tokenUser.apiToken
  • title/type/options 会标准化为 SearchParams,只把题目长度、题型和是否有选项写日志。
  • 缓存不再是进程内内存缓存;答案记录写入 qa_record,按 userId + md5(title|type|options) 查询最近答案。
  • 清缓存不删除历史记录,而是更新 User.cacheClearedAt;缓存查询只命中该时间之后的记录。
  • 调 OpenAI 前必须通过 getUserEffectiveApiConfig(userId) 取得生效配置,保证系统 key 不会发往用户自定义 endpoint。
  • 多选题继续使用 # 分隔;server/utils/answer.ts 中的 OCS 响应格式不可随意改动。

配置模型

  • OpenAI 运行配置以数据库 system_config 为准,不再依赖 OPENAI_API_KEY 等旧环境变量。
  • server/plugins/initConfig.ts 启动时补全缺失的默认系统配置;不覆盖已有值。
  • server/utils/sysConfig.ts 对系统配置做 60 秒 TTL 内存缓存;superadmin 更新配置后必须调用 invalidateSysConfigCache()
  • 系统配置接口:
    • GET /api/admin/system-config:仅 superadmin,可读,API Key 只返回遮蔽预览。
    • PUT /api/admin/system-config:仅 superadmin,可写,openAiApiKey 为空字符串表示不修改已有 key。
  • 用户自定义配置保存在 user_config.value JSON 中:
    • GET /api/user/settings:只返回遮蔽后的 key 预览。
    • POST|PUT /api/user/settings:白名单字段,空字符串 key 表示不修改旧 key。
  • 用户启用自定义配置时,只有 customApiBasecustomApiKey 同时非空,才使用用户 endpoint + key;否则 base 和 key 完全回退系统配置。
  • DATABASE_URLBETTER_AUTH_SECRETBETTER_AUTH_URL 仍来自环境变量;.env.example 只放这些运行必需项。

数据库与 Prisma

  • Prisma schema 位于 prisma/schema.prisma,实际数据库结构以 Prisma 为准。
  • 当前使用 Prisma 7 + @prisma/adapter-mariadbPrisma Client 从 server/utils/db.ts 创建和复用。
  • 业务代码不要直接 new PrismaClient(),不要在前端读取或间接获取数据库连接信息。
  • DATABASE_URL 只允许服务端读取;日志和响应都不能输出完整连接串。
  • prisma.config.ts 在 Prisma 命令加载时需要 DATABASE_URLDocker build 使用占位值,运行时由容器环境变量覆盖。
  • 修改数据库结构的标准流程:
    1. 先阅读 prisma/schema.prisma、相关 handler、server/utils 和共享类型。
    2. 修改 schema。
    3. 优先新增 Prisma migration;若当前开发库确认可接受 prisma db push,必须说明原因。
    4. 运行 .\node_modules\.bin\prisma.CMD generate 重新生成客户端。
    5. 对实际数据库执行同步命令,并确认 schema 已落库。
    6. 运行类型检查;涉及服务启动或构建行为时再运行构建。
  • 如果 migration 状态和数据库实际结构不一致,先停下来检查原因,不要直接强行部署或 resolve。
  • 数据库不要保存大体积二进制、base64 或敏感明文;API Key 如需保存,只能服务端读取,前端只看遮蔽预览。

前端约定

  • 组件只负责 UI 渲染和交互,通过 useXxxStore()storeToRefs() 消费状态,通过 store action 发起操作。
  • 接口调用放在 app/services/;业务状态、loading、错误文案和 toast 放在 Pinia store。
  • Store 内部通用常量作为具名导出,供组件引用,不在组件中重复定义。
  • 认证状态由 app/plugins/auth.ts 做 SSR/client 共用初始化,避免首屏水合不一致。
  • /dashboardapp/middleware/auth.ts 保护,未登录跳 /login
  • 深色模式通过 useCookie("color-mode") 持久化,避免 localStorage 导致 SSR 水合不匹配。
  • 页面与组件不得展示内部服务名、第三方错误、token、key、连接串或原始异常信息。
  • shadcn-vue 配置见 components.json;新增 UI 优先使用现有 app/components/ui、lucide 图标和 Tailwind token。

代码风格

  • TypeScript 严格类型优先,避免 any 扩散;确需使用时收敛在边界处。
  • 新增或大改 server/ 文件时,文件顶部写一行中文用途说明,格式建议为 // server/... - 用途说明
  • 复杂 defineEventHandler 上方写清楚流程、鉴权边界、数据是否入库、哪些信息不会返回前端。
  • 注释使用中文,描述意图、边界和排查信息,不写机械解释。
  • 不删除已有有价值注释;重写文件时保留或迁移原注释表达的信息。
  • import 排序受 eslint-plugin-simple-import-sort 约束。
  • app/components/ui/** 在 ESLint 中忽略,通常视为生成代码,不做无关手改。

常用命令

  • 安装依赖:pnpm install
  • 开发启动:pnpm dev
  • 构建:pnpm build
  • 预览构建产物:pnpm preview
  • Nuxt 静态生成:pnpm generate
  • Prisma generate.\node_modules\.bin\prisma.CMD generate
  • 类型检查:.\node_modules\.bin\nuxi.CMD typecheck
  • ESLint.\node_modules\.bin\eslint.CMD .

执行说明:

  • 涉及 Prisma schema 时必须运行 Prisma generate。
  • 涉及 API、共享类型、store、组件联动时至少运行类型检查。
  • 涉及构建、Nitro 插件、Nuxt 配置、Docker 行为时再运行构建。
  • 当前仓库未声明专门的 test 脚本,不要假设存在单元测试命令。

Docker 与部署

  • Dockerfile.base 基于 node:22-alpine,启用 corepack 并激活 pnpm 10.33.0。
  • Dockerfileocs-base:latest 构建应用,复制源码、安装依赖、执行 pnpm build,最终用 node .output/server/index.mjs 启动。
  • .dockerignore 排除 node_modules/.nuxt/.output/prisma/generated/、本地 env 和日志。
  • 容器默认 PORT=3000TZ=Asia/Shanghai
  • 构建阶段的 DATABASE_URL 是占位值;运行时必须提供真实数据库连接和 Better Auth 环境变量。

可用 Skills

本项目本地 .agents/skills 包含以下 Better Auth 相关 skills,遇到对应任务时优先查阅:

  • better-auth-best-practicesBetter Auth 服务端/客户端、数据库适配、session、插件和环境变量。
  • create-auth-skill:为 TypeScript/JavaScript 项目脚手架或扩展认证流程。
  • email-and-password-best-practices:邮箱密码、邮箱验证、密码重置和密码策略。
  • better-auth-security-best-practices:速率限制、secret、CSRF、trusted origins、cookie/session 安全和审计。
  • organization-best-practices:组织、多租户、成员、邀请、团队和 RBAC。
  • two-factor-authentication-best-practices:TOTP、OTP、备份码、可信设备和 2FA 登录流。

当前会话还可用的通用 skills / 插件能力:

  • frontend-design:构建或美化前端页面、组件、应用体验。
  • openai-docs:查询 OpenAI 官方文档和最新 API/模型建议。
  • imagegenhatch-pet:生成位图视觉素材或 Codex pet。
  • documents:documentspresentations:Presentationsspreadsheets:Spreadsheets:处理 Word、PPT、表格文件。
  • GitHub 插件 skillsgithub:githubgithub:gh-address-commentsgithub:gh-fix-cigithub:yeet
  • skill-creatorskill-installerplugin-creator:创建/安装 Codex skills 或插件。

使用 skills 时先读对应 SKILL.md,只加载与当前任务直接相关的引用文件,不要为了普通代码改动批量读取全部资料。

AI 执行清单

当新增或调整 API、数据结构、认证、配置或核心业务逻辑时,按以下顺序执行:

  1. 阅读真实实现:handler、middleware、server/utilsshared/types、Pinia store、Prisma schema。
  2. 明确鉴权边界:公开路由、session、apiToken、superadmin 二次校验。
  3. 更新共享类型,再更新服务端、前端 service、store 和组件。
  4. 对请求体做普通对象校验和字段白名单过滤。
  5. 确认响应 msgdata 不包含内部或第三方细节。
  6. 确认敏感信息不进入前端响应、日志或不该保存的数据库字段。
  7. 涉及数据库时同步 schema、migration 或 db push、Prisma generate 和实际数据库。
  8. 运行类型检查;涉及 Prisma schema 时必须 Prisma generate;涉及服务启动或构建行为时再构建。

非目标

  • 不恢复旧的 ACCESS_TOKEN 单全局令牌模式。
  • 不恢复旧的 OpenAI 环境变量配置作为主配置来源。
  • 不为同一能力创建多个本地别名路由。
  • 不新增长期保留的临时排查接口,例如数据库健康检查类接口。
  • 不引入与当前需求无关的抽象层、兼容层或配置项。
  • 不在多个地方维护同一配置值。
  • 不保存大体积 base64、敏感明文或上游完整响应体。
  • 不把服务端密钥、session、token、cookie、数据库连接信息或内部错误暴露给前端。