diff --git a/docs/02-architecture-and-code-map.md b/docs/02-architecture-and-code-map.md index 4883c81..29ca6d3 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: abef747ecc01c1538c376d9c35a299d3bc4fb3c5 -synchronized_at: 2026-08-22T04:05:56Z +wiki_revision: 2805f53ecd5f98fcf2a7602d47f4c89d55e40d7e +synchronized_at: 2026-08-23T17:03:07Z # 架构与代码地图 @@ -308,3 +308,83 @@ go-admin 的 AutoMigrate 只能在隔离、可丢弃数据库中用于研究固 - `migrations/000005_provider_plaintext_credentials` 切换明文凭据、菜单和权限;遇到已有加密 credential 时 fail closed,要求人工受控转换。 - Casbin adapter 绑定 `sys_casbin_rule` 并关闭 AutoMigrate;管理端启动不会创建或修改表结构。 - 列表与普通 Provider 详情不返回完整 Key;只有单条 credential 接口回显并设置 `Cache-Control: no-store`、`Pragma: no-cache`。真实值仍禁止进入日志、审计、错误、文档、工单和截图。 + +## MVP-2 API 开放与治理技术设计草案(#36,待验收) + +本节对应 [#35](https://git.ilapage.cn/OPC/chorus/issues/35) 和技术设计 [#36](https://git.ilapage.cn/OPC/chorus/issues/36)。它用于审核和拆分实施,**当前不是已放行的生产实现依据**;只有 #36 与原型 #37 同时经用户明确确认后,才能建立和执行生产单元工单。 + +### 范围和运行边界 + +- 面向现有受控 `users` 开放 API Key 和版本化生成接口;不开放注册、找回密码或匿名访问。 +- 保持浏览器 Cookie/CSRF、管理员 JWT/Casbin、终端用户 API Key 三条认证链相互独立,不能相互回退或混用。 +- 限流只控制请求速率和上游调用速度,不读取或写入点数,不形成计费、每日配额或额度余额。 +- 当前支持单 portal 进程内嵌单 worker 的部署形态;限流器在同一进程和所有 worker goroutine 之间共享。MVP-2 不宣称支持多 portal 实例;未来横向扩容前必须另行设计共享限流存储。 +- 不实施生成物、审计或 API Key 的自动删除;API Key 只撤销,审计追加写。 + +### 模块边界 + +```text +portal 浏览器路由 /api/* + -> session + CSRF -> 既有 generation service + +程序路由 /openapi/v1/* + -> Authorization: Bearer + -> API Key 认证 -> user/key 限流 -> 既有 generation service + -> 立即返回 202 或幂等重放 200,不调用上游 + +worker + -> 取得 generation lease + -> 路由和 circuit reservation + -> provider 限流 + 放行 -> BeginProviderAttempt -> 安全 HTTP Client -> 既有结果/失败协议 + 拒绝 -> 不调用上游、不写 Provider attempt、不改变 circuit,释放租约并延后领取 +``` + +API Key 记录和 repository 接口属于 `internal/core`,只依赖 GORM/标准库;随机令牌生成、SHA-256/常量时间校验和限流器位于 platform/portal 外层;Gin middleware 只在 `portal`。OpenAPI handler 复用现有 `portal/service` 的提交、历史、详情和文件归属校验,不复制生成业务规则。 + +### API Key 数据和安全协议 + +新增 `api_keys`:`id BIGINT UNSIGNED`、`user_id`、`name VARCHAR(80)`、唯一 `public_id VARCHAR(24)`、`key_prefix VARCHAR(32)`、`secret_hash BINARY(32)`、可空 `expires_at`、`last_used_at`、`revoked_at`、`created_at/updated_at`。`user_id` 对 `users` 使用 RESTRICT;索引覆盖 `(user_id,revoked_at,created_at)`、`public_id` 唯一和 `last_used_at`。不存原始 Key,也不提供恢复字段。 + +令牌固定为 `chorus__`:`public_id` 和 `secret` 均由 `crypto/rand` 生成,secret 至少 256 bit,使用无填充 base64url;数据库只保存 secret 的 SHA-256。认证先按 public_id 定位候选,再对请求 secret 计算哈希并用常量时间比较;随机 secret 具有足够熵,不能使用用户密码式慢哈希。错误响应统一为 `invalid_api_key`,不区分不存在、已撤销、已到期或用户停用。 + +用户端接口位于 session/CSRF 下:`GET /api/api-keys`、`POST /api/api-keys`、`PATCH /api/api-keys/:id` 和幂等撤销 `DELETE /api/api-keys/:id`。创建响应设置 `Cache-Control: no-store` 与 `Pragma: no-cache`,完整 Key 只在该次 201 响应中出现;列表、详情、后续读取、日志、审计和管理端永远只返回名称、前缀、状态和时间。轮换流程为先创建新 Key,再撤销旧 Key,不增加会泄露旧值的“重新显示”或“旋转并返回旧值”接口。 + +管理员 API 位于既有 JWT/Casbin 边界:`GET /api/v1/chorus/api-keys` 查看用户和 Key 元数据,`POST /api/v1/chorus/api-keys/:id/revoke` 幂等撤销。管理员不能创建用户 Key、不能代用户读取完整 Key。用户自行撤销写 API 安全审计;管理员撤销同时写 `admin_audit_events` 和 API 安全审计。 + +### OpenAPI v1 契约 + +程序接口仅接受 `Authorization: Bearer`,拒绝 query 参数、Cookie 和管理员 JWT。认证成功后把 `user_id` 与 `api_key_id` 放入请求 context。响应使用既有 `{error:{code,message}}` 错误信封,时间统一为 UTC RFC3339,返回 `X-Request-ID`,认证和限流响应禁止缓存。 + +| 方法与路径 | 内容类型 | 行为 | +|---|---|---| +| `POST /openapi/v1/generations/text` | JSON | 提交文本生成,202;幂等重放 200 | +| `POST /openapi/v1/generations/image` | multipart | 复用已确认 metadata/files 契约,提交图片生成或编辑 | +| `GET /openapi/v1/generations` | query | 复用 user 作用域 HMAC cursor 历史 | +| `GET /openapi/v1/generations/:id` | - | 查询本人任务详情 | +| `GET /openapi/v1/generations/:id/inputs/:inputID` | - | 读取本人输入原图 | +| `GET /openapi/v1/generations/:id/outputs/:outputID` | - | 读取本人输出原图 | +| `GET /openapi/v1/generations/:id/outputs/:outputID/thumbnail` | - | 读取本人缩略图 | +| `GET /openapi/v1/openapi.json` | - | 返回与实现同版本的 OpenAPI 3.1 描述 | + +OpenAPI 提交只从必填 `Idempotency-Key` 请求头读取幂等键,规则为去空格后 8 至 128 个可打印 ASCII 字符;不同时接受 body 同名字段。已有 `(user_id,idempotency_key)` 唯一约束继续保证浏览器和 API Key 跨渠道重放属于同一用户。下载 URL 必须指向 `/openapi/v1`,不能要求 API 客户端持有浏览器 Cookie。 + +关键状态码:参数/图片错误 400,缺失或无效 Key 401,跨用户对象统一 404,幂等冲突 409,速率限制 429 并带整数秒 `Retry-After`,路由未配置或服务暂不可用 503。接口规范以仓库中的 OpenAPI 3.1 文件为源,并用 handler 合约测试防止路径、状态码和 schema 漂移;MVP-2 不交付 Swagger UI、SDK、webhook、批量、取消或流式接口。 + +### 三维限流和异步延后 + +采用固定窗口限流器,配置必须分别给出正整数容量和窗口:用户提交维度、单 API Key 总请求维度、单 Provider 上游 attempt 维度。生产环境缺少任一配置时启动失败;开发和测试必须在 fixture 中显式设置,技术设计不臆造生产阈值。用户和 Key 同时检查,任一拒绝即返回 429;Provider 桶按 Provider ID 分开,但共享同一组策略值。 + +限流状态只保存在当前 portal 进程内,重启会重置窗口;这是流量保护而不是配额或计费账本。日志和审计不记录原始 Key。HTTP 429 包含 `Retry-After`,错误正文不披露 Provider、内部路由或其他用户的桶状态。 + +Provider 限流检查发生在真实上游调用前。放行后才执行 `BeginProviderAttempt`,因此本地限流不计入 `provider_attempt_count`、`attempts`、熔断或故障转移。若当前候选受限,可继续检查尚未调用的其他候选;全部候选都只因本地 Provider 限流不可用时,使用最早 retry 时间重新排队,而不是写失败终态。 + +为此 `generations` 增加非空 `available_at DATETIME(6)`,默认创建时间;队列只领取 `pending AND available_at <= NOW(6)` 或租约过期的 running。新增带 lease token 的 `Defer` CAS:把当前 running 任务改回 pending,清空 lease,设置 `available_at`,但不增加 Provider attempt、错误或 circuit;记录一条脱敏 `local_rate_limit` 调度事件。该事件不能被 `usedRouteMembers` 当作已调用候选。延后时间取最早允许时间并设置上限,避免异常配置长期占用任务。已有外部 429 仍按既有红线完成真实 Provider attempt、计入熔断并换下一家,两者不得合并。 + +### 审计、迁移和验证 + +新增 `api_audit_events` 作为追加写安全审计:`id`、可空 `user_id/api_key_id/generation_id`、`action`、`result`、`request_id`、可空 `status_code/error_code`、脱敏 `summary JSON`、`created_at`。记录 Key 创建/改名/用户撤销/管理员撤销、程序提交成功或拒绝、认证成功后的限流拒绝;普通成功查询不逐条入库,`last_used_at` 用节流更新。未知 Key 的认证失败只写受控安全日志和指标,避免攻击者制造无限审计行。summary 禁止 Prompt、文件内容、完整 Key、Authorization、Cookie、响应正文和原始 IP。 + +`000006_mvp2_openapi_governance` 创建两张表、`available_at`/队列索引和管理端菜单/API/Casbin seed;down 按稳定键删除 seed、索引、列和新表。必须在固定隔离 MySQL 8 上完成空库及含 MVP-1 数据的 up/down/up,并验证现有 generation 仍可领取。回退前停止 portal/worker;回退会删除 API Key 和新增审计,属于需人工确认的数据丢失操作,不能在生产自动执行。 + +必测矩阵包括:随机格式/哈希/常量比较、完整 Key 只出现一次、撤销/到期/用户停用、三条认证链不混用、CSRF、跨用户 404、幂等重放/冲突、JSON 与 multipart 边界、历史 cursor、文件归属、429 与 Retry-After、user/key/provider 三维桶、重启边界、Provider 本地限流不调用上游且不改变 retryable/circuit/attempt、Defer CAS 和旧 lease、审计脱敏、管理员权限、OpenAPI schema 合约、MySQL up/down/up,以及 mock 上游完整链路。不得使用真实 Provider 额度做回归。 diff --git a/docs/03-business-rules-and-glossary.md b/docs/03-business-rules-and-glossary.md index 0bb83c1..1814310 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: 88e750129170ef7fcce9d2194cc96bb74d9e462e -synchronized_at: 2026-08-22T03:42:45Z +wiki_revision: 5ba3d903cb98df6651906761b8233032c810fca6 +synchronized_at: 2026-08-23T17:03:09Z # 业务规则与术语 @@ -125,3 +125,17 @@ synchronized_at: 2026-08-22T03:42:45Z ## 已交付:Provider 凭据明文与单条回显 2026-08-22 用户明确接受风险并确认 [#30](https://git.ilapage.cn/OPC/chorus/issues/30):Provider API Key 目标状态改为明文落库,管理员可在单个 Provider 页面显式读取和再次隐藏。列表接口不得批量返回完整值,读取响应必须禁止缓存;日志、审计摘要、错误、工单、Wiki、原型和截图继续禁止记录真实密钥。#24 已完成明文迁移、单条回显和运行时主密钥移除,并通过隔离 MySQL 8、真实管理 API 联调及用户验收。 + +## MVP-2 已确认范围与待确认技术规则(2026-08-24) + +以下产品范围已经用户确认:只服务现有受控用户;交付 API Key、版本化程序生成接口、用户/API Key/Provider 维度限流、安全审计和必要管理页面;不开放注册,不自动删除数据,不增加计费、点数或每日配额。 + +以下是 #36 待验收的技术规则,在用户明确确认前不得作为生产编码放行依据: + +1. 完整 API Key 只在创建成功响应显示一次,数据库只保存随机 secret 的 SHA-256、public_id、前缀和元数据;用户和管理员后续都不能回显完整值。 +2. API Key 只代表 `users`,不得认证 `sys_user`;Cookie/CSRF、管理员 JWT/Casbin 和 Bearer API Key 三条认证链不得混用或回退。 +3. OpenAPI 同步链路只认证、限流、校验和落库,不调用上游;worker 是唯一调用上游的位置。 +4. user/key/provider 限流是进程内固定窗口流量保护,不是计费或每日配额;当前只支持单 portal 实例,多实例部署需另行设计共享存储。 +5. Provider 本地限流不得记作上游 429、不得增加 Provider attempt、不得触发熔断或消耗故障转移;所有候选受限时只延后 generation。 +6. API 安全审计禁止保存 Prompt、文件、完整 Key、Authorization、Cookie、响应正文或原始 IP;未知 Key 失败不允许制造无限数据库审计行。 +7. API Key 只撤销不物理删除;本 MVP 不自动删除 Key、审计、生成任务或生成物。 diff --git a/docs/04-local-development-and-verification.md b/docs/04-local-development-and-verification.md index 5861847..e2b133a 100644 --- a/docs/04-local-development-and-verification.md +++ b/docs/04-local-development-and-verification.md @@ -2,8 +2,8 @@ generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Local-Development-and-Verification wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Local-Development-and-Verification.- -wiki_revision: f4654349f06e495e499742354d3fb834ee08c4fd -synchronized_at: 2026-08-22T03:08:27Z +wiki_revision: b6d4960d79265a7823111989b93a8947f71c094e +synchronized_at: 2026-08-23T17:03:12Z # 本地开发与验证 @@ -343,3 +343,9 @@ pnpm --dir admin-ui dev ``` 管理端不会创建数据库、运行 migration、seed 或 AutoMigrate。首次本地运行先人工创建空库并执行 `migrate -path migrations ... up`,再通过 `chorus-admin-bootstrap` 建立管理员。已验证纯账号密码登录、动态 Chorus 菜单和 Provider 列表 API;Casbin 使用 `sys_casbin_rule`。前端固定基线默认端口为 9527,端口占用时 Vue CLI 会选择下一可用端口。 + +## MVP-2 设计中的配置门禁(#36,尚未实现) + +生产实现将增加三组显式正整数配置,分别控制用户提交、单 API Key 总请求和单 Provider 上游 attempt 的容量与窗口。生产环境缺少任何一项都必须启动失败;开发/测试 fixture 显式给值,不把测试值冒充生产阈值。具体环境变量名在实现工单中按现有 `CHORUS_*` 命名固定,并同步本页和部署页。 + +当前部署边界仍为单 portal 进程内嵌 worker。MVP-2 限流状态在进程内共享,重启会重置窗口;在共享限流存储设计和验证完成前,不得把多个 portal 实例当作受支持部署。Provider 限流与延后队列只允许用 mock 上游验证。 diff --git a/docs/09-product-requirements-overview.md b/docs/09-product-requirements-overview.md index 5794b98..a7e3bf1 100644 --- a/docs/09-product-requirements-overview.md +++ b/docs/09-product-requirements-overview.md @@ -2,8 +2,8 @@ generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Product-Requirements-Overview wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Product-Requirements-Overview.- -wiki_revision: d19c38a02f3070a37e83d79a940098792fa7dbc5 -synchronized_at: 2026-08-23T16:31:59Z +wiki_revision: aa3543138722e5653fbc9df5764e9e08298d15c1 +synchronized_at: 2026-08-23T17:03:26Z # 产品需求总览 @@ -284,3 +284,9 @@ portal 已按 #18 v1 原型实现文生图与图片编辑切换、role_rule、 ## #19 MVP-1 汇总验收状态(2026-08-24,已验收) MVP-1 的设计、生产实现、变更工单和跨模块集成验收均已完成;用户于 2026-08-24 明确确认 #19 验收通过。交付范围包括多 Provider 路由/熔断/故障转移、多协议 mock 链路、运营管理端、portal 图片角色编辑与历史体验。真实 Provider 连通性仍未执行,后续如需验证必须单独授权。 + +## MVP-2 API 开放与治理(2026-08-24,设计中) + +用户已确认 MVP-2 面向现有受控用户交付安全的 API Key、OpenAPI 生成接口、用户/API Key/Provider 限流、审计和必要的用户端/管理端页面;不开放注册,不实施自动数据删除,不增加计费、点数或每日配额。汇总工单为 [#35](https://git.ilapage.cn/OPC/chorus/issues/35),技术设计 [#36](https://git.ilapage.cn/OPC/chorus/issues/36) 正在审核准备,界面原型 [#37](https://git.ilapage.cn/OPC/chorus/issues/37) 的待审核快照为 `prototypes/37/v1/index.html`。 + +生产实现尚未放行。必须先由用户明确确认 #36 的认证、API、数据、限流、审计和迁移设计,并确认 #37 v1 覆盖的用户端创建/一次显示/撤销及管理端元数据检索/撤销、响应式和异常状态;两项均通过后才拆分生产实现单元工单。 diff --git a/prototypes/37/v1/icons.min.js b/prototypes/37/v1/icons.min.js new file mode 100644 index 0000000..f24ec06 --- /dev/null +++ b/prototypes/37/v1/icons.min.js @@ -0,0 +1,36 @@ +(()=>{var u={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":2,"stroke-linecap":"round","stroke-linejoin":"round"};var L=([e,a,t])=>{let r=document.createElementNS("http://www.w3.org/2000/svg",e);return Object.keys(a).forEach(o=>{r.setAttribute(o,String(a[o]))}),t?.length&&t.forEach(o=>{let s=L(o);r.appendChild(s)}),r},y=(e,a={})=>{let r={...u,...a};return L(["svg",r,e])};var R=e=>{for(let a in e)if(a.startsWith("aria-")||a==="role"||a==="title")return!0;return!1};var T=(...e)=>e.filter((a,t,r)=>!!a&&a.trim()!==""&&r.indexOf(a)===t).join(" ").trim();var q=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(a,t,r)=>r?r.toUpperCase():t.toLowerCase());var b=e=>{let a=q(e);return a.charAt(0).toUpperCase()+a.slice(1)};var G=e=>Array.from(e.attributes).reduce((a,t)=>(a[t.name]=t.value,a),{}),U=e=>typeof e=="string"?e:!e||!e.class?"":e.class&&typeof e.class=="string"?e.class.split(" "):e.class&&Array.isArray(e.class)?e.class:"",p=(e,{nameAttr:a,icons:t,attrs:r})=>{let o=e.getAttribute(a);if(o==null)return;let s=b(o),f=t[s];if(!f)return console.warn(`${e.outerHTML} icon name was not found in the provided icons object.`);let l=G(e),O=R(l)?{}:{"aria-hidden":"true"},F={...u,"data-lucide":o,...O,...r,...l},v=U(l),H=U(r),D=T("lucide",`lucide-${o}`,...v,...H);D&&Object.assign(F,{class:D});let E=y(f,F);return e.parentNode?.replaceChild(E,e)};var m=[["path",{d:"M20 6 9 17l-5-5"}]];var d=[["circle",{cx:"12",cy:"12",r:"10"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16"}]];var x=[["circle",{cx:"12",cy:"12",r:"10"}],["path",{d:"M12 6v6h4"}]];var i=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"}]];var n=[["path",{d:"M12 15V3"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"}],["path",{d:"m7 10 5 5 5-5"}]];var c=[["path",{d:"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49"}],["path",{d:"M14.084 14.158a3 3 0 0 1-4.242-4.242"}],["path",{d:"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143"}],["path",{d:"m2 2 20 20"}]];var C=[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"}],["circle",{cx:"12",cy:"12",r:"3"}]];var h=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5"}],["path",{d:"M10 9H8"}],["path",{d:"M16 13H8"}],["path",{d:"M16 17H8"}]];var S=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2"}],["circle",{cx:"9",cy:"9",r:"2"}],["path",{d:"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"}]];var g=[["path",{d:"m16 17 5-5-5-5"}],["path",{d:"M21 12H9"}],["path",{d:"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"}]];var w=[["path",{d:"M5 12h14"}],["path",{d:"M12 5v14"}]];var k=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"}],["path",{d:"M21 3v5h-5"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"}],["path",{d:"M8 16H3v5"}]];var P=[["path",{d:"m21 21-4.34-4.34"}],["circle",{cx:"11",cy:"11",r:"8"}]];var A=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"}],["path",{d:"m9 12 2 2 4-4"}]];var M=[["path",{d:"M18 6 6 18"}],["path",{d:"m6 6 12 12"}]];var B=({icons:e={},nameAttr:a="data-lucide",attrs:t={},root:r=document,inTemplates:o}={})=>{if(!Object.values(e).length)throw new Error(`Please provide an icons object. +If you want to use all the icons you can import it like: + \`import { createIcons, icons } from 'lucide'; +lucide.createIcons({icons});\``);if(typeof r>"u")throw new Error("`createIcons()` only works in a browser environment.");if(Array.from(r.querySelectorAll(`[${a}]`)).forEach(f=>p(f,{nameAttr:a,icons:e,attrs:t})),o&&Array.from(r.querySelectorAll("template")).forEach(l=>B({icons:e,nameAttr:a,attrs:t,root:l.content,inTemplates:o})),a==="data-lucide"){let f=r.querySelectorAll("[icon-name]");f.length>0&&(console.warn("[Lucide] Some icons were found with the now deprecated icon-name attribute. These will still be replaced for backwards compatibility, but will no longer be supported in v1.0 and you should switch to data-lucide"),Array.from(f).forEach(l=>p(l,{nameAttr:"icon-name",icons:e,attrs:t})))}};var V={AlertCircle:d,Check:m,Clock3:x,Copy:i,Download:n,Eye:C,EyeOff:c,FileText:h,Image:S,LogOut:g,Plus:w,RefreshCw:k,Search:P,ShieldCheck:A,X:M};window.renderChorusIcons=(e=document)=>B({icons:V,attrs:{"aria-hidden":"true"},root:e});document.addEventListener("DOMContentLoaded",()=>window.renderChorusIcons());})(); +/*! Bundled license information: + +lucide/dist/esm/defaultAttributes.mjs: +lucide/dist/esm/createElement.mjs: +lucide/dist/esm/shared/src/utils/hasA11yProp.mjs: +lucide/dist/esm/shared/src/utils/mergeClasses.mjs: +lucide/dist/esm/shared/src/utils/toCamelCase.mjs: +lucide/dist/esm/shared/src/utils/toPascalCase.mjs: +lucide/dist/esm/replaceElement.mjs: +lucide/dist/esm/icons/check.mjs: +lucide/dist/esm/icons/circle-alert.mjs: +lucide/dist/esm/icons/clock-3.mjs: +lucide/dist/esm/icons/copy.mjs: +lucide/dist/esm/icons/download.mjs: +lucide/dist/esm/icons/eye-off.mjs: +lucide/dist/esm/icons/eye.mjs: +lucide/dist/esm/icons/file-text.mjs: +lucide/dist/esm/icons/image.mjs: +lucide/dist/esm/icons/log-out.mjs: +lucide/dist/esm/icons/plus.mjs: +lucide/dist/esm/icons/refresh-cw.mjs: +lucide/dist/esm/icons/search.mjs: +lucide/dist/esm/icons/shield-check.mjs: +lucide/dist/esm/icons/x.mjs: +lucide/dist/esm/lucide.mjs: + (** + * @license lucide v1.33.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + *) +*/ diff --git a/prototypes/37/v1/index.html b/prototypes/37/v1/index.html new file mode 100644 index 0000000..0e48391 --- /dev/null +++ b/prototypes/37/v1/index.html @@ -0,0 +1,86 @@ + + + + + + + Chorus - MVP-2 API Key 原型 v1 + + + + +
+ CChorus + +
+ +
+ +
+
+
原型状态
+

API Key

为当前账户创建程序调用凭据。完整 Key 只在创建成功后显示一次,之后只能查看名称、前缀和使用状态。

+

每个 Key 都代表当前账户只通过 Authorization Bearer 请求头发送,不要放进网址、浏览器脚本、工单或日志。

+

我的 API Key

撤销后立即失效,不能恢复;需要轮换时先创建新 Key。

共 4 个
+
+
+ + + + + + + +
+ + + +