fix(models): Assimp 缺失时给出可操作报错,部署脚本自动安装

现象:服务器未安装 Assimp,上传 PMX 压缩包只得到
  GLB 直出:导出失败(ENOENT):spawn assimp ENOENT
  glTF 直出:导出失败(ENOENT):spawn assimp ENOENT
用户看不出缺什么、怎么装,也不知道只传 glb/vrm 其实不需要它。

修复:
1. 新增 src/common/assimp.ts:按 ASSIMP_BIN -> PATH -> /usr/bin、/usr/local/bin
   等常见目录定位可执行文件(只认文件,不认同名目录),转换前先预检;
   找不到就一次性给出各发行版安装命令、查过的路径与替代办法,
   不再打两条 ENOENT。动作预处理(FBX/glTF -> BVH)复用同一文案。
2. 新增 deploy/assimp-lib.sh:install.sh 按发行版自动安装
   (apt-get / dnf / yum / zypper / apk / pacman),装在非常规目录时把绝对
   路径写入 deploy/.env 的 ASSIMP_BIN;装不上只告警不中止,可用
   SKIP_ASSIMP_INSTALL=1 跳过。check-access.sh 自检增加 Assimp 一行。
3. /health 增加 assimp.path;管理端「概览 -> 服务健康」显示 Assimp 状态,
   不可用时给出安装提示。
4. 新增 test/assimp-locate.spec.ts 13 项;强化「assimp 不存在」用例断言安装命令。
   测试规模 12 文件 / 193 项。
5. 版本 0.2.2,README(.md/.txt)/DSH.md 同步(前置要求、FAQ、环境变量说明)。
This commit is contained in:
opendance
2026-09-16 02:55:32 +08:00
parent e9de740d66
commit da03777995
20 changed files with 579 additions and 53 deletions
+22 -10
View File
@@ -10,7 +10,15 @@ OpenDance Studio **阶段一**: Web 管理服务端(NestJS + TypeORM + 现有 My
## 状态
**阶段一已完成**: T-WEB-001 -> T-WEB-010 全部落地,当前版本 **v0.2.1**(契约 `rest-api / data-models / model-formats 0.4.0`、`database 0.3.0`)。
**阶段一已完成**: T-WEB-001 -> T-WEB-010 全部落地,当前版本 **v0.2.2**(契约 `rest-api / data-models / model-formats 0.4.0`、`database 0.3.0`)。
v0.2.2 修订:
1. **修复 Assimp 缺失时的报错与部署体验**:服务器没装 Assimp 时,上传 .pmx/.fbx(或含它们的压缩包)只得到两条 `导出失败(ENOENT): spawn assimp ENOENT`,看不出缺什么、怎么装;
2. 转换前先**定位 Assimp 可执行文件**(`src/common/assimp.ts`):按 `ASSIMP_BIN` -> PATH -> `/usr/bin`、`/usr/local/bin` 等常见目录查找,找不到就一次性给出各发行版安装命令、查过的路径与**替代办法**(只传 .glb/.vrm 不需要它),不再打两条 ENOENT;
3. `install.sh` 新增 **Assimp 检查与自动安装**(`deploy/assimp-lib.sh`):按发行版调用 apt-get / dnf / yum / zypper / apk / pacman;装在非常规目录时自动把绝对路径写进 `deploy/.env` 的 `ASSIMP_BIN`;装不上只告警不中止(可用 `SKIP_ASSIMP_INSTALL=1` 跳过);
4. `deploy/check-access.sh` 自检增加 Assimp 一行;管理端「概览 -> 服务健康」显示 Assimp 状态,不可用时给出提示;
5. 新增 13 项测试(`test/assimp-locate.spec.ts`),覆盖定位、错误文案、快速失败与部署脚本的安装逻辑。
v0.2.1 修订:
@@ -45,15 +53,15 @@ v0.2.0 主要变更:
3. 取得发布包并校验(地址用你们自己的制品库替换; 下面是本仓库的发布地址)
```bash
curl -LO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.2.1/opendance-web-v0.2.1.tar.gz
curl -LO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.2.1/SHA256SUMS
curl -LO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.2.2/opendance-web-v0.2.2.tar.gz
curl -LO https://gitea.kasugano.cn/zijunchiang/opendance-web/releases/download/v0.2.2/SHA256SUMS
sha256sum -c SHA256SUMS # 期望输出 "OK"
```
4. 解压并生成配置(交互式引导,逐项问答,口令不回显)
```bash
tar -xzf opendance-web-v0.2.1.tar.gz && cd opendance-web-v0.2.1
tar -xzf opendance-web-v0.2.2.tar.gz && cd opendance-web-v0.2.2
bash deploy/init-env.sh
```
@@ -103,6 +111,9 @@ Node 业务服务由系统服务托管,**同时监听两个端口**,职责分开
- Linux 服务器,有 **root** 权限(不需要 sudo,直接以 root 执行即可)
- **Node.js 22** 或更高版本
- 进程管理:`systemd` / `OpenRC` / `SysVinit` 均可;三者都没有也能装(自动降级为 `nohup` 后台进程 + PID 文件 + crontab `@reboot` 开机自启)
- Assimp 命令行工具(**可选但推荐**):`.pmx/.fbx` 模型转换与 FBX/glTF 动作预处理需要它;
`install.sh` 会自动尝试安装(apt-get / dnf / yum / zypper / apk / pacman),装不上只告警不中止。
只上传 `.glb/.vrm` 模型与 `.bvh/.vmd` 动作则完全不需要
- 反向代理:由另外的服务提供,本项目不要求、也不安装
- 一个可连通的现有 MySQL / MariaDB 实例,并已创建数据库与账号:
@@ -120,8 +131,8 @@ FLUSH PRIVILEGES;
### 5. 部署步骤
```bash
tar -xzf opendance-web-v0.2.1.tar.gz
cd opendance-web-v0.2.1
tar -xzf opendance-web-v0.2.2.tar.gz
cd opendance-web-v0.2.2
bash deploy/init-env.sh # 首次启动引导: 逐项问答生成 deploy/.env(见下)
bash deploy/install.sh # 已以 root 登录, 不需要 sudo
```
@@ -261,7 +272,7 @@ bash /opt/opendance/deploy/ctl.sh status | restart | logs
| 健康检查返回 503 | 服务起来了但连不上数据库:核对 `deploy/.env` 的 `DB_HOST/DB_PORT/DB_USER/DB_PASS/DB_NAME`,以及数据库账号是否允许从本机 IP 连接(`GRANT ... TO 'user'@'%'` 或具体主机) |
| 服务本机 curl 通、外部访问不了 | `bash deploy/check-access.sh` 一次定位:打印 `HOST`/`PORT`、实际监听地址、对外 IP 自测、80/443 有无反代、防火墙放行情况,并给出修复命令 |
| 管理页面打开是 404 | 说明访问的是软件 API 端口(默认 3000);管理页面在管理端端口(默认 3001) |
| 上传 `.zip` 模型包报 422 且提示 Assimp 不可用 | PMX/FBX 转换依赖 Assimp:`apt-get install -y assimp-utils libassimp6`;只上传 `.glb` / `.vrm` 则不需要它 |
| 上传 `.zip` / `.pmx` / `.fbx` 报转换失败(ENOENT / Assimp 不可用) | PMX/FBX 转换依赖外部 Assimp:`apt-get install -y assimp-utils libassimp6`(RHEL 系 `dnf install -y assimp`,`install.sh` 现在会尝试自动安装)。装好后**不需要重启服务**;若不在服务 PATH 中,在 `deploy/.env` 设 `ASSIMP_BIN` 为绝对路径。只上传 `.glb` / `.vrm` 则不需要它 |
| 安装时报 `command not found` / 退出码 127,且提示指向 `deploy/.env` 某一行 | 该行的值含空格或 shell 元字符却没加引号,被 shell 当成命令执行了。按预检提示给这个值加引号(如 `MODEL_CONVERT_PMX="Hatsune Miku.pmx"`)后重跑;`deploy/env-lib.sh` 会提前拦住并给出行号 |
| 模型审核界面显示"未分析 / 无预览图" | 该模型是升级前入库的:调 `POST /api/models/<id>/reanalyze`,或批量跑 `node scripts/reanalyze-models.mjs --url http://127.0.0.1:3000` |
@@ -291,10 +302,11 @@ npm run dev # http://localhost:5173(/api 与 /health 代理到 3000)
cd web-server
npm run contracts:check # 契约版本一致性
npm run typecheck # TypeScript 严格检查
npm run test # 180 项单元测试 / 11 个文件(VMD 31、模型压缩包 24、
npm run test # 193 项单元测试 / 12 个文件(VMD 31、模型压缩包 24、
# 模型检查器 25、缩略图 8、骨骼过滤 8、
# 预处理 25、init-env 16、admin-port 16、
# access-log 9、VRM 检查器 6、.env 预检 12)
# access-log 9、VRM 检查器 6、.env 预检 12、
# Assimp 定位与安装 13)
npm run build # 生产构建
npm run start:prod & # 启动
npm run test:smoke # 57 项端到端冒烟(需要数据库可连通)
@@ -316,7 +328,7 @@ cd ../admin && npm run build # 管理端构建
| `ADMIN_DIR` | `../admin`(即 `/opt/opendance/admin`) | 管理端静态站点目录 |
| `STORAGE_DIR` | `/var/lib/opendance/storage` | 上传文件存储目录(原生部署默认值) |
| `UPLOAD_MAX_BYTES` | `536870912`(512 MiB) | 单文件上限。反向代理层的同名限制必须不小于此值,否则大模型上传会在代理层被拒(nginx `client_max_body_size` / IIS `requestLimits`),详见 `deploy/proxy/README.md` |
| `ASSIMP_BIN` | - | 可选:Assimp 可执行文件。FBX/glTF 动作预处理需要;上传 `.zip` 模型包、或 `.pmx/.fbx` 单文件模型时必须能调用到,否则转换失败(422) |
| `ASSIMP_BIN` | `assimp` | Assimp 可执行文件(`install.sh` 会自动安装并解析出绝对路径写入此项)。`.pmx/.fbx` 模型转换与 FBX/glTF 动作预处理需要;缺失时上传会失败并给出安装命令,只上传 `.glb/.vrm` 不受影响 |
| `MODEL_CONVERT_FORMAT` | `glb` | 需要转换的模型(PMX/FBX)的交付格式:`glb`(两个客户端都原生加载)或 `vrm`。`vrm` = 在 GLB 之上补 `VRMC_vrm` 扩展(humanoid 骨骼 / 表情语义),humanoid 骨骼映射不全时自动回退交付 GLB 并写进 `conversion_note`。产物一律用 `assimp info` 回读校验 |
| `MODEL_CONVERT_PMX` | `"Hatsune Miku.pmx"` | 压缩包内优先选用的 PMX 文件名(大小写不敏感);找不到时按「格式优先级 -> 目录最浅 -> 文件最大」挑选 |
| `MODEL_CONVERT_TIMEOUT_MS` | `180000` | 单次转换超时(毫秒) |