Files
Scoreboard/console/main/datadir.js
T
AskaEth 407abec3b0 fix: 大屏背景图被兜底渐变盖住 + 配置/规则也搬到用户数据目录
1) 大屏背景图不生效:App.vue 里 .bg 与 .bg-fallback 都是 absolute inset:0,
   而兜底渐变是不透明的且在图片之后绘制 → 配了背景图也只看得到渐变。
   改成 v-if/v-else 互斥(用无头 Chromium 真渲染对照验证:修复后取到图片像素,
   回退版本只剩渐变)。新增 background-layer.test.ts 锁住层次关系。

2) 规则/config 仍在安装目录,覆盖安装/卸载会连配置一起删:现在三样都落在
   %LOCALAPPDATA%\ScoreboardConsole\(与数据目录同一基座):
   - console\config.json(Console 读写都用这份)
   - config.yaml(Console 用绝对路径 -config 传给服务端与 passwd)
   - rules\(首次从安装目录复制过去,之后只认用户目录那份)
   首次运行复制迁移、已存在不覆盖、不可写则回落安装目录并记日志(不影响启动);
   开发态行为不变。备份脚本与文档同步。
   注意 resolveExecutable 必须显式传部署包根目录,否则会去用户目录找 scoreboard.exe。

自检:服务端 11 包全绿;console 语法 16 文件 + node:test 20/20;
前端 vue-tsc + build、node --test 69/69。
2026-09-10 10:06:21 +08:00

114 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict'
const fs = require('fs')
const path = require('path')
// 数据目录必须落在安装目录之外:electron-builder 的卸载器结尾是 `RMDir /r $INSTDIR`,
// 而覆盖安装会先静默跑一遍旧版卸载器 → 装在安装目录里的 data/ 会被整体删掉。
// 也不能放 %APPDATA%\<AppName>(卸载器同样会删),所以默认落在 %LOCALAPPDATA%\ScoreboardConsole\data。
// 配置与规则目录(userpaths.js)用同一个基座,这里导出给它复用。
const LOCAL_BASE = 'ScoreboardConsole'
const DATA_SUBDIR = 'data'
// 解析最终数据目录。installDir 是安装目录(打包后 = exe 所在目录),
// 相对路径按安装目录解析,现场写 `data`、`D:\scoreboard-data` 都能用。
// passToServer=false 表示不给服务端加 -data,沿用服务端自身默认(开发态)。
function resolveDataDir({ values, installDir, env, isPackaged }) {
const base = path.resolve(installDir || '.')
const configured = values && values.server ? values.server.dataDir : ''
if (typeof configured === 'string' && configured.trim() !== '') {
const value = configured.trim()
return {
dir: path.isAbsolute(value) ? path.resolve(value) : path.resolve(base, value),
source: 'config',
passToServer: true
}
}
// 开发态不接管:继续用服务端默认(工作目录下的 data),免得仓库里的 data/ 被顶掉
if (!isPackaged) {
return { dir: path.join(base, DATA_SUBDIR), source: 'default', passToServer: false }
}
const localAppData = env ? env.LOCALAPPDATA : ''
if (!localAppData) {
// 拿不到 %LOCALAPPDATA% 时退回老位置:这次覆盖安装仍有丢数据风险,
// 但总好过把数据指向一个猜出来的路径
return { dir: path.join(base, DATA_SUBDIR), source: 'fallback', passToServer: false }
}
return {
dir: path.join(localAppData, LOCAL_BASE, DATA_SUBDIR),
source: 'localappdata',
passToServer: true
}
}
// 判断 dir 是否落在 base 之内(含 base 本身):用于提醒「数据还在安装目录里」,
// 这种配置在覆盖安装时会被卸载器连目录一起删掉
function isInside(base, dir) {
const rel = path.relative(path.resolve(base), path.resolve(dir))
return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel))
}
// missing = 不存在,empty = 存在但空,has-data = 有内容
// 读不出来时返回 error,由调用方按「可能有数据」处理,宁可不迁移
function dirState(dir) {
let entries
try {
entries = fs.readdirSync(dir)
} catch (err) {
return err.code === 'ENOENT' ? 'missing' : 'error'
}
return entries.length > 0 ? 'has-data' : 'empty'
}
// 一次性迁移:只在「目标为空且旧目录有数据」时把旧目录整体改名过去。
// 任何一步失败都只记日志、旧数据保持原地,绝不删除数据;
// 两边都有数据时不做任何动作,只提示现场人工确认。
function migrateLegacyData({ targetDir, legacyDir, log }) {
const notify = typeof log === 'function' ? log : () => {}
if (!targetDir || !legacyDir) return { migrated: false, reason: 'invalid' }
if (path.resolve(targetDir) === path.resolve(legacyDir)) return { migrated: false, reason: 'same-dir' }
const target = dirState(targetDir)
const legacy = dirState(legacyDir)
if (target === 'error') {
notify(`警告:读不到数据目录 ${targetDir},跳过旧数据迁移;旧数据仍在 ${legacyDir}`)
return { migrated: false, reason: 'target-unreadable' }
}
if (target === 'has-data') {
if (legacy === 'has-data') {
notify(
`警告:旧数据目录 ${legacyDir} 与新数据目录 ${targetDir} 都有数据,未做迁移。` +
'请人工确认要保留哪一份(两份都不会被自动删除)'
)
return { migrated: false, reason: 'both' }
}
return { migrated: false, reason: 'target-has-data' }
}
if (legacy !== 'has-data') return { migrated: false, reason: 'no-legacy' }
try {
// Windows 上 rename 到已存在的目录会失败,先摘掉这个空目录(空目录里没有数据)
if (target === 'empty') fs.rmdirSync(targetDir)
fs.mkdirSync(path.dirname(targetDir), { recursive: true })
fs.renameSync(legacyDir, targetDir)
} catch (err) {
notify(
`警告:迁移旧数据失败(${err.message}),旧数据仍保留在 ${legacyDir};` +
`本次服务端将使用 ${targetDir},请人工把旧目录内容拷过去`
)
return { migrated: false, reason: 'failed' }
}
notify(`已把旧数据从 ${legacyDir} 迁移到 ${targetDir}`)
return { migrated: true, reason: 'moved' }
}
module.exports = { resolveDataDir, migrateLegacyData, isInside, LOCAL_BASE }