feat: support custom instance icons and refresh release

This commit is contained in:
shiyue
2026-08-25 22:11:07 +08:00
parent bd20a79d4b
commit 05480e511d
20 changed files with 1104 additions and 23 deletions

View File

@@ -0,0 +1,84 @@
# 实例自定义图标技术设计
## 数据流
```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. 实现最小代码使测试通过,再运行总回归。