# 实例自定义图标技术设计 ## 数据流 ```text 本地 PNG/JPEG/WebP → 浏览器解码并居中裁剪为 512×512 PNG → POST /api/instance-icon(Bearer,二进制,≤4 MiB) → 服务端验证 Content-Type、PNG 签名、IHDR 尺寸 → CONFIG_DIR/.instance-icon.png.tmp 原子 rename → 返回 { custom, version, updatedAt, url } → 前端更新 favicon、Apple Touch、登录 Logo、通知图标与设置预览 ``` ## 持久化与升级 - 正式文件:`CONFIG_DIR/instance-icon.png`。 - 临时文件:`CONFIG_DIR/.instance-icon.png.tmp`,失败时清理。 - 默认 `CONFIG_DIR` 为 `APP_DIR/config`;`.gitignore` 精确忽略这两个文件。 - 如果部署设置 `CC_WEB_CONFIG_DIR` 到仓库外,图标自动跟随外置配置目录。 - 不覆盖任何 `public/*.png`,源码升级只更新默认兜底图。 ## HTTP 契约 ### GET /api/instance-icon/config 公开返回: ```json { "custom": true, "version": "sha256-short-hash", "updatedAt": "ISO-8601", "url": "/api/instance-icon?v=sha256-short-hash" } ``` 未配置时 `custom=false`,`version=default`,URL 仍指向统一读取接口。 ### GET /api/instance-icon - 自定义存在且有效:返回该 PNG。 - 未配置或运行文件失效:返回 `public/icon-192.png`。 - `Content-Type: image/png`、`X-Content-Type-Options: nosniff`。 - 使用 ETag/no-cache;版本化 URL 负责同页缓存刷新。 ### POST /api/instance-icon - 必须通过现有 Bearer token 鉴权。 - 仅 `Content-Type: image/png`,body 非空且不超过 4 MiB。 - 必须是标准 PNG 签名,IHDR 宽高均为 512。 - 固定路径原子写入;返回最新配置对象。 ### DELETE /api/instance-icon - 必须通过现有 Bearer token 鉴权。 - 删除实例运行文件,幂等返回默认配置对象。 ### GET /api/site.webmanifest - 未自定义:保留现有 192/512 默认图标声明。 - 已自定义:使用版本化 `/api/instance-icon`,声明 512×512。 ## 前端契约 - head 中 favicon、Apple Touch 与 Manifest 从首屏即指向动态 API,默认服务端兜底避免闪烁。 - 需要动态更新的图片用 `data-instance-icon` 标识;图标 link 用 `data-instance-icon-link` 标识。 - `instanceIconUrl(config)` 是唯一 URL 生成入口;浏览器通知和 Service Worker 默认也用 `/api/instance-icon`。 - 外观设置中新增“实例图标”区:预览、选择图标、恢复默认、状态文字。 - 客户端拒绝非 PNG/JPEG/WebP;使用 canvas 居中裁剪,不拉伸;上传失败保留旧图标。 ## 错误语义 - 401:未鉴权写操作。 - 400:空内容、错误 MIME、非法 PNG、非 512×512。 - 413:超过 4 MiB。 - 500:原子保存/读取不可恢复错误;前端显示服务端中文消息。 ## 测试顺序 1. 增加聚焦服务集成测试并确认在实现前失败。 2. 覆盖默认读取、鉴权、非法输入、上传、覆盖版本、恢复默认与 Git 外置路径。 3. 增加前端静态/DOM 契约测试,覆盖设置 UI 和所有图标消费点。 4. 实现最小代码使测试通过,再运行总回归。