• v0.7.3 74332e20d8

    zijunchiang released this 2026-09-17 03:39:36 +08:00 | 7 commits to main since this release

    OpenDance Studio -- Web 管理服务端 v0.7.3

    两个真实缺陷修复(均在生产部署上复现并定位)。

    契约无变更(仍为 rest-api 0.5.0 / websocket-keypoints 0.2.0 / data-models 0.7.0 /
    skeleton-topology 0.2.0 / model-formats 0.5.0 / database 0.4.0)。

    一、场景根本传不上来:diskStorage 却按内存存储判断

    现象:POST /api/scenes 一律 400「上传场景必须带 file 字段(.glb 为主…)」,
    而同一份 multipart 请求打 /api/models 返回 201(已用 curl 对照验证)。

    根因:上传中间件在 storage.module.ts 里用的是 multer 的 diskStorage
    (先落 ${STORAGE_DIR}/tmp,避免 512 MiB 级 VRM 打爆进程内存),落盘后的文件对象
    只有 path、没有 buffer;而 ScenesService.create() 当初按内存存储写:

    if (!file || !file.buffer || file.buffer.length === 0) { /* 400 */ }
    

    file.buffer 恒为 undefined → 任何场景都传不上来。

    修复:与 models / motions 统一到同一套上传处理:

    • requireUploadedFile(file) —— 校验确实落盘,并修正 busboy 按 latin1 解出的中文/日文文件名;
    • 直接导入(.glb/.gltf)→ storage.persistTempFile(AssetKind.Scene, upload.path, …)
      —— 归档并顺带清理临时文件;
    • 需转换(.zip/.pmx/.fbx)→ readFile(upload.path) 取 buffer 后转换 → persistBuffer,
      并在 finally 中 unlink(upload.path);
    • 体积上限用 upload.size 判断(UPLOAD_MAX_BYTES),超限 400 且不落盘。

    全局排查过同类写法:除注释外,代码里已无按 file.buffer 处理上传的地方。

    二、场景表不可用会拖垮整站

    现象(生产):数据库缺 scenes 表,于是

    请求 结果
    /api/songs、/api/motions、/api/models 200
    /api/scenes、/api/scenes/:id 500
    /api/catalog/export、/api/catalog/version 500 ← 客户端拉不到任何目录
    /api/reviews?status=pending 500 ← 管理端待审列表打不开

    而 /health 显示 database.connected true、migrationsPending 0 —— 因为
    DataSource.showMigrations() 只比较代码里加载到的迁移;部署的 dist/migrations/
    若缺 AddScenes.js,它根本不在比较集合里,于是"没有待执行迁移"但表并不存在。

    契约与文档都写明场景是可选资源(没有可用场景时客户端回退内置空场景),
    因此改为降级而不是 500:

    • ScenesService.queryScenes():数据库层面失败 → 返回空结果、只记一次日志、
      原因写入 diagnostics();下一次查询成功即自动复位;
    • ReviewsService.listByStatus():场景查询单独 catch,待审列表照常返回其它资源;
    • /health 新增 scenes: { available, error } —— 降级但绝不静默。

    三、新增诊断脚本 web-server/scripts/diagnose-scenes.mjs(默认只读)

    打印运行期真实连的库、表清单、typeorm_migrations 记录、代码实际加载的迁移、
    scenes 实际列 vs 实体期望列的逐列 diff;--fix 仅在缺表时用迁移里同一份 DDL
    幂等补表。

    cd <APP_DIR>/web-server
    set -a; . ../deploy/.env; set +a
    node scripts/diagnose-scenes.mjs            # 只读诊断
    node scripts/diagnose-scenes.mjs --fix      # 仅在缺表时补表(幂等)
    

    四、顺带修正

    • models.controller.ts 的误导性注释:模型上传不接受 isPrimary/status
      (契约:上传一律 pending,默认模型由审核通过时带 setPrimary=true 或
      POST /api/models/{id}/primary 设定);
    • test/smoke.mjs 此前按错误预期断言「isPrimary 已置位」,已改为契约流程
      (上传断言 pending + isPrimary=false → 审核带 setPrimary=true → 断言 isPrimary=true);
      另给目录导出/指纹等检查补上失败细节(HTTP 状态 + 响应体),并把待审列表查询的
      pageSize 放大到 200(避免库里待审项多时新条目被挤出第一页);
    • deploy/proxy/README.md 第九节更正一次误判:Gitea 附件接口本身是好的 ——
      它的 multipart 字段名是 attachment,用 file= 会得到
      HTTP 500 {"message":"","url":"…/api/swagger"}(空消息,极易误判成"服务器磁盘写满")。
      本节同时记录了两种正确的上传写法与复验命令。

    ⚠️ 仍然存在、需要运维处理的唯一上传限制:IIS 的 maxAllowedContentLength
    还是默认的 30,000,000 字节
    ,37 MB 的 Windows 整包上传仍返回 HTTP 413。
    请按 deploy/proxy/README.md 9.2 放开到 256 MB。

    校验

    • npm run verify 通过:contracts:check(六项一致 + 35 条路由一致 + 版本一致)、
      tsc --noEmit、22 文件 / 281 项测试、nest build、启动冒烟;
    • 新增 test/scene-upload.spec.ts(5 项,修复前必失败)与
      test/scenes-degradation.spec.ts(6 项);
    • 发布产物解压后 npm ci --omit=dev + node dist/main.js 实测打印
      应用版本 v0.7.3(与 /health、管理端左下角同源)。

    部署与部署后自检

    curl -fLO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.7.3/opendance-web-v0.7.3.tar.gz
    curl -fLO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.7.3/SHA256SUMS
    sha256sum -c SHA256SUMS
    tar -xzf opendance-web-v0.7.3.tar.gz && cd opendance-web-v0.7.3
    sudo bash deploy/install.sh
    
    # 部署后必须「可核对」:
    curl -s http://<服务器地址>:3000/health | grep -o '"version":"[^"]*"'   # 必须输出 0.7.3
    # 场景子系统可用性(降级时也会如实显示原因,而不是把整站带崩):
    curl -s http://<服务器地址>:3000/health | python3 -m json.tool | grep -A3 '"scenes"'
    
    # 若 scenes 不可用,先诊断再决定是否补表:
    cd <APP_DIR>/web-server && set -a && . ../deploy/.env && set +a
    node scripts/diagnose-scenes.mjs
    

    数据库端到端验收(歌曲 → 动作 → 预处理 → 模型 → 审核 → 场景 → 目录导出):

    API_BASE=http://<服务器地址>:3000 node web-server/test/smoke.mjs
    

    <服务器地址> 是你们自己部署的服务端地址(部署方决定),不是被脱敏的域名。

    交付物校验和(可核对)

    5d0f90ec6a1b58ad0beec92928c99467bc159c2720e3b058da7e588fdddda519  opendance-web-v0.7.3.tar.gz
    

    下载后:

    curl -fLO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.7.3/SHA256SUMS
    curl -fLO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.7.3/opendance-web-v0.7.3.tar.gz
    sha256sum -c SHA256SUMS     # 期望 OK
    
    Downloads