Files
FootPrint/docs/api.md
T
root 8cdf8b44fa docs: refresh public docs for current codebase, add SECURITY.md
- README: real counts (44 commands, 61 integration asserts, demo=25 records
  + 4 semesters + 7 attachments), add resume/assessment module features,
  env vars incl. LSKY, doc navigation table, done-roadmap items checked
- docs/api.md: /auth/logout, admin users/demo-rebuild/sync-lsky routes,
  attachment thumb/download channels, profile_upsert top-key merge
  semantics, full footprint_list query reference, 44-command table
- sync-server/README: replace stale ADMIN_TOKEN/X-Admin-Token admin section
  (actual is_admin Bearer model), add go-live checklist, backup note for WAL
- CHANGELOG: add Unreleased section (resume/assessment modules, security
  fixes, perf tuning)
- CONTRIBUTING: 61 asserts, security-sensitive-change guidance, perf baseline
  table, security issue routing
- docs/SECURITY.md: new - report channel, threat model table, hardening
  summary, known tradeoffs
- LICENSE: ensure MIT file matches README badge
2026-10-17 22:27:56 +08:00

114 lines
5.4 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.
# HTTP API 参考
Base URL:部署域名(如 `https://footpr.example.com`)。除 `/health` 与 `/auth/register|login` 外,
所有接口都鉴权。登录限速:同一 IP + 用户名连续失败 5 次会收到 429,冷却 10 分钟。
统一响应格式:
```json
{ "ok": true, "data": { ... } }
{ "ok": false, "error": "错误原因" }
```
## 健康检查
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/health` | 存活探针,无需鉴权 |
## 认证
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | `/auth/register` | 注册(`REGISTER_ENABLED`/邀请码/`MAX_USERS` 门控;首个非 demo 账号自动成为管理员)。请求体里的 `seed` 标志一律被服务端剥离,仅供内部播种 |
| POST | `/auth/login` | 登录,返回 Bearer Token(365 天) |
| POST | `/auth/logout` | 吊销当前 Bearer Token(服务端删除 token 行) |
| GET | `/auth/me` | 当前用户信息(`user_id` / `username` / `is_admin` / `device_id`) |
```bash
TOKEN=$(curl -s https://footpr.example.com/auth/login \
-H 'content-type: application/json' \
-d '{"username":"demo","password":"demo1234","device_name":"cli"}' | jq -r .data.token)
```
## 管理(仅 is_admin 会话)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/admin/users` | 用户列表(设备数/记录数/附件用量),元数据不含档案内容 |
| POST | `/admin/users/delete` | 删除用户 `{"user_id":"..."}`,级联清理图床/档案库/附件/会话;不能删自己、不能删最后一位管理员 |
| GET | `/admin/invites` | 邀请码列表(remark、创建者/使用者、valid、holder_gone) |
| POST | `/admin/invites/generate` | 批量生成随机码 `{"count": 5}`(1-50) |
| POST | `/admin/invites/create` | 新建自定义码 `{"code":"XIONG-2025","remark":"备注"}`(4-32 位字母数字-_) |
| POST | `/admin/invites/update` | 改备注 / 启停 `{"id":1,"remark":"...","valid":false}`(已使用的码不可再启用) |
| POST | `/admin/invites/delete` | 删除 `{"id":1}` |
| POST | `/admin/invites/revoke` | 作废(等价 update valid=false,兼容保留) |
| POST | `/admin/demo-rebuild` | 级联删除现有 demo 并重建、重播种演示数据 |
| POST | `/admin/demo-seed` | 给 demo 播种演示数据(幂等;档案库非空时返回 seeded:false) |
| POST | `/admin/attachments/sync-lsky` | 把当前用户全部未上图床的附件批量推送到 Lsky(需配置 `LSKY_API`/`LSKY_TOKEN`) |
## 同步
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/sync/status` | 当前用户同步账本统计 |
| POST | `/sync/push` | 推送 wire 变更(LWW 上/删除,立即生效) |
| GET | `/sync/pull?cursor=...&limit=...` | 按 seq 游标增量拉取 |
| POST | `/sync/attachment` | 上传附件 `{"sha256","size","mime","file_name","data_b64"}`(内容寻址去重,哈希校验) |
| GET | `/sync/attachment/<sha256>` | 下载附件(`Cache-Control: private, immutable`) |
附件另有两个业务命令通道:`POST /api/attachment_read`(base64 全文,含缩略图通道 `attachment_thumb`)
与 `POST /api/attachment_download`(直接落盘,`Content-Disposition` 带原始文件名)。
## 业务命令层 `POST /api/<command>`
与桌面端共用同一命令层(`app/server/db.mjs`),按用户隔离。44 个命令:
| 分组 | 命令 |
|---|---|
| 应用 | `app_info` · `sync_status` |
| 记录类型 | `type_list` · `type_upsert` · `type_delete` |
| 字典/维度 | `taxonomy_list` · `taxonomy_upsert` · `taxonomy_delete` · `dimension_list` · `dimension_upsert` · `dimension_delete` |
| 足迹 | `footprint_list` · `footprint_get` · `footprint_upsert` · `footprint_delete` · `timeline` |
| 统计 | `stats_overview` · `metric_list` · `metric_upsert` · `metric_delete` |
| 成绩 | `grades_overview` · `semester_upsert` · `semester_delete` · `course_upsert` · `course_delete` · `band_upsert` · `band_delete` · `gpa_settings_get` · `gpa_settings_upsert` |
| 附件 | `attachment_add` · `attachment_list` · `attachment_delete` · `attachment_read` · `attachment_thumb` · `attachment_set_remote` · `attachment_set_thumb` |
| 档案 | `profile_get` · `profile_upsert` |
| 保存视图 | `view_list` · `view_upsert` · `view_delete` |
| 播种/同步内核 | `demo_seed` · `sync_apply_wire` · `sync_pull_wire` |
### `profile_upsert` 语义
顶层 key 合并:`info` / `resume` / `zongce` 是互相独立的子文档,只更所提交的 key,
其余原样保留(避免三张页面互相覆盖):
```json
{ "info": { "name": "...", "phone": "...", "eduStart": "2023-09" } }
```
### `footprint_list` 的 `query`
| 字段 | 说明 |
|---|---|
| `q` | 全文检索(≥3 字符走 FTS5 trigram 子串匹配,短词回退 LIKE) |
| `year` | 起始年份过滤 |
| `types` / `levels` / `roles` / `statuses` | 数组多选过滤 |
| `dims` | 维度多选 |
| `polarity` | `positive` / `negative` |
| `has_attachment` | 仅看有附件记录 |
| `tag` | 标签匹配 |
| `sort` | `date_desc`(默认)/ `date_asc` / `updated_desc` / `weight_desc` |
| `page` /`page_size` | 分页(`page_size` 上限 2000) |
### 分页约定
`page_size` 上限 2000;传非 `{query:{...}}` 形状的载荷会按默认 20 条处理(客户端请按此约定传参)。
示例(检索记录):
```bash
curl -s https://footpr.example.com/api/footprint_list \
-H 'content-type: application/json' -H "authorization: Bearer $TOKEN" \
-d '{"query":{"page":1,"page_size":20,"year":2025}}'
```