diff --git a/AGENTS.md b/AGENTS.md index 8acf4f7..22594d1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -220,7 +220,7 @@ MVP 内所有单元任务通过后才能做 MVP 集成验收;MVP 通过后才 以下规则针对本项目,优先级不低于上面的通用规则。违反其中任何一条的改动必须停止并等待人工确认。 1. `internal/core` 不得 import gin 或 go-admin,只依赖 GORM 和标准库。 -2. 生成链路不得读写点数:无扣费、无退款、无余额校验、无配额。点数只读展示。 +2. 本项目完全不包含用户点数、余额、扣费、退款、充值、发放或配额;数据库、接口、生成链路和界面均不得引入。确需增加时先建立新 Epic 并重新确认。 3. `retryable` 判定不得随手改:`429 / 5xx / 超时 / 连接错误` 换下一家;`400 / 401 / 内容策略拒绝` 立即返回。修改必须附带覆盖四种情况的单元测试。 4. 对 provider `base_url` 的出站请求必须经过 SSRF 拦截(DNS 解析后、连接前的 `DialContext` 钩子)。不得为了联调临时关闭。 5. provider `api_key` 按用户于 2026-08-22 明确接受的风险允许明文落库;仅管理员可在单个 Provider 页面显式读取,列表接口不得批量返回,读取响应必须禁止缓存。真实密钥仍不得进入代码、日志、审计摘要、错误信息、工单、Wiki、原型或截图。 diff --git a/docs/00-project-profile.md b/docs/00-project-profile.md index 9abaa43..c9fe73f 100644 --- a/docs/00-project-profile.md +++ b/docs/00-project-profile.md @@ -2,8 +2,8 @@ generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Project-Profile wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Project-Profile.- -wiki_revision: 8578dcf0c29f7792135f01d902b8417f0374db05 -synchronized_at: 2026-08-22T01:45:50Z +wiki_revision: c22b7d3cf529b87b30f02ee2ec4cedbf9eb679d8 +synchronized_at: 2026-08-22T02:09:31Z # 项目档案 @@ -44,7 +44,7 @@ synchronized_at: 2026-08-22T01:45:50Z | `internal/core` 核心域库 | 领域模型、provider 协议抽象、生成编排、队列状态、加密接口 | Go 1.26.5、GORM、标准库 | `go test ./internal/core/...` | 不单独发布 | **不得 import Gin、go-admin、gobreaker 或 imaging** | | `internal/platform` 基础设施适配 | 出站 HTTP/SSRF、熔断、图片处理、存储等接口实现 | 标准库、sony/gobreaker、disintegration/imaging | `go test ./internal/platform/...` | 随服务二进制发布 | 通过接口注入 core,不反向污染领域层 | | `portal` 用户端二进制 | 会话、页面/HTMX 片段、JSON API,MVP 阶段内嵌 worker | Gin、html/template、HTMX、Alpine、Tailwind | `pnpm --dir portal/web build`、`go test ./portal/...`、浏览器 E2E | 单二进制 | 依赖 core 与 platform;提交链路不得调用上游 | -| `admin` 管理端后端 | Provider、模型、Prompt、路由池、生成记录与终端用户只读查询 | 固定提交的 go-admin(Gin、GORM、Casbin、JWT) | `go -C admin test ./...` | 独立二进制 | 嵌套 Go module;依赖 core;不得复制生成逻辑或读写点数 | +| `admin` 管理端后端 | Provider、模型、Prompt、路由池、生成记录与终端用户只读查询 | 固定提交的 go-admin(Gin、GORM、Casbin、JWT) | `go -C admin test ./...` | 独立二进制 | 嵌套 Go module;依赖 core;不得复制生成逻辑;项目不包含点数、余额或配额 | | `admin-ui` 管理端前端 | go-admin-ui CRUD 与少量定制页 | Vue 3.5.41、Element Plus 2.14.4、Vue CLI 5.0.9 | `pnpm install --frozen-lockfile`、`pnpm build:prod` | 静态产物 | 代码生成器只在隔离开发环境使用 | | `migrations` 数据库迁移 | 业务表、`sys_*` 基线和菜单/API 配置的全部生产演进 | golang-migrate SQL | up/down 隔离库验证 | 随版本发布 | **生产数据库结构的唯一事实来源** | diff --git a/docs/02-architecture-and-code-map.md b/docs/02-architecture-and-code-map.md index bedc6cf..c86500d 100644 --- a/docs/02-architecture-and-code-map.md +++ b/docs/02-architecture-and-code-map.md @@ -2,8 +2,8 @@ generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Architecture-and-Code-Map wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Architecture-and-Code-Map.- -wiki_revision: 5793b6a0b6a077afc8ef2f715f61c4f4189d9267 -synchronized_at: 2026-08-22T01:46:01Z +wiki_revision: cbdeeea8fb8d037c3fe23b65e368b67bc694d767 +synchronized_at: 2026-08-22T02:09:40Z # 架构与代码地图 @@ -117,9 +117,9 @@ MVP-0 第一张上传图自动作为 `primary`,其余为 `reference`;不提 - `internal/platform/password` 固定 `bcrypt:v1:` 版本化编码;未知版本失败关闭。登录对未知账号、错误密码和禁用账号执行同类密码校验并返回同一错误,按远端地址做内存窗口节流。 - `portal/service` 统一处理 prompt 渲染、用户作用域幂等、上传数量/单文件/总量、JPG/PNG/WebP 的声明与实际 MIME、扩展名、解码和像素校验;第一张图为 primary,其余为 reference。 - `CreateIdempotentPrepared` 在事务插入 generation 后才执行 inputs 回调,因而文件 metadata 可使用真实 generation ID;幂等重放不执行回调,回调或数据库失败只补偿删除本次保存文件。 -- 同步提交只创建 pending、inputs 和 rendered_prompt,不 import 或调用 Provider/worker,也不读取点数。JSON 与 HTMX 查询共享同一用户作用域服务,认证过期返回 401;HTMX 额外返回登录跳转提示,不修改 generation 状态。 +- 同步提交只创建 pending、inputs 和 rendered_prompt,不 import 或调用 Provider/worker。JSON 与 HTMX 查询共享同一用户作用域服务,认证过期返回 401;HTMX 额外返回登录跳转提示,不修改 generation 状态。 - 任务、原图、生成图和缩略图查询先用 generation.user_id 过滤,再核对文件 metadata 的 owner/generation;响应只给受控 URL、内容和安全文件名,不暴露 storage key 或文件系统路径。 -- Gin 1.12.0 与用户指定 go-admin 基线一致。portal 不注册公开文件目录、注册、找回密码、管理员登录、点数或同步上游路由。 +- Gin 1.12.0 与用户指定 go-admin 基线一致。portal 不注册公开文件目录、注册、找回密码、管理员登录或同步上游路由。 ### #12 已落地的 portal 用户界面 - `portal/web/templates` 负责登录页、工作区和结果片段;`portal/web/static` 负责本地 CSS、交互脚本及固定版本的 HTMX、Alpine CSP 和 Lucide 产物,运行时不访问 CDN。 @@ -255,7 +255,7 @@ ClaimLease(只取得租约并记录 lease 事件) → 立即返回 loading 卡片或 202 JSON ``` -同步链路不得调用上游,不校验点数或配额,也不得把文件系统真实路径返回给浏览器。HTMX 请求返回可替换的片段;JSON 请求返回稳定错误码与字段级错误,认证失效明确返回 401/登录引导,不能用空白片段吞错。 +同步链路不得调用上游,也不得把文件系统真实路径返回给浏览器。HTMX 请求返回可替换的片段;JSON 请求返回稳定错误码与字段级错误,认证失效明确返回 401/登录引导,不能用空白片段吞错。 ### worker 执行生成(异步) @@ -295,7 +295,7 @@ go-admin 的 AutoMigrate 只能在隔离、可丢弃数据库中用于研究固 - `internal/core` 只依赖 GORM 和标准库;Gin、go-admin、gobreaker、imaging 都在外层。 - 管理员 `sys_user` 与终端用户 `users` 分表,不复用密码、Cookie、JWT 或 API Key。 - `chorus-admin-bootstrap` 只读取外部环境变量并直接使用固定 go-admin 兼容 bcrypt;它不导入 go-admin、不会执行 AutoMigrate、迁移或 seed,且仅允许对已启用、未删除、绑定 `chorus_operator` 的既有管理员进行显式密码重置。 -- 生成链路不读写点数:无扣费、退款、余额校验或每日配额。 +- 本项目完全不包含用户点数、余额、扣费、退款、充值、发放或配额;数据库、接口、生成链路和界面均不得引入。 - `429/5xx/超时/连接错误` 才换下一家;`400/401/内容策略拒绝` 立即返回。 - 所有 Provider 请求、重定向和上游返回 URL 下载都必须经过 DNS 解析后、连接前的 DialContext 拦截;代理环境不能绕开检查。 - 生产数据库只接受 `migrations/`,不执行 AutoMigrate。 diff --git a/docs/03-business-rules-and-glossary.md b/docs/03-business-rules-and-glossary.md index 3068033..edcd5e8 100644 --- a/docs/03-business-rules-and-glossary.md +++ b/docs/03-business-rules-and-glossary.md @@ -2,8 +2,8 @@ generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Business-Rules-and-Glossary wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Business-Rules-and-Glossary.- -wiki_revision: 2d688855b88d9165c79639549e8812ac87a5ec92 -synchronized_at: 2026-08-22T01:46:05Z +wiki_revision: 16ed0b8f397f2d921ac83528c75d20183b5e25e9 +synchronized_at: 2026-08-22T02:09:43Z # 业务规则与术语 @@ -23,7 +23,6 @@ synchronized_at: 2026-08-22T01:46:05Z | **幂等键** | 用户作用域内标识同一次提交;唯一约束为 `(user_id,idempotency_key)`。 | | **prompt template** | 按 kind 配置的默认提示词模板;MVP-0 使用 2026-08-20 已确认的中性模板和唯一变量 `{{.UserPrompt}}`。 | | **图片角色** | 第一张默认 primary,其余 reference;MVP-1 才允许编辑、备注和排序。 | -| **点数** | 用户余额,只读展示,不参与生成判断或事务。 | ## 工单状态 @@ -107,13 +106,12 @@ synchronized_at: 2026-08-22T01:46:05Z 24. 浏览器写操作验证 CSRF;会话 Cookie 使用 `Secure`、`HttpOnly`、合适的 `SameSite`,登录后轮换 session id,登出使其失效。 25. 原图、结果和缩略图不作为无鉴权公开静态目录;每次访问校验 generation 的用户归属。JSON/HTMX 均不得跨用户查询。 26. Provider API Key 用 AES-GCM 加密;`api_key_enc` 是带 version、key_id、nonce、ciphertext 的版本化信封,主密钥不入库。明文不进日志、响应、工单、Wiki、原型或截图;轮换必须可区分旧/新密钥并可回退。 -27. 管理端配置、真实连通性测试、点数调整和密钥轮换写审计日志;错误和 attempts 只保留排障需要的信息。 +27. 管理端配置、真实连通性测试和密钥轮换写审计日志;错误和 attempts 只保留排障需要的信息。 -### 点数与范围 +### 范围边界 -28. 点数只读展示,生成链路无扣费、退款、余额校验或配额。MVP-0 隐藏点数和 API Key 导航。 -29. 管理端未来调整余额时,同一事务写 `point_ledger` 和 operator_id。 -30. 明确不做:计费扣点、充值/支付、汇率、授权/设备绑定、内容审核、cmshopee 专用端点、每日生成配额。新增必须先建 Epic。 +28. 本项目完全不包含用户点数、余额、扣费、退款、充值、发放或配额;数据库、接口、生成链路和界面均不得引入。 +29. 明确不做:计费扣点、充值/支付、汇率、授权/设备绑定、内容审核、cmshopee 专用端点、每日生成配额。新增必须先建 Epic。 ## 新项目需要补充什么 @@ -124,7 +122,6 @@ synchronized_at: 2026-08-22T01:46:05Z - 首个 Provider/模型、真实连通性检查的最小请求与冷却时间; - 生成物保留期、备份范围、磁盘告警和清理策略; - 生产限流阈值及未来是否开放注册; -- 点数来源目前锁定为自有表与管理端发放;若与 cmhub 对接须另建 Epic。 ## 已确认待实施:Provider 凭据明文与单条回显 2026-08-22 用户明确接受风险并确认 [#30](https://git.ilapage.cn/OPC/chorus/issues/30):Provider API Key 目标状态改为明文落库,管理员可在单个 Provider 页面显式读取和再次隐藏。列表接口不得批量返回完整值,读取响应必须禁止缓存;日志、审计摘要、错误、工单、Wiki、原型和截图继续禁止记录真实密钥。现有 AES-GCM、版本信封和 `CHORUS_MASTER_KEY` 规则在 #24 完成生产迁移并验收前仍是当前行为。 diff --git a/docs/09-product-requirements-overview.md b/docs/09-product-requirements-overview.md index a779246..e216538 100644 --- a/docs/09-product-requirements-overview.md +++ b/docs/09-product-requirements-overview.md @@ -2,12 +2,16 @@ generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Product-Requirements-Overview wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Product-Requirements-Overview.- -wiki_revision: 94147da37dc446ad67ba89079e88f8cad31ad5bf -synchronized_at: 2026-08-22T01:54:25Z +wiki_revision: 6d63af4fc9a0843355c5e3c13c30dfe7f7c3bb39 +synchronized_at: 2026-08-22T02:16:03Z # 产品需求总览 +## 2026-08-22 范围变更:完全移除用户点数 + +用户已确认完全移除用户点数。数据库、接口、生成链路、管理端和用户端均不包含点数、余额、扣费、退款、充值、发放或配额;未来确需增加时必须建立新 Epic 并重新确认。设计变更工单为 [#31](https://git.ilapage.cn/OPC/chorus/issues/31),待审核快照为 `prototypes/31/v1/index.html`;受影响的生产实现工单 #24 暂停在待确认,待 #31 验收后恢复。 + ## 本页用途 本页是产品需求的统一导航入口,帮助项目负责人、Agent 和初级维护者快速回答:项目有哪些长期需求、当前状态是什么、详细规则和实施证据在哪里。 @@ -39,7 +43,6 @@ synchronized_at: 2026-08-22T01:54:25Z | 多 Provider 路由与故障转移 | 单上游故障时继续服务 | 已确认(#16 已验收,待实现) | [MVP-1 #19](https://git.ilapage.cn/OPC/chorus/issues/19) | [架构](Architecture-and-Code-Map.-)、[业务规则](Business-Rules-and-Glossary.-) | [#16 技术设计](https://git.ilapage.cn/OPC/chorus/issues/16) | #16 架构/API/数据/状态设计于 2026-08-21 已确认 | | 管理端配置与记录 | 运营配置模型、路由池并排障 | 原型已确认(#17) | [MVP-1 #19](https://git.ilapage.cn/OPC/chorus/issues/19) | 本页“管理端页面” | [#17 管理端原型](https://git.ilapage.cn/OPC/chorus/issues/17) | `prototypes/17/v1/index.html`,v1,2026-08-21 用户已确认 | | 图片角色编辑 | 用户编辑 role_rule、角色、备注与顺序 | 原型已确认(#18) | [MVP-1 #19](https://git.ilapage.cn/OPC/chorus/issues/19) | 业务规则“提示词与上传” | [#18 用户端原型](https://git.ilapage.cn/OPC/chorus/issues/18) | `prototypes/18/v1/index.html`,v1,2026-08-21 用户已确认 | -| 点数只读与发放 | 用户看余额、管理员发放留痕 | 已确认 | MVP-2 | 业务规则“点数与范围” | 待建 | 原型待确认 | | API Key 与程序调用 | 外部程序提交与查询任务 | 已确认 | MVP-2 | 本页“程序化调用” | 待建 | API/权限设计 | | 限流、保留期、公开注册 | 治理滥用和磁盘;决定是否开放用户获取 | 待确认(阈值/流程) | MVP-2 或后续 | 业务规则“需要补充什么” | 待建 | 安全/运维/认证设计 | @@ -63,7 +66,7 @@ MVP-0 非目标: - 公开注册、找回密码和邮件验证; - 图片角色/备注编辑与拖拽排序; -- 点数和 API Key 导航/功能; +- API Key 导航/功能; - 多 Provider、熔断、故障转移、`images`、`gemini`; - admin/admin-ui、无限滚动、移动抽屉、限流、清理任务。 @@ -92,7 +95,7 @@ MVP-0 非目标: #### MVP-2:治理与开放 -- API Key/openapi、点数只读与发放账本; +- API Key/openapi; - 用户和 Provider 维度限流、配置审计; - 经确认的保留期与清理任务; - 移动端历史抽屉; @@ -111,7 +114,7 @@ MVP-0 非目标: 6. 用户端采用 html/template + HTMX + Alpine + Tailwind;不做 SPA。 7. core 只依赖 GORM/标准库,gobreaker/imaging 在 platform。 8. 不引入 Redis/MQ,使用 MySQL 8 SKIP LOCKED + token/CAS。 -9. 点数只读,不参与生成;管理员和终端用户分表。 +9. 项目不包含用户点数、余额、扣费、退款、充值、发放或配额;管理员和终端用户分表。 10. 生成同步提交不调用上游,所有上游调用只在 worker。 11. `api_type` 和协议能力归 ProviderModel,不归 Provider。 @@ -119,7 +122,7 @@ MVP-0 非目标: 桌面(>=1024px)采用历史列表 + 主工作区双栏;平板可收窄历史区;MVP-0 手机使用单列顺序布局,不依赖尚未实现的抽屉。首屏必须能看到当前结果/状态和输入动作,固定元素不能遮住滚动内容。 -MVP-0 导航只显示已实现的生成入口、必要账户操作和退出;点数/API Key 不显示空入口。图片区只显示上传顺序,第一张自动主体,其余参考,不展示不可用的角色/拖拽控件。 +MVP-0 导航只显示已实现的生成入口、必要账户操作和退出;API Key 不显示空入口。图片区只显示上传顺序,第一张自动主体,其余参考,不展示不可用的角色/拖拽控件。 | 状态 | 必须呈现 | |---|---| @@ -148,7 +151,7 @@ MVP-0 导航只显示已实现的生成入口、必要账户操作和退出; 代码生成基于固定 go-admin-ui 的既有表导入: -- CRUD:providers、provider_models、prompt_templates、users;point_accounts/api_keys 到对应 MVP 再生成。 +- CRUD:providers、provider_models、prompt_templates、users;api_keys 到对应 MVP 再生成。 - 定制页:路由池编排、Provider 健康、生成记录详情。 - 标准 CRUD 可用一个明确复用固定 go-admin 视觉/交互的代表性原型覆盖同类页面;定制页分别覆盖加载、空、错误、权限和边界状态。 - 生成记录默认不展示 API Key、完整敏感错误或不必要的真实用户输入。 diff --git a/prototypes/31/v1/icons.css b/prototypes/31/v1/icons.css new file mode 100644 index 0000000..47fd1be --- /dev/null +++ b/prototypes/31/v1/icons.css @@ -0,0 +1,51 @@ +@font-face { + font-family: "remixicon"; + src: url("./remixicon.woff2") format("woff2"); + font-display: block; +} + +[class^="ri-"]::before, +[class*=" ri-"]::before { + font-family: "remixicon" !important; + font-style: normal; + font-weight: normal !important; + speak: never; + line-height: 1; +} + +.ri-add-line::before { content: "\ea13"; } +.ri-arrow-down-s-line::before { content: "\ea4e"; } +.ri-arrow-left-s-line::before { content: "\ea64"; } +.ri-arrow-right-s-line::before { content: "\ea6e"; } +.ri-check-line::before { content: "\eb7b"; } +.ri-close-line::before { content: "\eb99"; } +.ri-database-2-line::before { content: "\ec16"; } +.ri-drag-move-2-line::before { content: "\ec60"; } +.ri-edit-line::before { content: "\ec86"; } +.ri-error-warning-line::before { content: "\eca1"; } +.ri-eye-line::before { content: "\ecb5"; } +.ri-eye-off-line::before { content: "\ecb7"; } +.ri-file-text-line::before { content: "\ed0f"; } +.ri-fullscreen-line::before { content: "\ed9c"; } +.ri-history-line::before { content: "\ee17"; } +.ri-home-3-line::before { content: "\ee1b"; } +.ri-image-line::before { content: "\ee4b"; } +.ri-information-line::before { content: "\ee59"; } +.ri-key-line::before { content: "\ee71"; } +.ri-list-settings-line::before { content: "\eebd"; } +.ri-loader-4-line::before { content: "\eec6"; } +.ri-lock-line::before { content: "\eece"; } +.ri-menu-fold-line::before { content: "\ef3d"; } +.ri-more-2-line::before { content: "\ef77"; } +.ri-pulse-line::before { content: "\f035"; } +.ri-refresh-line::before { content: "\f064"; } +.ri-route-line::before { content: "\f09b"; } +.ri-save-line::before { content: "\f0b3"; } +.ri-search-line::before { content: "\f0d1"; } +.ri-send-plane-line::before { content: "\f0da"; } +.ri-server-line::before { content: "\f0e0"; } +.ri-settings-3-line::before { content: "\f0e6"; } +.ri-shield-keyhole-line::before { content: "\f107"; } +.ri-stop-circle-line::before { content: "\f19f"; } +.ri-time-line::before { content: "\f20f"; } +.ri-user-line::before { content: "\f264"; } diff --git a/prototypes/31/v1/index.html b/prototypes/31/v1/index.html new file mode 100644 index 0000000..807d827 --- /dev/null +++ b/prototypes/31/v1/index.html @@ -0,0 +1,142 @@ + + + + + + Chorus 运营后台 - 无点数范围变更原型 v1 + + + + + +
+
+ +
+
+ +
运营后台 / Provider 配置
+
+ + +
运运营管理员
+
+
+
+
+
+ + + +
+ + + + diff --git a/prototypes/31/v1/remixicon.woff2 b/prototypes/31/v1/remixicon.woff2 new file mode 100644 index 0000000..89a0b99 Binary files /dev/null and b/prototypes/31/v1/remixicon.woff2 differ