Files
makelore/docs/learning-project-catalog-server-contract.md

137 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AI 学习项目目录服务端协作契约
> 状态Makelore 客户端与 Electron Main 已按本契约实现;运营后台、数据库、对象存储与生产数据不在本仓库内,需在 Works Square 侧实现并部署后联调。
## 1. 产品与权限边界
AI 学习是登录后可用的精选项目目录。V1 只包含项目列表、README 详情和 ZIP 下载不包含课程生成、课程播放器、本地课程库、学习进度、Agent、ASR、评分或课堂 runtime也不兼容旧课程接口。
沿用现有账号与 `module_access.learning` 策略客户端入口策略在页面初始化前拦截无权限账号Works Square 的列表、详情、媒体和下载接口仍必须独立校验登录态及 Learning 权限,不能依赖客户端置灰。
Renderer 的固定链路为 `Renderer -> Host API -> Electron Main -> Works Square`。Renderer 不得获得 Works Token、对象存储凭据、内部存储 key、任意下载 URL或本地保存路径。
## 2. 运营后台“学习项目管理”
运营后台新增一级菜单“学习项目管理”,至少支持新建、编辑、预览、发布、下架、排序和查看发布记录。项目字段如下:
| 字段 | 规则 |
| --- | --- |
| 项目名称 `name` | 必填1200 字符 |
| 摘要 `summary` | 必填12,000 字符,用于卡片 |
| 封面 `cover` | 必填PNG/JPEG/WebP最大 10 MiB服务端读取真实 MIME、尺寸并生成受控媒体 |
| 项目压缩包 `archive` | 必填 ZIP最大 512 MiB服务端流式计算字节数与 SHA-256并验证 ZIP 签名 |
| README `readme` | 必填 `.md`UTF-8最大 500 KiB不接受可执行 HTML 作为发布内容 |
| 标签 `tags` | 016 个,每项 164 字符,去重 |
| 版本 `version` | 可空,最大 64 字符,仅作展示 |
| 排序 `sort_order` | 有界整数;列表默认按运营排序,再按发布时间稳定排序 |
| 状态 `status` | `draft``published``archived` |
上传中的对象不能直接进入公开目录。发布必须在一个事务/发布代际中冻结元数据、经过净化和 URL 校验的 README、封面及 ZIP 摘要;任一校验失败则整个发布失败,旧的已发布版本继续可读。下架后列表和详情立即不可见,但已有审计记录不能物理删除。
后台必须记录操作人、时间、发布代际、变更摘要、归档 SHA-256/字节数、README 警告和远程图片 URL 数量。客户端不提供任何运营上传或发布入口。
## 3. README 远程图片发布规则
README 中的远程图片在“发布”时由服务端解析 Markdown并保留通过校验的原始 HTTPS URL服务端不下载或处理图片字节。处理要求
1. 只接受无用户名/密码的 HTTPS URL拒绝 `http:``data:``file:`、本地路径和协议相对地址。
2. 只允许默认 HTTPS 端口且不允许 fragment解析当前 DNS任一结果属于 loopback、私网、链路本地、保留地址、云元数据或其他非公网地址时拒绝发布。
3. 限制 README 图片总量不请求远端响应因此不校验重定向、响应大小、MIME、像素、实际格式或内容。SVG 及其他 Electron 可渲染格式可直接显示。
4. 发布后的 Markdown 保留通过校验的 URL不创建 README 图片 blob 或新 release-media 行;现有媒体路由继续用于封面和历史已镜像发布。
5. Markdown 原始 HTML 在客户端被禁用;服务端也从发布内容中移除 HTML 并返回 warning避免运营误判展示效果。
6. 客户端请求会直接到第三方图片 origin图片可用性、后续 DNS/重定向和格式支持由 origin 与 Electron 决定origin 也会看到请求方网络信息。单图加载失败不得阻断 README 其余内容。
封面同样优先返回固定媒体路径。若返回 HTTPS CDN 地址,该地址必须无凭据、由 Works Square 控制且不包含用户隐私。
## 4. 客户端公开接口
| 方法 | Works Square 路径 | 用途 |
| --- | --- | --- |
| `GET` | `/api/learning/projects?cursor=&limit=` | 已发布项目分页列表 |
| `GET` | `/api/learning/projects/:id` | 项目详情和 README |
| `GET` | `/api/learning/projects/:id/media/:mediaId` | 受控封面/README raster 图片 |
| `GET` | `/api/learning/projects/:id/archive` | ZIP 字节流或同 Works origin 重定向 |
`limit` 为 148默认 24`cursor` 是不透明游标。空列表返回 `200`,不要用 `404`。列表不返回 `readmeMarkdown``archiveSha256``archiveFileName` 或任何存储字段。
### 列表响应
```json
{
"success": true,
"data": {
"items": [
{
"id": "robot-arm",
"name": "桌面机械臂",
"summary": "从零搭建一个可以抓取积木的桌面机械臂。",
"cover": {
"url": "/api/learning/projects/robot-arm/media/cover",
"alt": "桌面机械臂成品",
"width": 1600,
"height": 900
},
"tags": ["机器人", "Python"],
"version": "1.2.0",
"archiveBytes": 12582912,
"publishedAt": "2026-08-01T00:00:00Z",
"updatedAt": "2026-08-18T00:00:00Z"
}
],
"nextCursor": null,
"total": 1
}
}
```
### 详情响应
详情复用全部列表字段,并增加:
```json
{
"success": true,
"data": {
"id": "robot-arm",
"name": "桌面机械臂",
"summary": "从零搭建一个可以抓取积木的桌面机械臂。",
"cover": {
"url": "/api/learning/projects/robot-arm/media/cover",
"alt": "桌面机械臂成品",
"width": 1600,
"height": 900
},
"tags": ["机器人", "Python"],
"version": "1.2.0",
"archiveBytes": 12582912,
"publishedAt": "2026-08-01T00:00:00Z",
"updatedAt": "2026-08-18T00:00:00Z",
"readmeMarkdown": "# 桌面机械臂\n\n![接线图](https://docs.example.com/wiring.svg)",
"archiveFileName": "makelore-robot-arm-1.2.0.zip",
"archiveSha256": "64位小写十六进制SHA-256"
}
}
```
TypeScript 权威字段定义位于 [`shared/learning.ts`](../shared/learning.ts)。未列出的内部字段会被 Main 丢弃。
### 媒体与归档响应
- 媒体接口只用于封面和历史已镜像内容,返回受控 raster 内容并设置准确 `Content-Type``Content-Length`Main 限制 10 MiB并转换为 data URL 给 Renderer。新发布 README 的 HTTPS 图片不经过该接口。
- 归档接口返回 `application/zip``application/x-zip-compressed``application/octet-stream`,设置准确 `Content-Length`。如需重定向,只能跳转到与 Works API 相同 origin 的 HTTP(S) 地址,最多 5 跳Main 不向重定向目标转发 Bearer。
- 归档字节必须与详情中的 `archiveBytes``archiveSha256` 精确一致。客户端验证失败时删除临时文件,不留下部分下载。
## 5. 错误、缓存与上线顺序
统一使用现有 `{ success, status, code, error }` 错误封装。至少支持:
- `LEARNING_AUTH_REQUIRED`401
- `LEARNING_FORBIDDEN`403
- `LEARNING_PROJECT_NOT_FOUND`404
- `LEARNING_CONFLICT`409发布代际变化
- `LEARNING_UNAVAILABLE`429/502/503
列表/详情可对发布代际生成 ETag媒体和归档按内容摘要设置不可变缓存但不得缓存带用户私有授权的响应到公共共享缓存。日志不得记录 Bearer、签名 URL、完整 README 图片 URL 或对象存储 key可记录规范化主机、URL 摘要和图片数量。
上线顺序:先部署数据库/对象存储、运营后台、README HTTPS URL 校验和四个公开接口,再发布包含直连图片支持的新客户端;随后用真实账号完成发布/下架/远程图片(含 SVG、失效 origin 和隐私提示)/ZIP 联调。旧 `/api/learning/courses`、generation/progress/runtime 接口不在新客户端兼容范围内,可按服务端消费者盘点结果独立退役。