diff --git a/README.md b/README.md index 8ea051f..b096a54 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,14 @@ v0.7.3 修订(**场景表不可用不再拖垮整站** —— 生产事故修复 `setPrimary=true` 或 `POST /api/models/:id/primary` 设定) —— 冒烟脚本此前正是 按错误预期断言,已同步修正; 5. 新增 6 项测试(`test/scenes-degradation.spec.ts`)锁定降级行为。 +6. **更正一次误判**:排查过程中曾把 Gitea 附件接口的 `HTTP 500 {"message":"","url":"…/api/swagger"}` + (空消息)判成"宿主机磁盘写满"。**实际是上传写法错误** —— 该接口 multipart 字段名是 + **`attachment`**,用 `file=` 就会得到这个空消息 500。实测对照:同一 1 KB 文件, + `-F "attachment=@…"` → **201**,`--data-binary @…` + `?name=` → **201**, + `-F "file=@…"` → **500**。两种正确写法已写进 `deploy/proxy/README.md` 第九节。 + **仍然真实存在的唯一限制是 IIS `maxAllowedContentLength`(默认 30,000,000 字节)**: + 927 KB / 14 MB / 16.4 MB 通过,30.5 MB / 38.2 MB / 40 MB 一律 HTTP 413。 + v0.7.2 修订(**修复产物"装得上、起不来"+ `/health` 报错版本号**): diff --git a/README.txt b/README.txt index 8130cc6..c1eff9c 100644 --- a/README.txt +++ b/README.txt @@ -28,6 +28,13 @@ v0.7.3 修订 - 场景表不可用不再拖垮整站(生产事故修复) pending, 默认模型由审核通过时 setPrimary=true 或 /primary 设定), 冒烟脚本的错误预期已同步修正。 5. 新增 6 项测试。 + 6. 更正一次误判: Gitea 附件接口的 HTTP 500 空消息("message":"")曾被我判成宿主机 + 磁盘写满, 实际是**上传写法错误** -- 该接口 multipart 字段名是 attachment, + 用 file= 就会得到空消息 500。正确写法两种(attachment=@... 或 + --data-binary + ?name=)已写进 deploy/proxy/README.md 第九节。 + 仍然真实存在的唯一限制是 IIS maxAllowedContentLength(默认 30,000,000 字节): + 927KB/14MB/16.4MB 通过, 30.5MB/38.2MB/40MB 一律 413。 + v0.7.2 修订 - 修复产物"装得上、起不来" + /health 报错版本号 1. CatalogModule 漏 import ScenesModule(致命): CatalogService 注入 diff --git a/deploy/proxy/README.md b/deploy/proxy/README.md index ab57ed5..87afac1 100644 --- a/deploy/proxy/README.md +++ b/deploy/proxy/README.md @@ -237,53 +237,66 @@ firewalld / ufw / iptables 的放行情况,并直接给出修复命令。 > **Gitea**(源码仓库 + Release 附件)的站点。Windows 客户端的发布整包走 Gitea, > 因此**必须**单独放开 Gitea 站点的上限。 -### 9.1 实测结论(IIS 放开之后:问题在 Gitea 侧,且与体积无关) +### 9.1 实测结论(含一次**误判更正**,请以本节为准) -**第一阶段(IIS 上限未放开时)** +#### A. IIS 的 `maxAllowedContentLength` 是**真实且当前仍在生效**的上限 | 上传体积 | 结果 | |--|--| -| 14 MB | 通过(HTTP 201) | +| 927 KB / 14 MB / 16,372,427 B | 通过(HTTP 201) | | 30.5 MB | **失败 HTTP 413**(IIS「请求实体过大」页面) | +| 38,245,602 B(Windows 整包) | **失败 HTTP 413** | | 40 MB | **失败 HTTP 413** | -**第二阶段(IIS 上限放开之后)** —— 放开的效果**可验证**: -38,245,661 字节的请求体被**完整接收**(耗时 115 s),不再出现 413, -说明 IIS 的 `maxAllowedContentLength` 已经够用。但随后 **Gitea 自己返回 HTTP 500**, -响应体是 Gitea 的 API 错误信封 `{"message":"","url":".../api/swagger"}`(`message` 为空) -——瓶颈已经不在 IIS,而在 Gitea 侧。 +→ IIS 的默认值 **`maxAllowedContentLength = 30,000,000` 字节(≈28.6 MiB)** 没有被放开, +这就是 Windows 端整包(约 37 MB)至今还要拆分卷/无法上传的**唯一真实原因**。 +(`exe` 约 104 MB 未压缩 + `pck`;`deploy/proxy` 之外的另一台站点,见本节开头说明。) -随后做**与体积无关**的隔离测试(同一个 Release 资产上传接口): +**放开办法**:见 9.2 的四条配置(IIS `requestLimits` + `uploadReadAheadSize`、 +ARR `maxRequestBytes`、Gitea `app.ini` 的 `[attachment] MAX_SIZE`、前置 nginx 的 +`client_max_body_size`),改完 `iisreset /noforce`。 -| 上传体积 | 结果 | +#### B. 【更正】Gitea 附件接口是好的 —— 之前"HTTP 500 / 磁盘写满"是**请求写法错误** + +排查过程中我一度判定"Gitea 附件通道坏了、宿主机磁盘写满",**这个结论是错的**,特此更正, +并把踩坑点记录下来: + +Gitea `POST /repos/{owner}/{repo}/releases/{id}/assets` 的 multipart 字段名是 +**`attachment`**(见 `/api/v1/swagger`),不是 `file`。用 `-F "file=@…"` 提交时 +**Gitea 返回 HTTP 500,且响应体是空消息**: + +```json +{"message":"","url":"https:///api/swagger"} +``` + +这个空消息极像"服务器内部错误 / 磁盘写满",实际只是"没有找到名为 `attachment` 的表单字段"。 +实测对照(同一个 1 KB 文件、同一个 Release): + +| 写法 | 结果 | |--|--| -| 914 KB / 927 KB(web v0.7.1 / v0.7.2 tar.gz) | 通过 | -| 16,372,427 B(android v0.2.0 zip) | 通过 | -| 37 MB(windows 整包) | **HTTP 500** | -| 24 MB / 21 MB / 18 MB / 16.5 MB / 16.4 MB | **HTTP 500** | -| **100 KB** | **HTTP 500,0.3 秒** | -| **1 KB** | **HTTP 500,0.14 秒** | +| `-F "attachment=@probe.bin"` | **201** | +| `--data-binary @probe.bin` + `-H "Content-Type: application/octet-stream"`(URL 带 `?name=`) | **201** | +| `-F "file=@probe.bin"` | **500**(空消息) | -关键结论: +**正确的两种上传写法**(都可直接照抄): -1. **与体积无关**:1 KB 也失败,且是**瞬间**失败(不是读完请求体才判断), - 所以**不是** `[attachment] MAX_SIZE` 这类"体积上限"(那样会先读完请求体); -2. **与仓库无关**:`opendance-web` / `opendance-windows` / `opendance-android` - 三个 Release 的资产上传接口**同样**失败; -3. **写入通道的其他部分正常**:创建 Release、PATCH Release 说明(纯 JSON)都成功 - —— 只有**附件文件**的上传/落盘这一条路坏掉; -4. **间歇性**:全部失败期间仍有 927 KB 的附件上传成功过一次。 +```bash +# 写法 1:multipart,字段名必须是 attachment +curl -f -X POST \ + -H "Authorization: token " \ + -F "attachment=@opendance-windows-vX.Y.Z.zip" \ + "https://gitea.kasugano.cn/api/v1/repos/zijunchiang/opendance-windows/releases//assets?name=opendance-windows-vX.Y.Z.zip" -> **最可能的原因:Gitea 宿主机的磁盘(或上传临时目录所在的分区/tmpfs)几乎写满。** -> Gitea 会先把请求体写入数据目录下的临时目录,再落库/改名;空间不足时 -> 表现为 Gitea 返回 500 而**不是** 413。 -> -> **运维请按顺序检查**: -> 1. Gitea 宿主机 `df -h`(数据盘、`/tmp`、以及 `data/tmp` 是否落在小容量 tmpfs 上); -> 2. Gitea 日志(`log/error.log` 或 journal)里这次 500 的真实错误 -> (大概率是 `no space left on device`); -> 3. 清理失败上传留下的临时文件与旧附件,释放空间; -> 4. 再按 9.2 把 `[attachment] MAX_SIZE` 设为 **256**,重启 Gitea。 +# 写法 2:裸 body + ?name=(大文件更省内存) +curl -f -X POST \ + -H "Authorization: token " \ + -H "Content-Type: application/octet-stream" \ + --data-binary @SHA256SUMS \ + "https://gitea.kasugano.cn/api/v1/repos/zijunchiang/opendance-web/releases//assets?name=SHA256SUMS" +``` + +> 另:`[attachment] MAX_SIZE` 仍然是需要放开的一项(默认可能只有 4~32 MB), +> 但它超限时返回的是**带消息**的错误,而不是上面那种空消息 500。 ### 9.2 正确做法:放开 Gitea 站点上限到 256 MB @@ -342,20 +355,20 @@ firewalld / ufw / iptables 的放行情况,并直接给出修复命令。 > 说明请求体已经到达 Gitea —— 此时按 9.1 的结论排查 **Gitea 宿主机磁盘空间** > (1 KB 也失败就说明与体积无关),然后再看 `[attachment] MAX_SIZE`。 -### 9.3 复验方法 +### 9.3 复验方法(用**正确**的字段名,否则会得到误导性的空消息 500) ```bash -# 造一个 40 MB 文件上传到 Gitea 的附件接口,预期 HTTP 201(不再是 413) +# 造一个 40 MB 文件,直接打 Gitea 的附件接口 head -c 40000000 /dev/urandom > /tmp/probe.bin curl -i -X POST \ -H "Authorization: token " \ - -F "file=@/tmp/probe.bin" \ + -F "attachment=@/tmp/probe.bin" \ "https://gitea.kasugano.cn/api/v1/repos/zijunchiang//releases//assets?name=probe.bin" ``` -在放开上限之前,**不要**用分卷绕过:Windows 端 `build/package-release.sh` -会拒绝生成 `.partN`,超限时直接告警并要求先放宽本节的配置。 +预期: -另外提醒:本节第 1 条(IIS)已经**确认放开生效**(37 MB 请求体不再 413)。 -如果此时仍然上传失败,请直接跳到 9.1 的第 4 条 —— 问题在 Gitea 宿主机侧 -(磁盘/临时目录),而不是 IIS 配置。 +* 放开 IIS 前 → **HTTP 413**(IIS 的"请求实体过大") +* 放开 IIS 后 → **HTTP 201**(返回资产 JSON) +* 若得到 **HTTP 500 + `{"message":"","url":"…/api/swagger"}`** → 十有八九是**字段名写错** + (用了 `file` 而不是 `attachment`),不是服务器故障