fix: 数据目录移出安装目录,修掉「覆盖安装/卸载即丢数据」
根因:数据目录是 <安装目录>\data(config.yaml 的 dataDir 相对服务端工作目录),
而 electron-builder 的卸载器结尾执行 RMDir /r $INSTDIR,且覆盖安装会先跑旧卸载器
(installSection.nsh:52 的 uninstallOldVersion)→ 赛事库/规则/用户/审计/上传图片
连同安装目录被整个删除,再装新版。卸载一次同样如此。
- 服务端新增 -data <dir>(主命令、backup、passwd 三处),非空时覆盖 config.yaml 的
dataDir;不传时行为完全不变(兼容便携/手工运行)
- Console 把数据指到当前用户目录 %LOCALAPPDATA%\ScoreboardConsole\data(卸载器不会碰
这里;没有用 app.getPath('userData'),因为 %APPDATA%\<AppName> 正是被删的那个)
- 一次性迁移:目标为空且 <安装目录>\data 有数据时整体改名过去;两边都有数据时不动作
只记警告;任何失败都保留原地数据、绝不删除
- passwd 也带 -data(否则重置的是安装目录下那个空库里的账号)
- deploy/backup.bat 指向同一数据目录
- 文档:部署指南新增「数据放在哪里(升级/卸载都不会删)」+ 备份/恢复/迁移路径改写,
上线检查清单与使用手册同步
自检:服务端 12 包全绿;console 语法 14 文件 + datadir 单测 11/11;backup.bat 保持纯 ASCII。
This commit is contained in:
1 parent
6483acf9ad
commit
005b51b2ec
18 files changed
+626
-64
No files matched your search
+26
-2
@@ -20,6 +20,8 @@ console/
|
||||
├── main/
|
||||
│ ├── index.js # 应用入口:窗口 / 托盘 / IPC / 生命周期
|
||||
│ ├── config.js # 配置加载与合并、exe 路径解析
|
||||
│ ├── datadir.js # 数据目录解析 + 旧数据一次性迁移
|
||||
│ ├── datadir.test.js # 数据目录的单元测试(node --test main/datadir.test.js)
|
||||
│ ├── service.js # 服务端子进程守护:spawn / 日志转发 / 健康检查 / 自动重启
|
||||
│ ├── instances.js # 已连接大屏实例看板:复用管理面板登录态读 /api/display/instances
|
||||
│ ├── account.js # 管理员账号重置:参数校验 + 调 scoreboard.exe passwd
|
||||
@@ -62,12 +64,31 @@ scoreboard-windows-amd64/
|
||||
├── scoreboard.exe
|
||||
├── config.yaml
|
||||
├── rules/
|
||||
├── data/
|
||||
└── logs/console.log # 运行时生成
|
||||
```
|
||||
|
||||
数据不在安装目录里,默认落在 `%LOCALAPPDATA%\ScoreboardConsole\data`(见下文「数据目录」)。
|
||||
|
||||
打包后 `config.json` 读取顺序:`SCOREBOARD_CONSOLE_CONFIG` 环境变量 → exe 同目录的 `config.json`。
|
||||
|
||||
## 数据目录(重要)
|
||||
|
||||
electron-builder 生成的卸载器结尾是 `RMDir /r $INSTDIR`,而**覆盖安装会先静默跑一遍旧版卸载器**,所以装在安装目录里的 `data\` 会被整体删掉;`%APPDATA%\Scoreboard Console` 卸载时同样会删,因此也不能用 `app.getPath('userData')`。最终方案:
|
||||
|
||||
| 场景 | 数据目录 |
|
||||
| --- | --- |
|
||||
| 打包态,`server.dataDir` 留空(默认) | `%LOCALAPPDATA%\ScoreboardConsole\data`(卸载器不碰这个位置) |
|
||||
| 配置了 `server.dataDir` | 配什么用什么:绝对路径直接用;相对路径以安装目录为基准 |
|
||||
| 开发态(`pnpm start`),`server.dataDir` 留空 | 不接管,沿用服务端默认(工作目录下的 `data`),仓库里的 `data/` 不受影响 |
|
||||
|
||||
- Console 把最终目录通过服务端的 `-data <绝对路径>` 传下去,`passwd` 子命令带同一个参数,保证改的是同一个库里的账号;
|
||||
- 迁移只在**打包态**做一次:**目标目录为空(或不存在)且 `<安装目录>\data` 有数据**时,用 `fs.renameSync` 把旧目录整体改名过去(同盘秒完成)。跨盘、权限不足等任何失败都只记日志,旧数据原地保留;
|
||||
- 两边都有数据时**不做任何动作**,只写一条醒目日志提示人工确认,两份都不会被自动删除;
|
||||
- 迁移结果与最终数据目录都写进 `logs/console.log`(搜「数据目录」「已把旧数据从」);
|
||||
- 数据目录若仍在安装目录内,启动时会额外打一条警告。
|
||||
|
||||
改完 `datadir.js` 跑 `node --test main/datadir.test.js`(Node 内置测试运行器,无额外依赖)。
|
||||
|
||||
## 行为说明
|
||||
|
||||
| 场景 | 行为 |
|
||||
@@ -79,6 +100,7 @@ scoreboard-windows-amd64/
|
||||
| 防熄屏 | Console 启动即生效,退出时释放;前台后台都算 |
|
||||
| 服务端就绪 | 首次 `/api/health` 通过后开大屏窗口;等待超过 `display.waitForServerMs` 也先开,避免现场只见桌面 |
|
||||
| 服务端崩溃 | 按 `restartDelayMs` 递增退避自动重启(`maxRestarts=0` 为无限);持续健康 `healthyResetMs` 后崩溃计数清零;恢复健康后自动重载大屏 |
|
||||
| 数据目录 | 默认 `%LOCALAPPDATA%\ScoreboardConsole\data`(安装目录之外,覆盖安装/卸载都不删),启动时按 `-data <绝对路径>` 交给服务端;打包态首次启动做一次性迁移(见「数据目录」一节),开发态不接管 |
|
||||
| 停止服务 | 先 `POST server.shutdownPath` 并带 `X-Shutdown-Token`(启动时用 `crypto.randomBytes(32)` 生成,经环境变量 `SCOREBOARD_SHUTDOWN_TOKEN` 只交给该子进程)请求服务端优雅关闭;受理后最多等 `shutdownTimeoutMs`,超时或接口不可用(404/连接失败)再强杀 |
|
||||
| 已连接大屏实例 | 管理窗口打开时每 5 秒读一次服务端 `GET /api/display/instances`(复用管理面板的 `scoreboard_session` Cookie,需记录员及以上),显示实例数、页面名与在线状态;窗口隐藏即停表,未登录/服务未启动时显示占位文案 |
|
||||
| 关管理窗口 | 只隐藏到托盘,服务端继续运行;退出必须走托盘「退出」 |
|
||||
@@ -86,7 +108,7 @@ scoreboard-windows-amd64/
|
||||
| 窗口外观 | 管理窗口与服务端管理面板窗口都是 `frame: false` 无边框,标题栏与最小化/最大化/关闭三键由页面自绘(拖拽区 `-webkit-app-region: drag`,按钮 `no-drag`,双击标题栏切最大化,关闭键 hover 变红) |
|
||||
| 服务端管理面板 | 用独立无边框 Electron 窗口加载 `http://127.0.0.1:<port>/admin`(`shell-preload.js` 暴露 `window.desktopShell`),不再调系统浏览器;面板页在浏览器里直接打开时标题栏自动不渲染 |
|
||||
| 显示器菜单 | 面板页的「大屏控制 → 显示器」卡片经 `display:list` / `display:open` / `display:close` / `display:relayout` 指挥本机开窗:按屏创建、聚焦、收窗。**全屏只在「多于一块屏 + 非主屏」时放行**,判定在主进程做,页面传 `fullscreen: true` 也会被降级为窗口化;面板页只拿到这几条白名单通道,拿不到 `ipcRenderer` |
|
||||
| 管理员账号 | 主进程 `spawn` 同目录 `scoreboard.exe passwd -config <同一份配置> -user <当前用户名> -new-username <新用户名> -password <新密码>`(留空 = 不改 / 随机生成)。本机进程、不经 HTTP,因此免登录验证;用户名/密码含空格、制表符或换行会被主进程拒绝,密码不进日志。`passwd` 会作废该用户全部会话,UI 提交前二次确认并提示 |
|
||||
| 管理员账号 | 主进程 `spawn` 同目录 `scoreboard.exe passwd -config <同一份配置> -data <同一个数据目录> -user <当前用户名> -new-username <新用户名> -password <新密码>`(留空 = 不改 / 随机生成)。本机进程、不经 HTTP,因此免登录验证;用户名/密码含空格、制表符或换行会被主进程拒绝,密码不进日志。`passwd` 会作废该用户全部会话,UI 提交前二次确认并提示 |
|
||||
| 日志 | 内存环形缓冲(`log.maxLines`)+ 文件 `logs/console.log`(超 `maxFileBytes` 轮转一份 `.1`,轮转前会删掉旧 `.1` 以兼容 Windows) |
|
||||
|
||||
## 配置项(`config.json`)
|
||||
@@ -95,6 +117,7 @@ scoreboard-windows-amd64/
|
||||
| --- | --- | --- |
|
||||
| `server.exe` | `scoreboard.exe` | 相对路径按上文顺序探测,也可写绝对路径 |
|
||||
| `server.args` | `["-config","config.yaml"]` | 传给服务端的参数 |
|
||||
| `server.dataDir` | `""` | 数据目录;空串 = 用默认位置(打包态 `%LOCALAPPDATA%\ScoreboardConsole\data`),填了就按它来(相对路径以安装目录为基准)。Console 会把它作为 `-data <绝对路径>` 追加到服务端参数后 |
|
||||
| `server.host` / `server.port` | `127.0.0.1` / `19540` | 健康检查、关闭握手与管理面板地址(`0.0.0.0` 会自动换成回环地址探测) |
|
||||
| `server.autoStart` | `true` | Console 启动后是否立刻拉起服务端 |
|
||||
| `server.healthIntervalMs` | `5000` | 健康检查间隔 |
|
||||
@@ -147,4 +170,5 @@ pnpm pack # 只出目录,不生成安装包,便于现场调试
|
||||
- `powerSaveBlocker` 在长时间后台下的实际效果。
|
||||
- 托盘图标在 Windows 任务栏的显示效果(16px 缩放)。
|
||||
- `scoreboard.exe` 真实拉起、日志转发、健康检查与崩溃重启的端到端链路。
|
||||
- 数据目录迁移实机路径:老版本安装目录里的 `data\` 是否被整体搬到 `%LOCALAPPDATA%\ScoreboardConsole\data`、跨盘安装(安装目录在 D 盘、`%LOCALAPPDATA%` 在 C 盘)时的失败日志与「旧数据原地保留」表现。
|
||||
- electron-builder 打包已通过 `deploy\build.bat` 实跑;NSIS 安装包在真机上的安装 / 卸载流程尚未验证。
|
||||
Reference in new issue
Block a user