根因:数据目录是 <安装目录>\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。
66 lines
2.0 KiB
Go
66 lines
2.0 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestDefaultAllowedOrigins(t *testing.T) {
|
|
if got := Default().Server.AllowedOrigins; len(got) != 0 {
|
|
t.Fatalf("默认白名单应为空: %v", got)
|
|
}
|
|
}
|
|
|
|
func TestLoadAllowedOrigins(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "config.yaml")
|
|
raw := "server:\n allowedOrigins:\n - http://a.example\n - https://b.example\n"
|
|
if err := os.WriteFile(path, []byte(raw), 0o644); err != nil {
|
|
t.Fatalf("写入配置: %v", err)
|
|
}
|
|
|
|
cfg, err := Load(path)
|
|
if err != nil {
|
|
t.Fatalf("加载配置: %v", err)
|
|
}
|
|
want := []string{"http://a.example", "https://b.example"}
|
|
if !reflect.DeepEqual(cfg.Server.AllowedOrigins, want) {
|
|
t.Fatalf("白名单解析错误: got %v want %v", cfg.Server.AllowedOrigins, want)
|
|
}
|
|
}
|
|
|
|
// -data 只在非空时覆盖配置:不传与传空串都必须保持配置文件里的值,
|
|
// 否则 Console 之外的便携/手工运行会被改写数据目录。
|
|
func TestApplyDataDir(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
dir string
|
|
want string
|
|
applied bool
|
|
}{
|
|
{"非空覆盖配置值", `/var/lib/scoreboard`, `/var/lib/scoreboard`, true},
|
|
{"两侧空白会被去掉", " /var/lib/scoreboard ", `/var/lib/scoreboard`, true},
|
|
{"空串保持配置值", "", "cfg-data", false},
|
|
{"纯空白等于不传", " ", "cfg-data", false},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "config.yaml")
|
|
if err := os.WriteFile(path, []byte("dataDir: cfg-data\nserver:\n port: 19540\n"), 0o644); err != nil {
|
|
t.Fatalf("写入配置: %v", err)
|
|
}
|
|
cfg, err := Load(path)
|
|
if err != nil {
|
|
t.Fatalf("加载配置: %v", err)
|
|
}
|
|
if got := cfg.ApplyDataDir(tc.dir); got != tc.applied {
|
|
t.Fatalf("ApplyDataDir(%q) 覆盖标记: got %v want %v", tc.dir, got, tc.applied)
|
|
}
|
|
if cfg.DataDir != tc.want {
|
|
t.Fatalf("ApplyDataDir(%q) 后 DataDir: got %q want %q", tc.dir, cfg.DataDir, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|