用户实测:服务器没有 systemctl,install.sh 直接报错退出。
原错误信息本身也是错的(建议 SKIP_NGINX=1,但那只跳过 nginx,仍走 systemctl)。
进程管理改为四层降级:
systemd → OpenRC → SysVinit → nohup 后台进程(deploy/ctl.sh)
- 新增 deploy/ctl.sh:start/stop/restart/status/logs,PID 文件 + 进程名双重判定
- 新增 deploy/openrc/opendance-api、deploy/initd/opendance-api
- 无 init 系统时写 crontab @reboot 实现开机自启
- uninstall.sh 同步支持四种模式清理(原先只处理 systemd,会留下运行中的进程)
- 汇总输出按实际初始化系统给出对应运维命令
测试中发现并修复降级路径的两个真实缺陷:
1. nohup 无法调用 shell 函数
实测退出码 127 且什么都不执行
(nohup 是外部命令,只能执行可执行文件)。
这会让无 systemd 的降级路径完全失效——服务根本起不来。
改为按 runuser / setpriv / su 显式拼接成外部命令后再 nohup。
另加启动后 1 秒存活检测,失败立即打印日志,不再白等 30 秒。
2. find_pids 在无进程时返回一个空格
在空输入下仍会执行一次 echo,输出空行,
最终得到 " ";调用方 [ -n ... ] 会判为「有进程」,
导致 start 直接跳过、status 谎报运行中。改为纯 bash 拼接,空时返回空串。
实测(真实 API 进程,端口 3100):
start → 健康检查 200,PID 正确
重复 start → 识别已在运行,进程数保持 1
status → 运行中
restart → 新 PID,健康检查 200
stop → 无残留进程,端口释放
无 process 时 find_pids 返回空串,is_running=false
198 lines
8.9 KiB
Plaintext
198 lines
8.9 KiB
Plaintext
================================================================
|
||
OpenDance Studio —— Web 管理服务端 v0.1.0 使用说明
|
||
================================================================
|
||
|
||
本服务端是 OpenDance Studio 三端中的第一端,负责管理歌曲、VRM 模型与
|
||
动作来源,是 Windows 客户端与 Android 辅助 App 的唯一数据源与契约权威。
|
||
|
||
重要:本服务端 **不部署、不管理 MySQL**。数据库为部署方提供的现有实例,
|
||
连接信息通过 .env 注入。
|
||
|
||
----------------------------------------------------------------
|
||
一、部署到 Linux 服务器(原生站点,不使用 Docker)
|
||
----------------------------------------------------------------
|
||
|
||
部署形态:一个 nginx 站点同时提供管理端页面与 API。
|
||
Node 进程由 systemd 托管,只监听 127.0.0.1,外部只能经 nginx 访问。
|
||
|
||
前置要求
|
||
- Linux 服务器,有 root 权限(不需要 sudo,直接以 root 执行即可)
|
||
- Node.js 22 或更高版本
|
||
- nginx(未安装时脚本会跳过站点配置并给出提示)
|
||
- 进程管理:systemd / OpenRC / SysVinit 均可;**三者都没有也能装**
|
||
(自动降级为 nohup 后台进程 + PID 文件 + crontab @reboot 开机自启)
|
||
- 一个可连通的现有 MySQL / MariaDB 实例,并已创建数据库与账号:
|
||
|
||
CREATE DATABASE IF NOT EXISTS dpendance
|
||
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||
CREATE USER IF NOT EXISTS 'dpendance'@'%' IDENTIFIED BY '<密码>';
|
||
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX, DROP, REFERENCES
|
||
ON dpendance.* TO 'dpendance'@'%';
|
||
FLUSH PRIVILEGES;
|
||
|
||
(本项目不会创建数据库本身,也不需要 GRANT OPTION / FILE / SUPER 等管理权限)
|
||
|
||
部署步骤
|
||
tar -xzf opendance-web-v0.1.0.tar.gz
|
||
cd opendance-web-v0.1.0
|
||
cp deploy/.env.example deploy/.env
|
||
vi deploy/.env # 填写 DB_HOST / DB_PORT / DB_USER / DB_PASS / DB_NAME
|
||
sudo bash deploy/install.sh
|
||
|
||
脚本会依次:
|
||
校验环境与 .env → 创建服务用户与目录 → 安装运行期依赖 →
|
||
执行 TypeORM 迁移 → 注册并启动 systemd 服务 → 等待健康检查 →
|
||
配置 nginx 站点并重载
|
||
|
||
部署完成后(<host> 为服务器 IP)
|
||
管理端首页: http://<host>/
|
||
API 前缀: http://<host>/api
|
||
健康检查: http://<host>/health
|
||
契约快照: http://<host>/contracts/
|
||
|
||
可覆盖的安装参数
|
||
APP_DIR=/opt/opendance 程序目录
|
||
SERVICE_USER=opendance 运行服务的系统用户
|
||
STORAGE_DIR=/var/lib/opendance/storage 上传文件目录
|
||
SERVER_NAME=example.com nginx server_name
|
||
SKIP_NGINX=1 只装 API 服务,不配置 nginx
|
||
|
||
例:sudo SERVER_NAME=dance.example.com bash deploy/install.sh
|
||
|
||
升级
|
||
解压新版本后重新执行 sudo bash deploy/install.sh 即可(会覆盖程序文件,
|
||
保留 deploy/.env 与上传数据)。
|
||
|
||
卸载
|
||
sudo bash deploy/uninstall.sh # 停服务、禁用站点,保留数据
|
||
sudo bash deploy/uninstall.sh --purge # 一并删除程序目录与上传文件
|
||
|
||
数据库始终由部署方自行管理,卸载脚本不做任何改动。
|
||
|
||
常用命令(按安装时自动检测到的初始化系统而定)
|
||
systemd:
|
||
systemctl status opendance-api # 服务状态
|
||
journalctl -u opendance-api -f # 实时日志
|
||
systemctl restart opendance-api # 重启
|
||
|
||
OpenRC:
|
||
rc-service opendance-api status / restart
|
||
tail -f /var/log/opendance-api.log
|
||
|
||
SysVinit:
|
||
/etc/init.d/opendance-api status / restart
|
||
|
||
无 init 系统(降级模式):
|
||
bash /opt/opendance/deploy/ctl.sh status | restart | logs
|
||
|
||
nginx 配置校验:
|
||
nginx -t && (systemctl reload nginx || rc-service nginx reload || nginx -s reload)
|
||
|
||
----------------------------------------------------------------
|
||
二、本地开发
|
||
----------------------------------------------------------------
|
||
|
||
后端
|
||
cd web-server
|
||
cp .env.example .env # 填写 DB_*
|
||
npm install
|
||
npm run migration:run # 首次需要
|
||
npm run start:dev # http://localhost:3000
|
||
|
||
管理前端
|
||
cd web-server/admin
|
||
npm install
|
||
npm run dev # http://localhost:5173(/api 与 /health 代理到 3000)
|
||
|
||
----------------------------------------------------------------
|
||
三、验证
|
||
----------------------------------------------------------------
|
||
|
||
cd web-server
|
||
npm run contracts:check # 契约版本一致性
|
||
npm run typecheck # TypeScript 严格检查
|
||
npm run test # 31 项单元测试
|
||
npm run build # 生产构建
|
||
npm run start:prod & # 启动
|
||
npm run test:smoke # 57 项端到端冒烟(需要数据库可连通)
|
||
|
||
cd ../admin && npm run build # 管理端构建
|
||
|
||
----------------------------------------------------------------
|
||
四、环境变量
|
||
----------------------------------------------------------------
|
||
|
||
DB_HOST / DB_PORT / DB_USER / DB_PASS / DB_NAME
|
||
现有 MySQL 实例连接信息(必填)
|
||
|
||
DB_SYNCHRONIZE
|
||
必须为 false。生产环境启动时若检测到 true 会直接中止启动。
|
||
所有表结构变更通过 TypeORM migration 执行。
|
||
|
||
DB_LOGGING 是否打印 SQL(默认 false)
|
||
PORT API 监听端口(默认 3000)
|
||
HOST API 监听地址(原生部署用 127.0.0.1,仅由 nginx 反代访问)
|
||
STORAGE_DIR 上传文件存储目录(原生部署默认 /var/lib/opendance/storage)
|
||
UPLOAD_MAX_BYTES 单文件上限,默认 536870912(512 MiB)
|
||
ASSIMP_BIN 可选:Assimp 可执行文件,FBX/glTF 动作预处理需要
|
||
CONTRACTS_DIR 可选:契约目录,设置后 /health 反映实时契约版本
|
||
(原生部署默认指向 /opt/opendance/contracts)
|
||
|
||
----------------------------------------------------------------
|
||
五、主要 API
|
||
----------------------------------------------------------------
|
||
|
||
GET /health 健康检查(含数据库连通性)
|
||
GET /api/songs 曲目列表(分页 / 检索)
|
||
POST /api/songs 新建曲目
|
||
GET /api/songs/:id 曲目详情
|
||
PATCH /api/songs/:id 更新曲目
|
||
DELETE /api/songs/:id 删除曲目(软删除)
|
||
POST /api/songs/:id/audio 上传音频
|
||
GET /api/motions 动作列表
|
||
POST /api/motions 上传动作 + 来源信息
|
||
POST /api/motions/:id/preprocess BVH/FBX → MediaPipe33 关键点
|
||
GET /api/motions/:id/keypoints 下载关键点序列(NDJSON)
|
||
GET /api/models 模型列表
|
||
POST /api/models 上传模型 + 来源信息(VRM 优先)
|
||
POST /api/models/:id/primary 设为全局默认模型
|
||
GET /api/reviews 审核流水 / 待审列表
|
||
POST /api/reviews/:type/:id/approve 审核通过
|
||
POST /api/reviews/:type/:id/reject 审核驳回(必须填意见)
|
||
GET /api/catalog/export 客户端一次性拉取已发布目录
|
||
GET /api/catalog/version 契约版本 + 目录指纹
|
||
|
||
完整契约(OpenAPI 3.0)见 contracts/rest-api.yaml。
|
||
|
||
上传文件通过 /static/<kind>/<yyyy>/<mm>/<sha256>.ext 访问,
|
||
相同内容只落盘一次(sha256 去重)。
|
||
|
||
----------------------------------------------------------------
|
||
六、目录结构
|
||
----------------------------------------------------------------
|
||
|
||
project.yaml 任务清单与状态
|
||
DSH.md 本项目 DSH 执行约束
|
||
tasks/ 单任务说明书(T-WEB-001 ~ 010)
|
||
contracts/ 跨端契约(权威,改接口先改这里)
|
||
web-server/ NestJS 服务端源码
|
||
web-server/admin/ React 管理端
|
||
deploy/ Linux 原生站点部署(nginx + systemd,不含 MySQL)
|
||
THIRD_PARTY.md 第三方开源项目声明
|
||
|
||
----------------------------------------------------------------
|
||
七、安全提示
|
||
----------------------------------------------------------------
|
||
|
||
1. .env 已被 .gitignore 忽略,禁止提交;仓库中只保留 .env.example。
|
||
2. 数据库账号使用最小权限,不授予 GRANT OPTION / FILE / SUPER。
|
||
3. 本版本未内置管理端登录认证,请部署在内网,或在 nginx 层加 Basic Auth / SSO。
|
||
4. 上传的模型与动作必须填写来源五元组并通过审核后才对客户端可见。
|
||
5. 未使用 SMPL 拓扑,统一 MediaPipe 33 点,规避非商用许可限制。
|
||
|
||
----------------------------------------------------------------
|
||
八、第三方开源声明
|
||
----------------------------------------------------------------
|
||
|
||
见 THIRD_PARTY.md,或访问站点 http://<host>/ 的「关于」页面。
|