• v0.7.5 12041f2e58

    zijunchiang released this 2026-09-18 03:03:50 +08:00 | 1 commits to main since this release

    OpenDance Studio Web 服务端 v0.7.5

    两个真实缺陷修复(都由跨端边界验收暴露)+ 跨端边界验收新增两类场景。

    契约快照不变: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
    (与 opendance-windows / opendance-android 逐字节一致)。

    一、上传落盘:去重命中时删掉了唯一副本

    persistTempFile(动作 / 模型 / 场景上传走的通路)在 sha256 去重命中时
    直接删掉刚上传的临时文件并返回旧记录。若旧记录指向的路径已不存在
    (换过 STORAGE_DIR、只恢复了数据库、手工删过文件),上传仍返回 201,
    但后续全部失败:

    ERROR [PreprocessService] 动作 #7 预处理失败:ENOENT ... /storage/motion/2026/09/a8/<sha>.vmd
    

    实测就是在本机的验收环境上撞到的(换过存储目录后动作预处理直接 500)。
    persistBuffer(内存通路:关键点序列 / 缩略图 / 转换产物)早已有「文件缺失则按
    原路径补写」的兜底,这条通路漏了。

    修复:去重命中时先确认文件存在;缺失则按记录里的原路径 rename
    (跨设备退化为 copyFile)补写,只有确认目标存在才删临时文件。

    新增 test/storage-persist.spec.ts(5 项)。反向验证:临时还原修复后,
    「去重命中但文件已丢失」这条立刻失败;恢复后 5 项全过。

    二、不可用模型的原因归因不准确

    unusableReason() 原来只要 stats 里有 humanoid 数字就归因于 humanoid,于是
    一个直接上传的 .glb(骨架完全合规、drivable = true、只是没有 VRM 容器)
    会被写成「humanoid 不达标(19/22)」—— 审核界面据此会把修复方向引到
    「重新绑定骨骼」,而实际要做的是「以 VRM 交付」。

    用真实素材实测(PMX → Assimp → GLB,886 KB):

    原因文案
    修复前 humanoid 不达标(19/22),未产出 VRM
    修复后 未产出 VRM(格式为 GLB;humanoid 覆盖 19/22 已达标),需重新以 VRM 交付才能进入客户端目录

    现在按 drivable 区分:不满足 16 根必需骨才归因 humanoid;达标但没有 VRM 容器
    则明确说明"已达标"。新增 2 项单测(model-usability 5 → 7 项)。

    三、跨端边界验收(Windows 客户端 × 本服务端)

    accept-integration.sh 新增两类场景,两端都断言:

    1. 不可用模型:真实 PMX 用 Assimp 转 GLB 后入库(等价"管线只产出 GLB")→
      usable=false、原因准确、不进入 catalog/export、defaultModelId 不受影响;
      包内客户端的 bundle.models 里没有任何非 VRM 模型;
    2. 场景 config 只给最少字段({physics:{gravity:{y:-9.8}}})→ 服务端补齐默认
      (碰撞体 auto、地面 0.6 / 45°、静风、相机 follow、环境光 1、出生点 1 条),
      客户端解析后逐项一致。

    四、发布前自检(本版实测)

    $ npm run verify
      ✓ 契约 6 项 / 35 条路由 / 版本一致(0.7.5)
      ✓ 27 个文件 309 项测试(原 26 / 304)
      ✓ 产物级实体注册核对(dist/entities 6 项含 Scene)/启动冒烟
    $ bash web-server/scripts/accept-release.sh <tar.gz>      # 对发布包本身的验收
      ✓ /health v0.7.5 / scenes.available=true / 包内 smoke 全过
    $ 跨端集成验收(Windows v0.2.5 包内客户端 × 本服务端)
      ✓ 目录四类资源 / 场景物理参数逐项 / 关键点 2901 帧 / 不可用模型不进目录 / 最简配置补默认
    

    包内全部 356 个文件已逐字节核对:179 个源码/文档与 tag v0.7.5 一致,
    177 个构建产物与工作树构建一致(0 处不一致 / 0 处多余 / 0 处缺失)。

    五、部署提示

    • 本次不涉及表结构变更(迁移仍为 6 个),可直接替换程序文件后重启;
    • 如果你换过 STORAGE_DIR 或只恢复过数据库:v0.7.5 起,命中去重时会自动按记录中的
      路径把文件补写回去;此前的版本会出现「上传成功但预处理/下载 500」。
    Downloads
  • v0.7.4 d1c0dd6e7a

    zijunchiang released this 2026-09-17 03:52:34 +08:00 | 5 commits to main since this release

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

    场景功能的真正根因修复:Scene 实体没有被注册进 ENTITIES
    (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)。

    根因:No metadata for "Scene" was found.

    部署 v0.7.3 后,/health 的 scenes.error 给出了决定性线索:

    "scenes": { "available": false, "error": "No metadata for \"Scene\" was found." }
    

    src/entities/index.ts 一直是:

    export const ENTITIES = [Asset, Song, Motion, CharacterModel, ReviewRecord];  // ← 少了 Scene
    

    TypeORM 的 DataSource 只按 ENTITIES 注册 metadata。漏注册的后果非常隐蔽:

    环节 表现
    启动 完全正常(DI 图可解析、/health 正常、migrationsPending 0)
    迁移 正常 —— AddScenes 已执行,scenes 表其实是建好的
    第一次查该实体 抛 No metadata for "Scene" was found.
    连带影响 /api/scenes、/api/catalog/export、/api/catalog/version、/api/reviews?status=pending 全部 500 → 客户端拉不到任何目录、管理端待审列表打不开

    也就是说:此前"数据库缺 scenes 表"的判断是错的(那是我的误判)。
    v0.7.3 的降级修复与诊断脚本因此仍然必要 —— 它们把"场景坏了"限制成"场景不可用",
    而不是让整站不可用(部署 v0.7.3 后 catalog/export 与待审列表已恢复 200)。

    修复

    ENTITIES 加入 Scene 并导出。

    新增静态防线:test/entities-registry.spec.ts(4 项)

    • 扫描 src/entities/*.entity.ts 里所有 @Entity() 类,逐一与 ENTITIES 对账
      (漏一个即失败;已做反向验证:把 Scene 从 ENTITIES 移除,测试立刻失败);
    • 检查 ENTITIES 无重复、无多余项;
    • 核对 AddScenes 迁移文件确实存在(迁移文件缺失时 showMigrations() 仍会显示
      "0 个待执行",这是另一个隐蔽的静默不一致)。

    这类"漏注册"既过得了 tsc,也过得了启动冒烟(应用能启动),只能靠静态核对兜住。

    校验

    • npm run verify 通过:contracts:check(六项一致 + 35 条路由一致 + 版本一致)、
      tsc --noEmit、23 文件 / 285 项测试、nest build、启动冒烟;
    • 发布产物解压后 npm ci --omit=dev + node dist/main.js 实测打印「应用版本 v0.7.4」。

    部署与部署后自检

    curl -fLO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.7.4/opendance-web-v0.7.4.tar.gz
    curl -fLO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.7.4/SHA256SUMS
    sha256sum -c SHA256SUMS
    tar -xzf opendance-web-v0.7.4.tar.gz && cd opendance-web-v0.7.4
    sudo bash deploy/install.sh
    
    # 1) 版本可核对
    curl -s http://<服务器地址>:3000/health | grep -o '"version":"[^"]*"'        # 期望 0.7.4
    # 2) 场景子系统应恢复可用(不再有 No metadata)
    curl -s http://<服务器地址>:3000/health | python3 -m json.tool | grep -A3 '"scenes"'   # 期望 available: true
    # 3) 场景接口不再 500
    curl -s -o /dev/null -w '%{http_code}\n' http://<服务器地址>:3000/api/scenes            # 期望 200
    # 4) 表/列实际状态(默认只读)
    cd <APP_DIR>/web-server && set -a && . ../deploy/.env && set +a
    node scripts/diagnose-scenes.mjs
    
    # 5) 数据库端到端验收(歌曲 → 动作 → 预处理 → 模型 → 审核 → 场景 → 目录导出)
    API_BASE=http://<服务器地址>:3000 node web-server/test/smoke.mjs
    

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

    交付物校验和(可核对)

    e03e28f3ab483291c4b030a428ace9100bddcd1ab5d40010ab29a4a9500d1987  opendance-web-v0.7.4.tar.gz
    

    下载后:

    curl -fLO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.7.4/SHA256SUMS
    curl -fLO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.7.4/opendance-web-v0.7.4.tar.gz
    sha256sum -c SHA256SUMS     # 期望 OK
    
    Downloads
  • 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
  • v0.7.2 2433cd22a5

    zijunchiang released this 2026-09-17 01:49:53 +08:00 | 13 commits to main since this release

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

    ⚠️ 请务必升级到本版:v0.7.0 / v0.7.1 的产物启动不起来(详见第一条)。
    本版同时修正 /health 在正式部署下报错版本号的问题。

    本版不含契约变更(仍为 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)与接口行为变更。

    一、【致命】CatalogModule 漏 import ScenesModule:产物「装得上、起不来」

    CatalogService 因为 catalog/export 要下发 scenes[] 而注入了 ScenesService,
    但 CatalogModule 的 imports 里只列了 SongsModule / MotionsModule / ModelsModule。
    Nest 的 DI 是按模块解析的,于是:

    UnknownDependenciesException: Nest can't resolve dependencies of the CatalogService
    (SongRepository, MotionRepository, CharacterModelRepository, SongsService, MotionsService,
     ModelsService, ?, AppConfig). Please make sure that the argument ScenesService at index [6]
     is available in the CatalogModule module.
    

    node dist/main.js 在 bootstrap 阶段就退出,服务完全起不来。

    为什么之前三层检查全绿却没发现:

    检查 为什么没抓到
    nest build / tsc --noEmit 模块 import 缺失是运行时 DI 问题,类型系统看不出来
    258 项单元测试 直接 new 服务或测纯函数,从不编译 AppModule
    打包脚本的产物完整性校验 只校验文件清单与压缩包结构,不启动服务

    修复:CatalogModule.imports 补上 ScenesModule。

    新增防线(同时挂进 npm run verify 与打包脚本):

    • scripts/check-bootstrap.mjs:以正式部署方式(ExecStart=node dist/main.js、
      不带任何 npm 环境变量、不需要数据库)拉起真实产物,断言
      ①日志里没有依赖解析类错误②启动日志里的应用版本等于 package.json
      ③启动确实推进到数据库连接阶段。
      已做反向验证:把 ScenesModule 删除后该检查立刻失败。

    二、/health 的 version 在正式部署下永远是 0.1.0

    原实现:process.env.npm_package_version ?? '0.1.0'。

    该变量只在通过 npm 脚本启动时才存在,而本项目的正式部署方式是原生服务
    (systemd / OpenRC / initd,ExecStart=node dist/main.js)—— npm 不参与,
    于是它恒为 undefined,/health 就回退到硬编码的 0.1.0。
    这与「管理端左下角展示 vX.Y.Z,且必须与 GET /health 的 version 一致」
    (文档 §11.2 第 4 条)直接冲突。

    修复:

    • 新增 src/config/app-version.ts 作为版本号的运行时唯一解析点,按优先级尝试
      APP_VERSION 环境变量 → 运行入口旁的 package.json → 工作目录的
      package.json → npm_package_version,每一步都做包名校验
      (必须是 opendance-web-server,避免读到上层仓库的 package.json);
      全都拿不到时返回显式的 0.0.0,绝不伪装成某个具体版本;
    • /health 改为 version: resolveAppVersion();
    • main.ts 启动首行也打印 应用版本 vX.Y.Z,即使数据库连不上、进程退出,
      日志里也一定能看到本次运行的版本号。

    新增防线:

    • scripts/check-version.mjs(并入 npm run contracts:check)校验
      web-server/package.json = admin/package.json = project.yaml,并用正则
      禁止 health.controller.ts 再出现硬编码版本回退;管理端已构建时还会断言
      产物 JS 内确实含 vX.Y.Z;
    • test/app-version.spec.ts 12 项,其中一项显式模拟无 npm 环境变量的原生部署。

    校验(全部通过)

    检查 结果
    npm run contracts:check 契约六项版本一致 + 35 条路由一致 + 版本一致性一致
    npx tsc --noEmit 通过
    npx vitest run 19 文件 / 258 项全通过
    nest build 通过
    启动冒烟 npm run bootstrap:check 通过(见上)
    对发布产物解压后的实测 解压 tarball → npm ci --omit=dev(与 install.sh 同一步骤)→ env -u npm_package_version node dist/main.js:打印 应用版本 v0.7.2,全部模块 dependencies initialized,随后因本机没有 MySQL 而止于 ECONNREFUSED(预期)

    opendance-web-v0.7.2.tar.gz sha256: f04ecc85d17517ec00754238616eb1237cdb2fe17440af6dce731827c0922b26

    部署与部署后自检

    curl -fLO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.7.2/opendance-web-v0.7.2.tar.gz
    curl -fLO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.7.2/SHA256SUMS
    sha256sum -c SHA256SUMS
    tar -xzf opendance-web-v0.7.2.tar.gz && cd opendance-web-v0.7.2
    bash deploy/install.sh
    
    # 部署后必须做到「可核对」:
    curl -s http://<服务器地址>:3000/health | grep -o '"version":"[^"]*"'   # 必须输出 0.7.2
    

    管理端页面左下角显示的版本号必须与上面这条完全一致(都来自
    web-server/package.json)。

    数据库端到端验收(歌曲 CRUD → 动作上传 → 预处理 → 模型上传 → 审核 → 场景 → 目录导出)
    可在部署完成后执行:API_BASE=http://<服务器地址>:3000 node web-server/test/smoke.mjs

    说明:<服务器地址> 是你们自己部署的服务端地址(部署方决定),不是被脱敏的域名;
    本文档只对 Gitea 域名这类「部署必需且非隐私」的信息直接写出真实值
    (gitea.kasugano.cn / 属主 zijunchiang)。

    Downloads
  • v0.7.1 3738bd91b9

    zijunchiang released this 2026-09-17 01:30:42 +08:00 | 15 commits to main since this release

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

    契约补齐 + 文档补齐(《DSH 开发文档 v1.6》7.2)。本版不含运行时行为变更,
    GET /health 的 version 由 web-server/package.json 同源驱动,管理端左下角同步显示 v0.7.1。

    一、契约 websocket-keypoints 0.1.0 → 0.2.0(T-WEB-016)

    与 Android 端 T-AND-011「连接状态异常、频繁重连」的两端一起改配套。
    按文档 §7.2,契约先在 Web 端修改并提升版本,再向另两端建迁移任务。

    完全向后兼容:子协议名 opendance.pose.v1 与二进制帧 version = 0x01 均不变,
    只新增可选控制字段与一种新控制类型;仅实现 v0.1.0 的一端仍可互通。

    章节 新增 为什么必须写进契约
    1.1 保活 接收端必须声明 silentTimeoutMs 与 heartbeatIntervalMs,且心跳必须小于静默超时的一半 客户端原先只能用 OkHttp 的协议层 ping,而它由底层库自动应答、不暴露给应用层,接收端根本看不到,无法续命 —— 这是"画面里没人就掉线、然后疯狂重连"的根因
    3.1 hello.deviceId(该安装实例的稳定标识,不使用任何硬件序列号) 没有它,一次闪断重连会被接收端显示成两台设备
    3.2 / 3.3 welcome / config 携带上述保活字段 让客户端按服务端节奏保活,而不是各写一套魔数
    3.6 应用层 ping / pong(回显 sentAtMs,pong 另带 serverAtMs 供算 RTT) 协议层 ping 对应用层不可见,必须自定义
    5 设备去重:同一 deviceId 只保留最新连接,旧连接以 1000 关闭 用 1000 是刻意的:对端据 §7 不会重连,否则会形成"挤掉-重连-再挤掉"的循环
    7 关闭码与重连策略:1000 / 1001 / 1002 与主动停止不重连,其余重连 原实现"只要能重连就重连",服务端重启或协议不匹配时也会疯狂重连
    7.1 退避参数冻结:min(500ms × 2^(n-1), 15000ms) + ±20% 抖动 避免同网段多台设备同时重连打尖峰
    8 连接状态机 idle → resolving → connecting → handshaking → streaming → degraded → reconnecting → stopped 两端同名,日志可直接对照

    contracts/VERSION 与 web-server/src/config/contract-versions.ts 同步到 0.2.0
    (npm run contracts:check 强制两者一致)。

    二、补齐 rest-api.yaml 里漏写的场景结构

    catalog/export 自 v0.7.0 起就返回 scenes[],data-models.md §7 也已冻结,
    但 CatalogBundle 漏了这个字段、components 里也没有 Scene / SceneConfig 定义 ——
    属于"实现已发布、规格漏写"。只补规格,不改线格式,六个契约版本号均不变:

    • CatalogBundle 增加 scenes[](标注 0.5.0 起下发);
    • 新增 Scene / SceneConfig / Vec3 schemas(重力 ±200、风 0100 / 0.015Hz、
      摩擦 01、坡度 089°、出生点 ≤16、相机 follow|fixed|orbit 等边界全部写明)。

    规格缺失会让两端对字段边界的理解不一致(Windows 端 T-WIN-013 正是照这份结构生成的
    GDScript 类型)。

    三、deploy/proxy/README.md 新增第九节:Gitea 站点的附件上传上限

    与前八节的 Web 服务反代是两个不同站点 —— 托管 Gitea(源码 + Release 附件)的
    IIS 站点必须单独放开:

    上传体积 实测结果
    14 MB 通过(HTTP 201)
    30.5 MB 失败 HTTP 413
    40 MB 失败 HTTP 413

    根因是 IIS 默认的 maxAllowedContentLength = 30,000,000 字节(≈28.6 MiB)。
    这正是 Windows 客户端 v0.1.0 被拆成 zip.part0/1/2(用户拿到无法直接解压的碎片)的根因。

    文档给出放宽到 256 MB 的四处配置(IIS requestLimits + uploadReadAheadSize、
    ARR maxRequestBytes、Gitea app.ini 的 [attachment] MAX_SIZE、前置 nginx 的
    client_max_body_size)与 curl 复验命令。

    四、文档与脚本

    • 补齐 tasks/011 ~ tasks/016 与 project.yaml 条目:此前 T-WEB-011~015
      已在代码中落地却缺任务说明书(011 版本号展示 / 012 VRM 优先 / 013 动作标准与骨骼
      重绑定 / 014 动作预览 / 015 场景管理 / 016 本契约变更);
    • project.yaml 的 contracts_version 由单个 0.4.0 改为六项字典
      (六项契约已各自独立递增);
    • scripts/package-release.sh:版本号改为从 web-server/package.json 读取
      (与 GET /health 的 version、管理端左下角展示同源),并与 project.yaml
      交叉校验;同时补上 SHA256SUMS 与自校验(此前是手工生成的)。

    校验

    • npm run verify:contracts:check(六项一致 + 35 条路由一致)、tsc --noEmit、
      18 文件 / 246 项测试、nest build 全部通过;
    • opendance-web-v0.7.1.tar.gz sha256: 27f92cfb125bbcf29077d4ad90c44720cbca46d23ddef12cd7826d73bd8865e2

    部署

    curl -fLO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.7.1/opendance-web-v0.7.1.tar.gz
    curl -fLO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.7.1/SHA256SUMS
    sha256sum -c SHA256SUMS
    tar -xzf opendance-web-v0.7.1.tar.gz && cd opendance-web-v0.7.1
    bash deploy/install.sh
    

    本版不需要重新部署也能继续使用:运行时行为未变,只是 /health 报出的
    websocket-keypoints 契约版本从 0.1.0 变为 0.2.0,以及 contracts/ 快照更新。
    若要让 /health 与新快照一致,重新部署本版即可。

    Downloads
  • v0.7.0 f2e231c0b2

    zijunchiang released this 2026-09-17 00:32:05 +08:00 | 17 commits to main since this release

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

    补齐场景管理前端(《DSH 开发文档 v1.6》4.12 / T-WEB-015)。
    契约保持 rest-api -> 0.5.0、data-models -> 0.7.0、database -> 0.4.0(三端一致,本次无契约变更)。

    本次改动

    1. 新增管理端「场景管理」页(左侧导航 → 场景管理,路由 /scenes):
      • 场景列表:审核状态、格式、可用性、GLB 体积、更新时间;
      • 上传:拖拽 .glb / .gltf / .zip / .pmx / .fbx(zip 自动解包、非 GLB 由服务端 Assimp 转 GLB);
      • 审核操作:通过 / 驳回(上传一律先落 pending,复用 /api/reviews/scene/:id/...);
      • 删除:同时清理对象存储中的源文件、GLB 与缩略图。
    2. 物理参数可视化编辑(对应 SceneConfig,冻结在 data-models 7.1):
      • 重力向量((0,-9.8,0) 默认,各分量 ±200)、质量无关的常量加速度;
      • 风:基础风向量 + 阵风(幅度 0100、频率 0.015Hz、方向、world/local);
      • 地面:摩擦 01、弹性 01、坡度上限 0~89 度;碰撞体 auto/mesh/none;
      • 环境:环境光颜色与强度、雾浓度;出生点(≤16)与相机(follow/fixed/orbit)。
      • 编辑期即做与后端相同的夹紧/回退(admin/src/lib/scene-physics.ts,与 scene-config.ts 同规则),
        保存前不会产生非法枚举;服务端仍会再收敛一次,服务端只存参数、不跑物理。
    3. 3D 预览:预审场景可展开惰性加载的 GLB 预览(与模型预览共用加载器),便于确认坐标系与尺寸。
    4. 版本号:管理端左下角显示当前版本(构建期由 web-server/package.json 注入),本次显示 v0.7.0。

    校验

    • 类型检查通过;vitest 245 项全绿(新增 test/scene-config.spec.ts 边界用例);
    • contracts:check 通过、check-routes 35 条路由一致;
    • 管理端生产构建通过;
    • opendance-web-v0.7.0.tar.gz sha256: 6ed1b0fd63697f38f5b4e40e25923d53d693d0742e4ef4c4093282e3c0942eab

    部署

    curl -fLO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.7.0/opendance-web-v0.7.0.tar.gz
    curl -fLO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.7.0/SHA256SUMS
    sha256sum -c SHA256SUMS
    tar -xzf opendance-web-v0.7.0.tar.gz && cd opendance-web-v0.7.0
    bash deploy/install.sh
    

    下一步(同一目标内继续)

    • Windows 端场景选择与物理落地(T-WIN-013)、发布整包(T-WIN-014);
    • Android 端重连与 MediaPipe 修复(T-AND-011/012)。
    Downloads
  • v0.6.0 584b7eaca9

    zijunchiang released this 2026-09-17 00:27:08 +08:00 | 18 commits to main since this release

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

    新增场景管理(《DSH 开发文档 v1.6》4.12 / T-WEB-015)。
    契约 rest-api -> 0.5.0、data-models -> 0.7.0、database -> 0.4.0(三端已同步)。

    本次改动

    1. 场景以 GLB 为主:.glb / .gltf 直接导入(不重编码);.zip / .pmx / .fbx
      由服务端转 GLB。场景不做 humanoid 重绑定、不转 VRM(与模型区分)。
    2. 物理参数(SceneConfig,冻结在 data-models 7.1):
      • gravity 重力向量(默认 (0,-9.8,0),各分量 ±200);
      • wind 风向动态:基础风向量 + 阵风(幅度 0100、频率 0.015Hz、方向、world/local 坐标系);
      • ground:摩擦 01、弹性 01、坡度上限 0~89 度;
      • colliders:auto / mesh / none;
      • environment:环境光颜色/强度、雾浓度;
      • spawnPoints(≤16)、camera(follow/fixed/orbit)。
        服务端只存参数、不跑物理 —— 仿真在客户端(Windows 端用 Godot 内置物理)。
    3. 入库即收敛:越界夹紧、非法枚举回退默认值,保证客户端拿到的参数一定可解释;
      config / stats 用 MEDIUMTEXT(TEXT 的 64KB 上限在 models.stats 上踩过坑)。
    4. 端点(check-routes 35 条一致):GET/POST /api/scenes、GET/PATCH/DELETE /api/scenes/<built-in function id>;
      审核复用 /api/reviews/scene/:id/approve|reject(上传一律 pending);
      catalog/export 增加 scenes[],目录指纹纳入场景变更。
    5. 缩略图复用模型光栅化器、分析复用模型检查器。

    下一步(同一目标内继续)

    • 管理端「场景管理」页:GLB 预览 + 重力/风向等参数可视化编辑;
    • Windows 端场景选择与物理落地(T-WIN-013)、发布整包(T-WIN-014);
    • Android 端重连与 MediaPipe 修复(T-AND-011/012)。

    校验

    opendance-web-v0.6.0.tar.gz sha256: 223d2b0d059bbffcfda6d873f13eea911a39619ad4de301be96b3e124f105ead

    Downloads
  • v0.5.0 aad60cbd2e

    zijunchiang released this 2026-09-17 00:21:09 +08:00 | 19 commits to main since this release

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

    动作预览(《DSH 开发文档 v1.6》4.11 / T-WEB-014)。契约 data-models -> 0.6.0。

    本次改动

    服务端

    1. 抽出公共 PNG 编码器 common/png.ts(模型缩略图改为复用;零原生依赖不变);
    2. 新增 motions/motion-thumbnail.ts:关键点序列 -> 正视图骨架 PNG
      (复用 33 点父表连线,包围盒按整段计算,低置信度点不画);
    3. motions.thumbnail_asset_id(迁移 AddMotionThumbnail)+ AssetKind.MotionThumbnail;
    4. /api/motions/:id/preprocess 生成缩略图并落库,失败只记 warning,不影响预处理;
    5. MotionDto.thumbnailUrl(契约 data-models 0.6.0)。

    管理端(重点)
    6. 新增 MotionPreview 3D 回放:懒加载 GET /api/motions/:id/keypoints(NDJSON)→
    骨架线框 + 关节球体,支持 播放/暂停、上一帧/下一帧、时间轴拖动、0.25x-2x 变速、循环,
    叠加「第 N/M 帧 · 当前时间 · 总时长」,按整段包围盒自动取景;
    7. 动作管理列表新增 40px 骨架缩略图列(点击即开预览)与「预览 / 收起预览」按钮 + 内联预览面板;
    8. three.js 单独分包(lazy + Suspense):主包 241 kB 不涨(three 568 kB 独立 chunk)。

    已有动作请重新预处理

    缩略图在 /preprocess 时生成,已有动作需要重跑一次预处理才会出现缩略图:

    # 管理端「动作管理」逐条点「预处理」,或对每个动作调用:
    curl -X POST http://127.0.0.1:3000/api/motions/<id>/preprocess
    

    预览功能本身不需要缩略图,只要动作处于 done 状态即可播放。

    校验

    opendance-web-v0.5.0.tar.gz sha256: 67afe31e51e192b0d766fff219381577b3a9c4e326c256093b5219d4b58d21c3

    Downloads
  • v0.4.0 9eae6f3e43

    zijunchiang released this 2026-09-17 00:10:07 +08:00 | 20 commits to main since this release

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

    骨骼重绑定:让「一律转 VRM」真正可用(契约 skeleton-topology -> 0.2.0)。
    对应《DSH 开发文档 v1.6》4.10(T-WEB-013)。

    根因:MMD 模型 humanoid 恒为 0/22

    normalizeBoneName() 原先只保留 a-z0-9,中文/日文字符被整个丢掉:
    上半身 / 左ひじ / 首 归一化后都是空串,MMD 骨名必然失配 ->
    humanoidMapped=0/22 -> 不可驱动 -> 只能回退交付 GLB 并标记 usable=false。

    本次改动

    1. 归一化修复:保留 CJK / 平假名 / 片假名 / 长音符 / 々,并把全角 ASCII 折成半角
      (MMD 常写 足IK / つま先IK);精确匹配保证 左足IK 不会抢占 左足。
    2. 候选表补齐四类命名(22 根 × 8-13 个候选):VRoid、Mixamo、MMD 标准骨架
      (下半身/上半身/上半身2/首/頭/左肩/左腕/左ひじ/左手首/左足/左ひざ/左足首/左つま先)、
      常见中文名(臀部/头/脖子/左大臂/左小臂/左大腿/左脚 …)。
    3. 服务端与 Windows 客户端逐字一致:humanoid_map.gd 的 CANDIDATES 由服务端表生成写入,
      normalize() 同步改造;parity 测试断言键顺序与候选顺序完全一致。
    4. 契约 skeleton-topology -> 0.2.0:新增 §6「内部动作标准与模型骨骼重绑定」
      (三层标准:33 点关键点 / MMD 标准骨架 / 22 根 humanoid;22 与 16 根骨清单;
      归一化与匹配规则;实测记录)。
    5. 测试规模 17 文件 / 229 项。

    实测(重绑定后)

    样本 之前 现在
    【奥黛塔】_by_原神.zip(PMX 日文骨名) 0/22,不可驱动,回退 GLB、usable=false 21/22,drivable=true,交付 VRM
    模型_初音未来…zip(PMX) 0/22,不可驱动 19/22,drivable=true,交付 VRM

    未命中的 1-3 根均为非必需骨(upperChest / leftToes 等),不影响驱动与评分。

    升级后请重新导入模型

    材质/骨骼重绑定都在转换时生效:把 MMD 模型(.zip / .pmx)重新上传即可,
    此前标记「不可用」的模型重新导入后会变成可用并进入客户端目录;
    reanalyze-models.mjs 只刷新分析与缩略图,不会重转。

    校验

    opendance-web-v0.4.0.tar.gz sha256: 2c913c39a5feda918bdf144ffac773284d7bdb768a2da50877647ae63ceb40bf

    Downloads
  • v0.3.0 39bfd390e3

    zijunchiang released this 2026-09-17 00:05:02 +08:00 | 21 commits to main since this release

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

    模型主力格式全面转为 VRM(契约 data-models / model-formats -> 0.5.0)。
    按《DSH 开发文档 v1.6》4.9 与第十一章实现的第一步(Web 端)。

    本次改动

    1. 所有上传的模型一律转 VRM:MODEL_CONVERT_FORMAT 默认改为 vrm
      (.pmx/.fbx 走 assimp -> GLB -> VRM 1.0;.glb/.gltf 导入后包装为 VRM)。
      支持库:自研 vrm-writer / vrm-inspector + 外部 assimp(可选 @gltf-transform/core 校验)。
    2. humanoid 硬门槛 + 可用性标记(按你的选择「入库但标记不可用,不进入已发布目录」):
      • 必需骨齐全 -> 交付 VRM,usable=true,可发布可选用;
      • 不达标(如 MMD 日文骨名 0/22)-> 回退交付 GLB、usable=false,
        可入库、可审核,但不进入 catalog/export(/api/catalog/version 指纹同步过滤),
        resolveDefaultModelId 也不会选中它;
      • 管理端模型列表与审核面板显示「不可用」及原因
        (如 humanoid 不达标(0/22),未产出 VRM);待骨骼按 skeleton-topology
        标准重绑定后重新导入即转为可用(下一步 T-WEB-013)。
    3. 管理端左下角展示当前版本号(v1.5 文档第十一章):构建期从
      web-server/package.json 注入,与 GET /health 的 version 同源;
      顺带修掉了侧栏里写死的「契约 rest-api 0.1.0」。
    4. 契约:data-models / model-formats -> 0.5.0(三端契约文件逐字节一致,
      已同步 opendance-windows / opendance-android 仓库)。
    5. 测试规模 16 文件 / 225 项。

    升级注意

    • 升级后新上传/重新导入的模型才会走 VRM 管线;存量 GLB 模型会显示为「不可用」,
      不再出现在客户端目录里 —— 这是本次策略变更的预期结果。
    • 想保留旧行为可显式设置 MODEL_CONVERT_FORMAT=glb(不推荐,与 0.5.0 契约不符)。

    校验

    opendance-web-v0.3.0.tar.gz sha256: 46c19e8f51a38f083bfc41e190fc45548dd0b248bd9a0932fdeaddd9988feb81

    Downloads