Files
jiawei 3f31a2a40d
aiartstudio-deploy / deploy (push) Failing after 2s
feat: Add Docker support
2026-04-28 18:39:42 +08:00

14 KiB
Raw Permalink Blame History

CLAUDE.md

目标

本文档用于约束 AI 在本项目内的实现方式,确保代码简洁、可维护、安全,并与当前业务链路保持一致。

核心原则

  • 默认最小改动,优先复用现有工具函数、类型和目录结构。
  • 保持单一入口和单一职责,避免同一业务暴露多套路由或重复实现。
  • 前端不接触 NewAPI session、完整 API Key、Lsky token 等敏感信息。
  • 所有返回给前端的 msg 必须使用本地业务表达,不提及“上游”“NewAPI”“Lsky”“供应商”“token”“key”等内部来源;内部异常统一返回“服务器内部错误”。
  • 注释和日志要解释意图、边界和排查信息,不输出密码、cookie、完整 key、token、图片二进制或完整 prompt。
  • 已存在的说明性注释不要随意删除,尤其是 server/utils/openai.ts 里的模型调用、视觉输入、流式解析等上下文注释。
  • 不要写任何可能阻塞整个进程的代码。

API 路由规范

  • 认证相关接口统一放在 server/api/auth
    • POST /api/auth/register
    • POST /api/auth/login
    • GET /api/auth/me
    • GET /api/auth/logout
    • GET /api/auth/ready
  • 生图相关接口统一放在 server/api/images
    • POST /api/images/generate
    • GET /api/images/history
    • GET /api/images/history/:id
    • DELETE /api/images/history/:id
    • GET /api/images/stats
  • 不创建 server/api/user 之类的本地别名路由。
  • 临时排查接口不要长期保留,例如数据库健康检查类接口排查结束后必须删除。

登录与环境准备

  • 登录成功后,后端从 NewAPI Set-Cookie 中提取 session,写入本站 httpOnly cookie,同时保存 newapi_user_id
  • 前端通过 /api/auth/me 恢复登录状态,通过 /api/auth/logout 清理登录状态。
  • 登录成功后前端可调用 /api/auth/ready,用于确认当前 NewAPI 用户是否具备可用的 NEWAPI_TOKEN_NAME 对应 Token。
  • /api/auth/ready 只检查或创建环境:
    • 只认 name === process.env.NEWAPI_TOKEN_NAMEstatus === 1、未软删除的 Token。
    • 没有可用 Token 时,后端按固定参数创建。
    • 不调用完整 key 获取接口,不向前端返回 key。
    • 返回文案使用“环境已准备 / 环境初始化完成”等业务表达,不暴露 API Key 细节。
  • 完整 key 只允许在服务端真实调用模型前按需获取,相关逻辑集中在 server/utils/newApiTokens.ts

上游与模型调用

  • 调用 NewAPI 用户、Token 等上游接口,使用 server/utils/fetch.ts 的统一入口:
    • newApiFetch:普通未登录请求。
    • newApiFetchRaw:需要读取上游响应头时使用,目前主要用于登录提取 Set-Cookie
    • newApiAuthedFetch:需要 NewAPI 登录态时使用,由服务端从 httpOnly cookie 读取 session/userId 并补齐请求头。
  • 生图模型调用集中在 server/utils/openai.ts
  • 当前生图上游使用 POST ${NEWAPI_BASE_URL}/v1/chat/completions,参数从环境变量读取:
    • model: process.env.IMAGE_GENERATION_MODEL
    • stream: true
    • messages: [{ role: "user", content: prompt }]
  • 生图通过 askImgStream 读取 SSE 流,累积 choices[].delta.content,从 Markdown 图片或普通 URL 中提取图片地址。
  • askStream 保留给 Responses API 的其他流式文本/多模态场景,不强行复用于 chat completions SSE。
  • 不再使用 /v1/images/generations 作为当前生图主链路。

生图、图床与历史记录

  • POST /api/images/generate 当前同步等待生图完成后立即返回,不向前端透传流式进度,也不等待图床归档完成。
  • 生图成功后返回:
    • imageUrl:生成图片访问地址,前端当前优先展示。
    • revisedPrompt:流式 chat completions 当前没有等价字段,通常为空。
  • 生图主链路先把记录落库为 SUCCEEDED,并清空 hostedImageUrlhostedResponseimageMimeTypeerrorMessage;之后再由响应后的后台任务异步上传 Lsky。
  • 历史记录中的图床归档状态约定:
    • status === "SUCCEEDED"hostedImageUrl === nullerrorMessage === null:表示图片已生成、仍在归档中。
    • status === "SUCCEEDED"hostedImageUrl === nullerrorMessage !== null:表示归档失败。
    • hostedImageUrl !== null:表示归档成功。
  • Lsky 归档逻辑集中在 server/utils/lsky.ts
    • 配置从 LSKY_BASE_URLLSKY_TOKENLSKY_STORAGE_ID 读取。
    • 上传时先下载上游图片,再用 multipart/form-data 调 Lsky /upload
    • 文件名格式为 userId_username_timestamp_recordId.ext
    • tags 固定包含 NEWAPI_TOKEN_NAMEuser:{userId}record:{recordId}image:{imageId}model:{IMAGE_GENERATION_MODEL}
  • 图床上传失败不影响本次生图成功:接口仍立即返回 imageUrl,数据库记录归档失败提示;前端可见文案只能说“图片归档失败”等本地描述。
  • 数据库不保存图片 base64;不要恢复 image_base64 或类似大文本存图方案。
  • 历史列表和详情都不向前端返回完整上游响应、图床响应或内部错误详情;这些内容可以入库供服务端排查,但 API 响应应固定隐藏或置空。

数据库与 Prisma

  • Prisma schema 位于 prisma/schema.prisma,实际数据库同步以 Prisma 为准。
  • 根目录 create_tables.sql 是人工可读建表参考,字段变更时必须同步更新。
  • Prisma Client 从 ~~/app/generated/prisma/client 引入,统一由 server/utils/prisma.ts 创建和复用。
  • 当前使用 Prisma 7 + @prisma/adapter-mariadbDATABASE_URL 在运行时解析为 MariaDB pool config。
  • 登录或 /api/auth/me 成功后会 upsert User 快照,主键使用 NewAPI 用户 id。
  • 生图记录写入 ImageGeneration
    • 开始时写 RUNNING
    • 生成成功时先写 SUCCEEDED、上游 URL、完整上游响应、耗时。
    • 后台归档成功后再补写图床 URL、MIME、完整图床响应。
    • 后台归档失败时只补写本地错误文案,不把记录改回失败。
    • 失败时写 FAILED、错误信息、耗时。
    • 删除历史采用软删除 deletedAt
  • GenerationStats 使用固定 id global 记录全局统计。
  • runningRequests 只表示生图进行中,不包含图床归档阶段。
  • 统计更新失败只记录日志,不应阻断用户拿到已经生成的图片。
  • 需要变更数据库结构时,同时维护 Prisma schema、Prisma migration 和 create_tables.sql;本项目实际数据库结构以已执行的 Prisma migration 为准,create_tables.sql 只作为人工可读参考。
  • 修改数据库结构后的同步流程必须完整执行:
    1. 修改 prisma/schema.prisma
    2. 新增对应的 prisma/migrations/<timestamp>_<name>/migration.sql,不要只改 schema 或只运行 prisma generate
    3. 同步更新根目录 create_tables.sql,确保新建库参考 SQL 与 Prisma schema 一致。
    4. 运行 .\node_modules\.bin\prisma.cmd generate 重新生成 app/generated/prisma
    5. 对当前实际数据库运行 .\node_modules\.bin\prisma.cmd migrate deploy,让表结构真正落库。
    6. 运行 .\node_modules\.bin\prisma.cmd migrate status,必须确认输出 Database schema is up to date!
    7. 最后运行 pnpm typecheck;涉及服务启动或 worker 行为时再运行 pnpm build
  • 如果 migrate status 显示历史迁移未 applied,但数据库中相关表/列已经存在,先停下来检查原因,不要直接 migrate deploy;只有确认这些结构确实已经由本项目命令同步过、只是 _prisma_migrations 缺少记录时,才可以用 prisma migrate resolve --applied <migration> 补登记。
  • 本项目数据库不要绕过 Prisma migration 手动改表;如确需临时 SQL 排查或修复,必须把最终结构回写到 Prisma schema、migration 和 create_tables.sql,并用 migrate status 校验一致。

日志规范

  • API handler 使用 server/utils/logging.tscreateApiLoggertoSafeLogError
  • 日志必须包含足够排查信息,例如 requestId、阶段、userIdrecordId、分页、耗时、状态。
  • 上游错误、内部响应、图床响应、数据库异常等细节只允许写入服务端日志或数据库排查字段,不允许通过接口 msgdata、历史详情等返回给前端。
  • 生图日志阶段建议保持清晰:
    • read_body
    • read_user_id
    • create_running_record
    • get_api_key
    • call_image_stream_api
    • finish_success_record
  • 图床归档改为响应后的后台日志阶段,继续打印 recordId、归档是否成功、失败摘要等信息,但不影响主请求返回。
  • 不记录密码、cookie、完整 API key、Lsky token、完整 prompt、图片二进制、base64。
  • 全局请求日志中间件只记录 method、path、status、耗时,不记录请求体和响应体。

请求、响应与错误处理

  • 所有 JSON 接口先校验请求体:只接受 JSON 对象或 null,数组、字符串等无效 body 直接返回 400。
  • 对上游透传字段必须使用白名单数组过滤,只透传类型正确且文档声明的字段。
  • 统一使用 createSuccessResponsecreateErrorResponse 返回结构。
  • createSuccessResponsemsg 只写本地业务结果,例如“登录成功”“生图完成”“环境已准备”。
  • createErrorResponsemsg 只写本地业务错误,例如“未登录”“请求体必须是 JSON 对象”“生图记录不存在”。
  • 上游或内部异常统一通过 createUpstreamErrorResponse 转换,并固定对前端返回“服务器内部错误”;具体异常必须在调用前写入服务端日志。
  • 不把上游 messagestatusMessage、响应体、错误 data、完整响应对象、图床错误信息、数据库错误信息透出给前端。
  • 401 或登录态失效时统一清理本地 auth cookie。
  • 不在 handler 内重复实现复杂错误解析,通用判断放到 server/utils

类型规范

  • 用户相关请求/响应类型放在 shared/types/user.ts
  • 公共响应类型放在 shared/types/index.ts
  • 生图、历史、统计、OpenAI 基础调用类型放在 shared/types/openai.ts
  • 只有前后端共享的接口契约放入 shared/types;服务端内部上游适配类型留在 server/utils 或对应 handler 内。
  • 新增或修改接口时先更新共享类型,再更新服务端和前端调用。
  • 类型字段应有中文注释,说明含义、可选性和是否可能为空。

前端接入约定

  • 用户服务在 app/services/user_service.ts,图片服务在 app/services/image_service.ts
  • 登录成功后可以调用 UserReady() 准备环境;/api/auth/me 恢复登录状态时不自动调用 ready。
  • 当前首页生图组件为 app/components/ImageGenerateCom.vue,优先展示响应中的 imageUrl
  • hostedImageUrl 不在生成接口中返回,只在历史记录中作为归档结果字段保留;后续需要切换展示来源时再调整前端策略。

前端 Store 约定

  • 所有接口请求逻辑和相关状态(数据列表、loading、errorMessage、操作函数等)统一放在对应的 Pinia store 中,不在组件内直接调用 service。
  • Store 文件放在 app/stores/ 下,按业务模块单独拆文件:
    • user.ts:用户登录、登出、状态恢复等。
    • history.ts:生图历史记录列表与公开状态操作。
    • plazal.ts:广场图片列表,导出常量 PAGE_SIZE
    • imageGenerate.ts:生图表单、生图请求、结果展示。
    • promptPresets.ts:预设提示词列表。
  • 组件只负责 UI 渲染和交互,通过 useXxxStore() + storeToRefs() 消费 store 中的状态,通过 store 的 action 发起操作。
  • Store 内部的常量(如分页大小)作为具名导出,供组件直接引用,不在组件中重复定义。

代码风格

  • TypeScript 严格类型优先,避免 any 扩散。
  • 新增通用逻辑优先抽到 server/utils,handler 只负责入参校验、调用和统一响应。
  • 修改优先小步快改,不重构无关代码。
  • server/ 下每个文件顶部必须有 // server/... - 用途说明 注释。
  • 复杂 defineEventHandler 上方必须写清楚接口流程、鉴权边界、数据是否入库、哪些信息不会返回前端。
  • 注释使用中文,描述意图与边界,不写机械解释。
  • 不删除已有有价值注释;确需重写文件时,要保留或迁移原注释表达的信息。

AI 执行清单

当 AI 新增或调整 API 时,按以下顺序执行:

  1. 先阅读现有 handler、utils、shared types、Prisma schema,确认真实实现。
  2. 更新共享类型和服务端内部类型。
  3. 在 handler 中完成请求体校验、白名单过滤、鉴权和日志。
  4. 将可复用业务逻辑抽到 server/utils
  5. 涉及数据库时同步修改 Prisma schema、migration、create_tables.sql,并对实际数据库执行 prisma migrate deploy
  6. 确认前端响应中的 msgdata、历史详情不包含上游/internal 细节;内部错误统一显示“服务器内部错误”。
  7. 确认敏感信息不进入前端响应、日志或数据库不该保存的字段。
  8. 运行 pnpm typecheck;涉及 Prisma schema 时必须运行 prisma generateprisma migrate deployprisma migrate status,确认实际数据库与生成 Client 一致。

非目标

  • 不为未来不确定需求预建兼容路由。
  • 不引入与当前需求无关的抽象层。
  • 不在多个地方维护同一配置值。
  • 不保存生成图片 base64。
  • 不把服务端密钥、session 或 token 暴露给前端。