# 轻湖文件快递柜 API 文档（取件码模式）

轻湖文件快递柜（LiteFileShare）是轻湖（Lite Lake）旗下的免登录文件分享产品：**上传一批文件 → 生成 6 位取件码 → 凭码下载**。文件保留 48 小时。

- English: [`/docs/api.en.md`](/docs/api.en.md)
- 网页版：`https://fileshare.litelake.com/`（上传 / 取件）
- 本文 Markdown 原文：[`/docs/api.md`](/docs/api.md)（AI Agent / 爬虫请直接抓取本地址）
- Skill 安装指引：[`/docs/skill.md`](/docs/skill.md)（免手写 HTTP，一条命令上传/下载）

## Base URL

**`https://fileshare.litelake.com`**

即当前部署的唯一公共入口（BFF），本页所有示例均基于它。本服务**免认证**（`/api/public` 分区），无需 API Key 与登录。二进制文件流量（上传/下载）走 SC 对象存储 presigned 直链，**不经本服务**；本服务只签发直链。

## 通用约定

- 除直链 PUT/GET 外，所有接口均为 `POST` + `application/json`。
- 响应统一 `APIResponse`：`{"code": 200, "message": "ok", "data": {...}}`。
- **HTTP 状态码策略**：成功与业务错误（400/4101/4102/4103）HTTP 恒为 200，靠 `body.code` 区分；**429 与 503 用真实 HTTP 状态码**（429 附 `Retry-After` 头）。
- 建议所有请求携带 `X-Device-Id` 请求头（UUID 格式，同一客户端固定复用）：错码失败限流按 IP + 设备号双维度计数，携带设备号可避免同 IP 多客户端互相误伤。
- 时间字段均为 RFC3339 UTC（如 `2026-09-12T04:00:00Z`）。

### 限流总表（按 IP）

| 接口 | 限额 |
|---|---|
| `POST /api/public/shares/init` | 12 次/小时 + 30 次/天（另受公开写基线 30 次/分钟约束） |
| `POST /api/public/shares/refresh` | 30 次/分钟 |
| `POST /api/public/shares/complete` | 30 次/分钟（公开写基线） |
| `POST /api/public/pickup/verify` | 30 次/分钟；错码失败 5 次/10 分钟（IP + 设备号双维度） |
| `POST /api/public/pickup/download` | 60 次/分钟；与 verify 共享错码失败计数 |

### 错误码总表

| body.code | 语义 | HTTP |
|---|---|---|
| 200 | 成功 | 200 |
| 400 | 参数错误/超限（message 说明原因） | 200 |
| 4101 | 取件码无效或已过期（统一文案防枚举） | 200 |
| 4102 | 文件缺失（complete 时部分文件未传成功，data 携带 `missingFileIds`） | 200 |
| 4103 | 上传会话已截止（init 超 24h 未 complete） | 200 |
| 429 | 触发限流（HTTP 429 + `Retry-After` 秒数） | 429 |
| 503 | 存储（SC/Redis）暂不可用，fail-closed | 503 |

### 约束上限（默认值）

| 项 | 上限 |
|---|---|
| 单批文件数 | 20 个 |
| 单文件大小 | 256 MB |
| 单批总量 | 2 GB |
| 上传直链（PUT）有效期 | 60 分钟（过期 403 后调 refresh 重签） |
| 下载直链（GET）有效期 | 5 分钟 |
| complete 截止 | init 后 24 小时内 |
| 文件保留时长 | complete 后 48 小时 |

## 上传流程（3 步）

```text
init（拿直链） → 逐文件 PUT 直传 SC → complete（换取件码）
```

### 第 1 步：POST /api/public/shares/init — 创建上传会话

```bash
curl -sS -X POST https://fileshare.litelake.com/api/public/shares/init \
  -H 'Content-Type: application/json' \
  -H "X-Device-Id: $(cat ~/.cache/litelake/fileshare/device_id 2>/dev/null || uuidgen)" \
  -d '{
    "files": [
      {"filename": "报告.pdf", "size": 1048576, "contentType": "application/pdf"},
      {"filename": "data.csv", "size": 2048, "contentType": "text/csv"}
    ]
  }'
```

响应（`data`）：

```json
{
  "shareId": "b1c2d3e4f5a6",
  "completeDeadline": "2026-09-13T04:00:00Z",
  "files": [
    {"fileId": "f1e2d3c4b5a7", "uploadUrl": "https://sc.../20260912/b1c2.../f1e2...?X-Amz-..."},
    {"fileId": "a9b8c7d6e5f4", "uploadUrl": "https://sc.../20260912/b1c2.../a9b8...?X-Amz-..."}
  ]
}
```

| 字段 | 说明 |
|---|---|
| `shareId` | 上传会话 ID（complete 时回传） |
| `completeDeadline` | complete 硬截止（init + 24h），超时 4103 |
| `files[].fileId` | 文件 ID（refresh/complete 缺失清单用） |
| `files[].uploadUrl` | presigned PUT 直链，60 分钟有效 |

### 第 2 步：PUT uploadUrl — 逐文件直传 SC

对每个文件，向对应 `uploadUrl` 发 **PUT**（body 为文件原始字节），**不要带多余的自定义头**（签名只覆盖路径与查询串）：

```bash
curl -sS -X PUT "<uploadUrl>" \
  -H 'Content-Type: application/pdf' \
  --data-binary @报告.pdf
```

- 返回 200 即成功；返回 **403** 说明直链过期（超 60 分钟），走第 2.5 步重签。
- Python 大文件请流式上传（参考 skill 的 `http.client` 实现），避免整体读入内存。

### 第 2.5 步（按需）：POST /api/public/shares/refresh — 重签过期直链

PUT 返回 403 时调用，幂等：

```bash
curl -sS -X POST https://fileshare.litelake.com/api/public/shares/refresh \
  -H 'Content-Type: application/json' \
  -d '{"shareId": "b1c2d3e4f5a6", "fileIds": ["f1e2d3c4b5a7"]}'
```

响应 `data`：`{"files": [{"fileId": "...", "uploadUrl": "..."}]}`，用新 `uploadUrl` 重传。

### 第 3 步：POST /api/public/shares/complete — 完成，换取件码

全部 PUT 成功后调用：

```bash
curl -sS -X POST https://fileshare.litelake.com/api/public/shares/complete \
  -H 'Content-Type: application/json' \
  -d '{"shareId": "b1c2d3e4f5a6"}'
```

响应（`data`）：

```json
{
  "pickupCode": "K7M2XQ",
  "expiresAt": "2026-09-14T04:00:00Z",
  "fileCount": 2,
  "totalSize": 1050624
}
```

- `code=4102`：`data.missingFileIds` 列出未传成功的文件，重传后再次 complete。
- `code=4103`：会话已截止，从 init 重新开始。
- 取件码 6 位，字符集 `0-9A-Z`（去 I/L/O/U）；过期时间 = complete + 48h。

## 下载流程（2 步）

```text
verify（验码 + 列文件） → 逐文件 download（拿直链 GET）
```

### POST /api/public/pickup/verify — 凭码取文件列表

```bash
curl -sS -X POST https://fileshare.litelake.com/api/public/pickup/verify \
  -H 'Content-Type: application/json' \
  -H "X-Device-Id: $DEVICE_ID" \
  -d '{"code": "K7M2XQ"}'
```

取件码**容错归一化**：整段粘贴即可——服务端自动去空格/连字符、转大写、`O→0`、`I/L→1`。

响应（`data`）：

```json
{
  "expiresAt": "2026-09-14T04:00:00Z",
  "files": [
    {"fileId": "f1e2d3c4b5a7", "filename": "报告.pdf", "size": 1048576},
    {"fileId": "a9b8c7d6e5f4", "filename": "data.csv", "size": 2048}
  ]
}
```

`code=4101` 表示取件码无效或已过期（不区分两种情况，防枚举）。

### POST /api/public/pickup/download — 签发下载直链

```bash
curl -sS -X POST https://fileshare.litelake.com/api/public/pickup/download \
  -H 'Content-Type: application/json' \
  -H "X-Device-Id: $DEVICE_ID" \
  -d '{"code": "K7M2XQ", "fileId": "f1e2d3c4b5a7"}'
```

响应（`data`）：`{"url": "https://sc...?...response-content-disposition=...", "filename": "报告.pdf"}`

直链 5 分钟有效，响应带 `Content-Disposition: attachment`（中文文件名 RFC2231 编码），直接 GET 即下载：

```bash
curl -sS -o "报告.pdf" "<url>"
```

## 使用注意

- init/verify 是高频滥用点，限流最严：脚本批量场景请**合并文件批量上传**（一次 init 最多 20 个文件），不要逐文件调用。
- 错码连续失败 5 次会封 10 分钟（IP + 设备号双维度）；不要用脚本爆破取件码。
- 429 时读取 `Retry-After` 头等待后重试，不要立即重试。
- 过期文件（48h）与超时未 complete 的会话（24h）由服务端定时清理，无需主动删除。
