483 lines
12 KiB
Markdown
483 lines
12 KiB
Markdown
# sumi.work NPM 部署说明
|
||
|
||
这份文档给 AI/运维执行者使用,用于把当前仓库部署为可运行的 Node.js 服务。项目包含静态首页、软件下载中心、玩家登录下载限制、软件管理后台和上传更新 API。
|
||
|
||
## 1. 运行环境
|
||
|
||
要求:
|
||
|
||
- Node.js 20+,推荐 Node.js 22 LTS 或更高版本
|
||
- npm
|
||
- 持久化可写目录:推荐 `/var/lib/sumi-work/`
|
||
- 对外端口:默认 `3030`,可通过 `PORT` 修改
|
||
|
||
检查:
|
||
|
||
```bash
|
||
node --version
|
||
npm --version
|
||
```
|
||
|
||
## 2. 获取代码
|
||
|
||
```bash
|
||
git clone ssh://git@www.sumi.work:222/lwt/sumi.work.git
|
||
cd sumi.work
|
||
```
|
||
|
||
如果服务器上已经有仓库:
|
||
|
||
```bash
|
||
cd /path/to/sumi.work
|
||
git pull origin main
|
||
```
|
||
|
||
## 3. 安装依赖
|
||
|
||
生产环境建议使用锁文件安装:
|
||
|
||
```bash
|
||
npm ci
|
||
```
|
||
|
||
如果没有 `package-lock.json` 或需要临时恢复:
|
||
|
||
```bash
|
||
npm install
|
||
```
|
||
|
||
## 4. 配置环境变量
|
||
|
||
必须为生产环境设置自己的管理令牌和玩家账号密码,不要使用默认值。
|
||
|
||
```bash
|
||
export PORT=3030
|
||
export ADMIN_TOKEN="change-this-admin-token"
|
||
export PLAYER_USERNAME="player"
|
||
export PLAYER_PASSWORD="change-this-player-password"
|
||
export SUMI_STORAGE_DIR="/var/lib/sumi-work"
|
||
```
|
||
|
||
可选变量:
|
||
|
||
```bash
|
||
export SUMI_DATA_DIR="/var/lib/sumi-work/data"
|
||
export SUMI_UPLOAD_DIR="/var/lib/sumi-work/uploads"
|
||
export MAX_UPLOAD_BYTES=1073741824
|
||
export PLAYER_SESSION_TTL_MS=604800000
|
||
```
|
||
|
||
变量说明:
|
||
|
||
- `PORT`:Node 服务监听端口,默认 `3030`
|
||
- `ADMIN_TOKEN`:管理后台调用上传/更新/删除 API 的令牌
|
||
- `PLAYER_USERNAME`:玩家登录账号
|
||
- `PLAYER_PASSWORD`:玩家登录密码
|
||
- `SUMI_STORAGE_DIR`:运行时数据根目录,推荐放在部署目录外,例如 `/var/lib/sumi-work`
|
||
- `SUMI_DATA_DIR`:软件列表和玩家账号数据目录;设置后会覆盖 `SUMI_STORAGE_DIR/data`
|
||
- `SUMI_UPLOAD_DIR`:上传图标、安装包和 version.json 目录;设置后会覆盖 `SUMI_STORAGE_DIR/uploads`
|
||
- `MAX_UPLOAD_BYTES`:最大上传文件大小,默认 1GB
|
||
- `PLAYER_SESSION_TTL_MS`:玩家登录有效期,默认 7 天
|
||
|
||
首次启动时,如果数据目录里的 `players.json` 不存在,服务会生成默认玩家。设置了 `PLAYER_USERNAME` 和 `PLAYER_PASSWORD` 时,会写入或更新对应玩家账号。
|
||
|
||
生产环境不要把上传数据放在 Git 部署目录里。推荐先创建持久化目录:
|
||
|
||
```bash
|
||
sudo mkdir -p /var/lib/sumi-work/data
|
||
sudo mkdir -p /var/lib/sumi-work/uploads/icons
|
||
sudo mkdir -p /var/lib/sumi-work/uploads/packages
|
||
sudo mkdir -p /var/lib/sumi-work/uploads/manifests
|
||
sudo chown -R "$USER":"$USER" /var/lib/sumi-work
|
||
```
|
||
|
||
如果服务器上已经有旧数据,先迁移到持久化目录再重新部署:
|
||
|
||
```bash
|
||
rsync -a server/data/ /var/lib/sumi-work/data/
|
||
rsync -a server/uploads/ /var/lib/sumi-work/uploads/
|
||
```
|
||
|
||
## 5. 启动服务
|
||
|
||
直接启动:
|
||
|
||
```bash
|
||
npm start
|
||
```
|
||
|
||
启动后访问:
|
||
|
||
- 首页:`http://服务器IP:3030/`
|
||
- 下载中心:`http://服务器IP:3030/software.html`
|
||
- 玩家登录:`http://服务器IP:3030/login.html`
|
||
- 管理后台:`http://服务器IP:3030/admin/software.html`
|
||
|
||
## 6. 用 PM2 部署
|
||
|
||
安装 PM2:
|
||
|
||
```bash
|
||
npm install -g pm2
|
||
```
|
||
|
||
启动:
|
||
|
||
```bash
|
||
PORT=3030 \
|
||
ADMIN_TOKEN="change-this-admin-token" \
|
||
PLAYER_USERNAME="player" \
|
||
PLAYER_PASSWORD="change-this-player-password" \
|
||
SUMI_STORAGE_DIR="/var/lib/sumi-work" \
|
||
pm2 start server/server.js --name sumi-work
|
||
```
|
||
|
||
保存进程:
|
||
|
||
```bash
|
||
pm2 save
|
||
pm2 startup
|
||
```
|
||
|
||
查看日志:
|
||
|
||
```bash
|
||
pm2 logs sumi-work
|
||
```
|
||
|
||
重启:
|
||
|
||
```bash
|
||
pm2 restart sumi-work
|
||
```
|
||
|
||
更新部署:
|
||
|
||
```bash
|
||
cd /path/to/sumi.work
|
||
git pull origin main
|
||
npm ci
|
||
pm2 restart sumi-work
|
||
```
|
||
|
||
如果用 PM2 保存环境变量,修改 `SUMI_STORAGE_DIR` 后需要重新执行启动命令或使用 `pm2 restart sumi-work --update-env`。部署更新时只更新代码目录,不要删除 `/var/lib/sumi-work`。
|
||
|
||
## 7. 用 systemd 部署
|
||
|
||
创建服务文件:
|
||
|
||
```bash
|
||
sudo nano /etc/systemd/system/sumi-work.service
|
||
```
|
||
|
||
示例内容,按实际路径替换 `WorkingDirectory`:
|
||
|
||
```ini
|
||
[Unit]
|
||
Description=sumi.work Node Service
|
||
After=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
WorkingDirectory=/path/to/sumi.work
|
||
ExecStart=/usr/bin/node server/server.js
|
||
Restart=always
|
||
RestartSec=3
|
||
Environment=NODE_ENV=production
|
||
Environment=PORT=3030
|
||
Environment=ADMIN_TOKEN=change-this-admin-token
|
||
Environment=PLAYER_USERNAME=player
|
||
Environment=PLAYER_PASSWORD=change-this-player-password
|
||
Environment=SUMI_STORAGE_DIR=/var/lib/sumi-work
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
启动并设置开机自启:
|
||
|
||
```bash
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable --now sumi-work
|
||
sudo systemctl status sumi-work
|
||
```
|
||
|
||
查看日志:
|
||
|
||
```bash
|
||
journalctl -u sumi-work -f
|
||
```
|
||
|
||
更新部署:
|
||
|
||
```bash
|
||
cd /path/to/sumi.work
|
||
git pull origin main
|
||
npm ci
|
||
sudo systemctl restart sumi-work
|
||
```
|
||
|
||
## 8. Nginx 反向代理
|
||
|
||
建议让 Nginx 只反向代理到 Node 服务,不要直接把上传安装包目录暴露成静态目录。安装包下载必须走 `/api/software/:id/download`,这样才能验证玩家登录。
|
||
|
||
示例:
|
||
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
server_name sumi.work www.sumi.work;
|
||
|
||
client_max_body_size 1024m;
|
||
|
||
location / {
|
||
proxy_pass http://127.0.0.1:3030;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
}
|
||
}
|
||
```
|
||
|
||
重新加载:
|
||
|
||
```bash
|
||
sudo nginx -t
|
||
sudo systemctl reload nginx
|
||
```
|
||
|
||
## 9. 验证部署
|
||
|
||
健康检查:
|
||
|
||
```bash
|
||
curl -i http://127.0.0.1:3030/api/health
|
||
```
|
||
|
||
预期包含:
|
||
|
||
```json
|
||
{"ok":true,"service":"sumi-software-center"}
|
||
```
|
||
|
||
检查软件下载列表:
|
||
|
||
```bash
|
||
curl -i http://127.0.0.1:3030/api/software
|
||
```
|
||
|
||
检查未登录不能下载:
|
||
|
||
```bash
|
||
curl -i -H "Accept: application/json" http://127.0.0.1:3030/api/software/sumi-launcher/download
|
||
```
|
||
|
||
预期:
|
||
|
||
```text
|
||
HTTP/1.1 401 Unauthorized
|
||
```
|
||
|
||
检查玩家登录:
|
||
|
||
```bash
|
||
curl -i \
|
||
-c /tmp/sumi-player.cookie \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"username":"player","password":"change-this-player-password","next":"/software.html"}' \
|
||
http://127.0.0.1:3030/api/auth/login
|
||
```
|
||
|
||
检查登录状态:
|
||
|
||
```bash
|
||
curl -i -b /tmp/sumi-player.cookie http://127.0.0.1:3030/api/auth/me
|
||
```
|
||
|
||
管理后台读取列表:
|
||
|
||
```bash
|
||
curl -i -H "X-Admin-Token: change-this-admin-token" http://127.0.0.1:3030/api/admin/software
|
||
```
|
||
|
||
## 10. version.json 与检查更新
|
||
|
||
管理后台上传软件时可以同时上传 `version.json`,也可以直接编辑“版本内容 JSON”。如果上传安装包且版本内容留空,服务端会自动生成一个兼容更新器的 version.json。
|
||
|
||
推荐 `version.json`:
|
||
|
||
```json
|
||
{
|
||
"version": "1.2.0",
|
||
"changelog": "修复已知问题并优化启动速度",
|
||
"forceUpdate": false,
|
||
"minSupportedVersion": "1.0.0",
|
||
"sha256": "package-sha256-if-known"
|
||
}
|
||
```
|
||
|
||
也兼容下面这种更新器常用格式:
|
||
|
||
```json
|
||
{
|
||
"version": "1.1.5",
|
||
"update_url": "https://sumi.work/api/software/server-manager/version.json",
|
||
"release_notes": "数据库测试连接改为真实 MySQL 连接探测。",
|
||
"download_url": "https://sumi.work/api/software/server-manager/download",
|
||
"full_installer_url": "https://sumi.work/api/software/server-manager/download",
|
||
"delta_updates": {}
|
||
}
|
||
```
|
||
|
||
字段说明:
|
||
|
||
- `version`:最新版本号,检查更新时用于和客户端当前版本对比
|
||
- `changelog`:更新说明
|
||
- `release_notes`:更新说明,等同于 `changelog`
|
||
- `update_url`:version.json 地址,建议改成当前下载中心的 `/api/software/<软件ID>/version.json`
|
||
- `download_url`:完整安装包下载地址,建议改成当前下载中心的 `/api/software/<软件ID>/download`
|
||
- `full_installer_url`:完整安装包下载地址,等同于 `download_url`
|
||
- `delta_updates`:差分更新信息,会原样返回
|
||
- `forceUpdate`:是否强制更新
|
||
- `minSupportedVersion`:最低支持版本,客户端版本低于它时会返回 `forceUpdate: true`
|
||
- `sha256`:安装包校验值;如果未提供,会使用服务端上传安装包计算出的值
|
||
|
||
如果使用你原来的内网文件服务地址,例如:
|
||
|
||
```json
|
||
"update_url": "http://172.18.180.94:3000/api/file?path=server_manager/build/output/version.json",
|
||
"download_url": "http://172.18.180.94:3000/api/download?path=server_manager/build/output/ServerManager_Setup.exe"
|
||
```
|
||
|
||
它也能被服务端读取和保存。但如果希望下载中心继续执行“玩家登录后才能下载”的规则,应改成下载中心自己的地址:
|
||
|
||
```json
|
||
"update_url": "https://sumi.work/api/software/server-manager/version.json",
|
||
"download_url": "https://sumi.work/api/software/server-manager/download",
|
||
"full_installer_url": "https://sumi.work/api/software/server-manager/download"
|
||
```
|
||
|
||
其中 `server-manager` 是软件 ID。新增软件时服务端会按软件名自动生成 ID;也可以上传后在管理接口返回结果里查看 `id`、`versionManifestUrl` 和 `downloadUrl`。
|
||
|
||
后台操作规则:
|
||
|
||
- 只上传安装包,版本内容留空:服务端自动生成 version.json。
|
||
- 上传安装包,同时填写版本内容 JSON:安装包会更新,版本内容按填写的 JSON 保存。
|
||
- 不上传安装包,只修改版本内容 JSON:只更新版本清单和检查更新内容。
|
||
- 上传 `version.json` 文件:文件内容优先于文本框内容。
|
||
|
||
获取版本清单:
|
||
|
||
```bash
|
||
curl -i http://127.0.0.1:3030/api/software/sumi-launcher/version.json
|
||
```
|
||
|
||
检查更新:
|
||
|
||
```bash
|
||
curl -i "http://127.0.0.1:3030/api/software/sumi-launcher/check-update?version=1.0.0"
|
||
```
|
||
|
||
返回重点字段:
|
||
|
||
```json
|
||
{
|
||
"currentVersion": "1.0.0",
|
||
"latestVersion": "1.2.0",
|
||
"updateAvailable": true,
|
||
"forceUpdate": false,
|
||
"downloadUrl": "/api/software/sumi-launcher/download",
|
||
"versionManifestUrl": "/api/software/sumi-launcher/version.json"
|
||
}
|
||
```
|
||
|
||
`downloadUrl` 仍然受玩家登录保护,未登录下载会返回 `401` 或跳转到登录页。
|
||
|
||
## 11. 数据与备份
|
||
|
||
生产环境需要把运行时数据放在 Git 部署目录外。推荐:
|
||
|
||
- `SUMI_STORAGE_DIR=/var/lib/sumi-work`
|
||
- `/var/lib/sumi-work/data/software.json`:软件元数据
|
||
- `/var/lib/sumi-work/data/players.json`:玩家账号数据
|
||
- `/var/lib/sumi-work/uploads/icons/`:上传的软件图标
|
||
- `/var/lib/sumi-work/uploads/packages/`:上传的软件安装包
|
||
- `/var/lib/sumi-work/uploads/manifests/`:上传的 version.json 文件
|
||
|
||
未设置 `SUMI_STORAGE_DIR`、`SUMI_DATA_DIR`、`SUMI_UPLOAD_DIR` 时,本地开发会继续使用仓库里的 `server/data/` 和 `server/uploads/`。生产部署不要依赖这个默认路径。
|
||
|
||
建议备份命令:
|
||
|
||
```bash
|
||
tar -czf sumi-work-data-$(date +%F).tar.gz -C /var/lib sumi-work
|
||
```
|
||
|
||
恢复时把备份内容还原到 `/var/lib/sumi-work`,确认服务环境变量仍然指向这个目录,然后重启服务。
|
||
|
||
重新部署代码时只执行:
|
||
|
||
```bash
|
||
cd /path/to/sumi.work
|
||
git pull origin main
|
||
npm ci
|
||
pm2 restart sumi-work --update-env
|
||
```
|
||
|
||
不要在部署脚本里执行 `rm -rf /var/lib/sumi-work`,也不要用新代码包覆盖这个目录。Node 服务只读取静态 HTML/CSS/JS 用于显示页面,上传软件包、version.json、玩家数据都写入持久化目录。
|
||
|
||
## 12. 常见问题
|
||
|
||
端口被占用:
|
||
|
||
```bash
|
||
lsof -i :3030
|
||
```
|
||
|
||
修改 `PORT` 后重启服务。
|
||
|
||
上传失败:
|
||
|
||
- 检查 `client_max_body_size`
|
||
- 检查 `MAX_UPLOAD_BYTES`
|
||
- 检查 `SUMI_UPLOAD_DIR` 或 `/var/lib/sumi-work/uploads/` 是否可写
|
||
|
||
管理后台 401:
|
||
|
||
- 确认前端输入的 `ADMIN_TOKEN` 与服务端环境变量一致
|
||
- 确认请求头是 `X-Admin-Token`
|
||
|
||
玩家无法下载:
|
||
|
||
- 先访问 `/login.html` 登录
|
||
- 确认 `/api/auth/me` 返回 `authenticated: true`
|
||
- 确认 Nginx 没有直接拦截或改写 Cookie
|
||
|
||
安装包被绕过下载:
|
||
|
||
- 不要配置 Nginx 直接静态暴露上传安装包目录,例如 `/var/lib/sumi-work/uploads/packages/`
|
||
- 正确做法是所有请求都反代到 Node 服务
|
||
|
||
## 13. 快速执行清单
|
||
|
||
```bash
|
||
cd /path/to/sumi.work
|
||
git pull origin main
|
||
npm ci
|
||
sudo mkdir -p /var/lib/sumi-work/data /var/lib/sumi-work/uploads/icons /var/lib/sumi-work/uploads/packages /var/lib/sumi-work/uploads/manifests
|
||
sudo chown -R "$USER":"$USER" /var/lib/sumi-work
|
||
export PORT=3030
|
||
export ADMIN_TOKEN="change-this-admin-token"
|
||
export PLAYER_USERNAME="player"
|
||
export PLAYER_PASSWORD="change-this-player-password"
|
||
export SUMI_STORAGE_DIR="/var/lib/sumi-work"
|
||
npm start
|
||
```
|
||
|
||
验证:
|
||
|
||
```bash
|
||
curl http://127.0.0.1:3030/api/health
|
||
```
|