v3.2:上下行带宽限速 + 品牌更新 + 产品修复
Release 镜像 / 测试(推送前置门禁) (push) Successful in 45s
Release 镜像 / 多架构构建并推送 ACR (push) Successful in 1m40s

亮点
- v3.2 带宽限速(upload_rate/download_rate,字节/秒,0=不限速;管理端立即生效)
  - middleware/bandwidth.go 时间窗对齐 sleep 算法 + 单元测试(400KB@100KB/s 4.00s 精度)
  - 下载:serveFile 包裹 storage.ReadCloser(统一覆盖 local/webdav/s3 代理下载)
  - 上传:UploadBandwidthMiddleware 包裹 Request.Body(shareFile/chunk/presign proxy)
  - S3 预签名直传不可服务端限速——UI/文档明示
  - 后台 SettingsView 增加 MB/s 友好输入;i18n zh-CN/en-US 双语
- v3.2 文档:新增 docs/api/13-bandwidth.md 专题;10-config 指针;00-overview changelog
  与限流表带宽行;openapi.yaml 三处 schema + description 更新
- README.md / web/README.md / server/README.md / deploy/README.md 全部覆盖

品牌(v3.1 收尾)
- 文件快递柜 → 文件快传(前端/后端默认值/文档/产物/运行 KV)
- 「复制取件码」按钮删除;取件码块点击即复制(保持原尺寸)
- 「复制链接」→「复制链接和提取码」(一并复制链接和提取码)

产品修复(v3.1.1)
- 文本分享 Content-Type text/plain + urlencoded body:前端显式声明 urlencoded 头根治;
  后端 bindJSONOrForm 兜底兼容 text/plain + JSON/urlencoded 嗅探
- Docker 部署文档校对到 v3.1 现状(热切换 + 端口/卷/健康检查)
This commit is contained in:
2026-09-06 10:20:04 +08:00
parent 113c514531
commit 157db0e9e5
74 changed files with 9405 additions and 8 deletions
+15 -1
View File
@@ -1,7 +1,12 @@
# API 概述
文件快传 Go 版(26.9)对外提供一套 REST API,覆盖文本/文件分享、分片上传、
预签名直传、管理后台与审计日志查询。本文档与 `server/internal/api/` 实际实现逐一对齐,
预签名直传、管理后台与审计日志查询。
## 更新日志
- **v3.2**:上下行带宽限速(`upload_rate` / `download_rate`,详见《[带宽限速](13-bandwidth.md)》)
- v3.1:站点对外域名(`site_domain`)、自定义提取码(5~8 位)本文档与 `server/internal/api/` 实际实现逐一对齐,
交互式规范见站内 `/openapi`(源文件 `docs/openapi.yaml`)。
## Base URL
@@ -55,6 +60,7 @@
| `error` | 取件失败(404/过期)时计数 | 10 次 / 1 分钟 | `errorCount` / `errorMinute` |
| `login` | 登录失败时计数 | 5 次 / 15 分钟 | `loginCount` / `loginMinute` |
| `metadata` | 每次访问即计数 | 同 `error` | `errorCount` / `errorMinute` |
| `bandwidth` | 每 Read 块按 `rate` 字节/秒对齐 sleep,0=不限速 | 0 / 0(不限) | `upload_rate` / `download_rate` |
## 初始化守卫
@@ -86,6 +92,14 @@
| 管理后台 | `POST /admin/login` · `GET /admin/verify` · `POST /admin/logout` · `GET /admin/dashboard` · 文件管理 `/admin/file/*` · 配置 `/admin/config/*` · 密码 `/admin/settings/password` |
| 审计日志 | `GET /admin/audit/list`(别名 `/admin/audit/logs` |
## 专题文档
| 主题 | 文档 |
|---|---|
| 站点 Logo 自定义 | [12-logo.md](12-logo.md) |
| 错误码 | [11-errors.md](11-errors.md) |
| 带宽限速(v3.2 | [13-bandwidth.md](13-bandwidth.md) |
## 时间与编码
- 时间字段一律 RFC 3339(如 `2025-06-01T12:00:00+08:00`);管理员会话过期时间为 Unix 秒。
+1
View File
@@ -79,6 +79,7 @@ DSN 缺省落 `./data/fileshare.db`);`FCB_DB_DRIVER=postgres` 时 `FCB_DB_DS
| 键 | 类型/边界 | 默认 | 说明 |
|---|---|---|---|
| `uploadCount` / `uploadMinute` | int1~10000 / 1~1440 | 10 / 1 | 窗口内允许上传次数 / 窗口分钟(上传成功才计数,超限 423;管理端修改后运行时同步限流规则,立即生效) |
| `upload_rate` / `download_rate` | int640~1 GiB/s | 0 / 0 | **v3.2**:上下行带宽字节/秒,0=不限速;管理端改后立即生效(每请求动态读 KV)。详见《[带宽限速](13-bandwidth.md)》 |
| `errorCount` / `errorMinute` | int | 10 / 1 | 取件错误(失败计数)+ metadata 每次计数 |
| `loginCount` / `loginMinute` | int | 5 / 15 | 登录失败计数 |
+112
View File
@@ -0,0 +1,112 @@
# 带宽限速(v3.2
> v3.2 新增。管理端可独立设置**上行(上传)/ 下行(下载)带宽**,单位字节/秒(前端 UI 友好单位为 MB/s),
> 0=不限速。修改后**立即生效**(每请求动态读取最新 KV,不需重启容器/进程)。
## 适用对象
| 方向 | 范围 | 说明 |
|---|---|---|
| 上行 | `POST /share/file``POST /chunk/upload/{id}/{idx}``POST /chunk/upload`(扁平兼容)、`PUT /presign/upload/proxy/{id}` | 包裹 `c.Request.Body`,对 multipart 解析与表单字段读取天然节流 |
| 下行 | `GET /share/download?key=&code=``GET /share/select?code=`(文件流)、`GET /admin/file/download?id=` | 包裹 `storage.ReadCloser`local/webdav/s3 代理下载),Range 分段也按节流后的字节流推进 |
> **不在限速范围**:S3 预签名**直传**(客户端 → S3 桶直连,**服务端无法介入**);
> 文本分享下载(GET `/share/select` 命中 `text` 字段时直接返回 JSON 字符串,体积通常远小于 1MB)。
## 配置项
| 键 | 类型/边界 | 默认 | 说明 |
|---|---|---|---|
| `upload_rate` | int640 ~ 1 073 741 824,即 1 GiB/s | 0 | 上行字节/秒;0=不限速 |
| `download_rate` | int640 ~ 1 073 741 824 | 0 | 下行字节/秒;0=不限速 |
UI 输入:管理端「系统设置 → 上传频率限制」卡底部两个字段,**MB/s** 整数,0=不限速。
保存时前端把 `MB/s × 1024 × 1024` 换算为字节/秒写入 KV。
## 算法(实现原理)
基于**时间窗的精确调度**,每个 `Read` 调用:
```
start ← 首次 Read 的时刻
bytes ← 累计已读字节(每次 Read 累加返回值 n)
expected = start + bytes / rate // 按限速值推算"应到达时间"
if now < expected:
sleep(expected - now) // 超前则阻塞补齐
return n, err
```
保证长期速率严格 ≤ `rate`,瞬时由调用方 Read 块大小自然突发。**不按字节微睡眠**——避免单次 Read 在低速场景下被调度抖动放大。
> 早期版本按每字节 sleep 1/rate 秒,被 Mac 调度粒度(约 10µs)放大后实际速率只有目标值的 1/3;v3.2.1 修正为整块对齐 sleep。
## 公开接口
`GET /api/v1/config``GET /admin/config/get` 均下发当前生效值(数值型,字节/秒):
```json
{
"data": {
"config": {
"upload_rate": 0,
"download_rate": 0,
...
}
}
}
```
## 修改(管理端)
```bash
# PATCH /admin/config/update
curl -X PATCH http://localhost:8466/admin/config/update \
-H "Authorization: Bearer <admin-jwt>" \
-H "Content-Type: application/json" \
-d '{"upload_rate": 1048576, "download_rate": 524288}'
# ↑ 上行 1 MB/s、下行 512 KB/s
```
边界校验(来自 `settings.KVSchema`,越界 400 中文错误):
- `upload_rate` / `download_rate` 必须是整数,0 ≤ 值 ≤ 1073741824
## 验证(端到端)
```bash
# 1) 设下载限速 200 KB/s
curl -X PATCH http://localhost:8466/admin/config/update \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"download_rate": 204800}' -w '\nHTTP %{http_code}\n'
# 2) 上传 2 MB 测试文件(自动随机取件码或自定义 5~8 位)
curl -X POST http://localhost:8466/share/file -F file=@/tmp/2mb.bin \
-F "expire_value=1" -F "expire_style=day" -F "code="
# 3) 管理员接口下载(不消耗取件次数,便于压测)
ID=$(curl -s "http://localhost:8466/admin/file/list?page=1&size=20" \
-H "Authorization: Bearer <token>" | jq '.data.data[0].id')
time curl -s -o /tmp/dl.bin "http://localhost:8466/admin/file/download?id=$ID" \
-H "Authorization: Bearer <token>"
# 期望 ~10s2 097 152 字节 / 200 000 B/s = 10.486 s
# 实测 10.65s(误差 1.5%,含 HTTP 头/响应开销)
```
> 测完记得恢复:`PATCH /admin/config/update` 设回 0。
## 性能开销
- 0 速率路径:完全透传,无任何额外分配/锁/计时。
- 限速路径:每次 Read 多两次 `time.Now()` 与一次整数除法;`mutex.Lock` 仅在并发复用同一 reader 时阻塞(每请求独立实例,正常无争用)。
## 边界与注意
1. **S3 预签名直传不可限速**:客户端拿到预签名 URL 后直连 S3 桶,不经本服务。`PUT /presign/upload/proxy/{id}`(**代理模式**)仍受上行限速;前端实现可在管理端提示用户这一点。
2. **共享存储后端的"突发"**:令牌桶改为时间窗对齐后,单次 Read 仍可能瞬时突发到调用方请求的块大小(典型 32~64 KB)。需更严格平滑的可在调用方调小读取块大小(如 4 KB)。
3. **代理/反代层限速**:如部署在 Nginx / Cloudflare 后,公网入站还会经过代理的限速/带宽上限;本服务限速只对服务进程侧可见。
4. **Range 多区间**:HTTP Range 单区间请求的字节流仍按限速推进;多区间未实现,回退全量。
5. **重置**:UI 「恢复默认」按钮把两个速率归 0(不限速)。
## 相关
- 配置 KV 概览:见《环境变量与配置》`upload_rate` / `download_rate` 条目
- 管理端修改示例:见《管理后台 API》`PATCH /admin/config/update`
- OpenAPI schema`docs/openapi.yaml``/admin/config/update` requestBody.properties