8.2 KiB
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 |
必填,1–200 字符 |
摘要 summary |
必填,1–2,000 字符,用于卡片 |
封面 cover |
必填,PNG/JPEG/WebP,最大 10 MiB;服务端读取真实 MIME、尺寸并生成受控媒体 |
项目压缩包 archive |
必填 ZIP;运营上传可保留独立的 512 MiB 服务端限制,服务端流式记录字节数、计算 SHA-256 并验证 ZIP 签名;该上传限制不由客户端在下载时执行 |
README readme |
必填 .md,UTF-8,最大 500 KiB;不接受可执行 HTML 作为发布内容 |
标签 tags |
0–16 个,每项 1–64 字符,去重 |
版本 version |
可空,最大 64 字符,仅作展示 |
排序 sort_order |
有界整数;列表默认按运营排序,再按发布时间稳定排序 |
状态 status |
draft、published、archived |
上传中的对象不能直接进入公开目录。发布必须在一个事务/发布代际中冻结元数据、经过净化和 URL 校验的 README、封面及 ZIP 摘要;任一校验失败则整个发布失败,旧的已发布版本继续可读。下架后列表和详情立即不可见,但已有审计记录不能物理删除。
后台必须记录操作人、时间、发布代际、变更摘要、归档 SHA-256/字节数、README 警告和远程图片 URL 数量。客户端不提供任何运营上传或发布入口。
3. README 远程图片发布规则
README 中的远程图片在“发布”时由服务端解析 Markdown,并保留通过校验的原始 HTTPS URL;服务端不下载或处理图片字节。处理要求:
- 只接受无用户名/密码的 HTTPS URL;拒绝
http:、data:、file:、本地路径和协议相对地址。 - 只允许默认 HTTPS 端口且不允许 fragment;解析当前 DNS,任一结果属于 loopback、私网、链路本地、保留地址、云元数据或其他非公网地址时拒绝发布。
- 限制 README 图片总量;不请求远端响应,因此不校验重定向、响应大小、MIME、像素、实际格式或内容。SVG 及其他 Electron 可渲染格式可直接显示。
- 发布后的 Markdown 保留通过校验的 URL,不创建 README 图片 blob 或新 release-media 行;现有媒体路由继续用于封面和历史已镜像发布。
- Markdown 原始 HTML 在客户端被禁用;服务端也从发布内容中移除 HTML 并返回 warning,避免运营误判展示效果。
- 客户端请求会直接到第三方图片 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 为 1–48,默认 24;cursor 是不透明游标。空列表返回 200,不要用 404。列表不返回 readmeMarkdown、archiveSha256、archiveFileName 或任何存储字段。
列表响应
{
"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
}
}
详情响应
详情复用全部列表字段,并增加:
{
"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",
"archiveFileName": "makelore-robot-arm-1.2.0.zip",
"archiveSha256": "64位小写十六进制SHA-256"
}
}
TypeScript 权威字段定义位于 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是列表、详情和运营审计使用的展示元数据,不是客户端下载门禁。客户端不比较它、Content-Length与实际流字节数,也不设置归档大小上限;客户端仍要求实际归档匹配archiveSha256和 ZIP 签名,验证失败时删除临时文件,不留下部分下载。
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 联调,并覆盖缺失或不准确 Content-Length 的归档响应。旧 /api/learning/courses、generation/progress/runtime 接口不在新客户端兼容范围内,可按服务端消费者盘点结果独立退役。