升级版本并改用内部账号存储

This commit is contained in:
yml2213
2026-08-09 13:04:54 +08:00
parent 1cd94c0179
commit 8cf29598a5
13 changed files with 271 additions and 54 deletions
+71 -8
View File
@@ -15,15 +15,43 @@ import (
)
// DefaultAppPaths 收集运行时路径,支持环境变量覆盖。
// 所有相对路径会基于当前工作目录解析为绝对路径,避免打包后工作目录不同导致路径错位。
// 默认数据存放在当前用户的应用配置目录,而非工作目录,避免 macOS 应用包目录无写权限。
func DefaultAppPaths() types.AppPaths {
return types.AppPaths{
ConfigFile: resolvePath(envOrDefault("DOUYU_CONFIG", constants.DefaultConfigFile)),
AccountFile: resolvePath(envOrDefault("DOUYU_USERS", constants.DefaultAccountFile)),
LogDir: resolvePath(envOrDefault("DOUYU_LOG_DIR", constants.DefaultLogDir)),
SuccessDir: resolvePath(envOrDefault("DOUYU_SUCCESS_DIR", constants.DefaultSuccessDir)),
FontFiles: imgpkg.CollectFontCandidates(),
dataDir := resolvePath(envOrDefault("DOUYU_DATA_DIR", defaultAppDataDir()))
configFile := filepath.Join(dataDir, constants.DefaultConfigFile)
accountFile := filepath.Join(dataDir, constants.DefaultAccountFile)
legacyConfigFile := resolvePath(constants.LegacyDefaultConfigFile)
legacyAccountFile := resolvePath(constants.LegacyDefaultAccountFile)
if value := os.Getenv("DOUYU_CONFIG"); value != "" {
configFile = resolvePath(value)
legacyConfigFile = ""
}
if value := os.Getenv("DOUYU_USERS"); value != "" {
accountFile = resolvePath(value)
legacyAccountFile = ""
}
return types.AppPaths{
DataDir: dataDir,
ConfigFile: configFile,
AccountFile: accountFile,
LegacyConfigFile: legacyConfigFile,
LegacyAccountFile: legacyAccountFile,
LogDir: resolvePath(envOrDefault("DOUYU_LOG_DIR", filepath.Join(dataDir, constants.DefaultLogDir))),
SuccessDir: resolvePath(envOrDefault("DOUYU_SUCCESS_DIR", filepath.Join(dataDir, constants.DefaultSuccessDir))),
FontFiles: imgpkg.CollectFontCandidates(),
}
}
func defaultAppDataDir() string {
if dir, err := os.UserConfigDir(); err == nil && dir != "" {
return filepath.Join(dir, constants.DefaultAppDataDirName)
}
if home, err := os.UserHomeDir(); err == nil && home != "" {
return filepath.Join(home, "."+constants.DefaultAppDataDirName)
}
return "." + constants.DefaultAppDataDirName
}
// resolvePath 将相对路径解析为绝对路径。
@@ -44,6 +72,38 @@ func envOrDefault(key, fallback string) string {
return fallback
}
// MigrateLegacyFile 在目标文件不存在时复制旧版本数据,不删除旧文件。
// 返回值表示是否完成了迁移。
func MigrateLegacyFile(target, legacy string) (bool, error) {
if target == "" || legacy == "" || samePath(target, legacy) {
return false, nil
}
if _, err := os.Stat(target); err == nil {
return false, nil
} else if !errors.Is(err, os.ErrNotExist) {
return false, err
}
content, err := os.ReadFile(legacy)
if errors.Is(err, os.ErrNotExist) {
return false, nil
}
if err != nil {
return false, err
}
if err := os.MkdirAll(filepath.Dir(target), 0700); err != nil {
return false, err
}
if err := os.WriteFile(target, content, 0600); err != nil {
return false, err
}
return true, nil
}
func samePath(left, right string) bool {
return filepath.Clean(left) == filepath.Clean(right)
}
// LoadTraceConfig 读取 trace 开关;默认关闭,避免保存敏感调试数据。
func LoadTraceConfig() types.TraceConfig {
return types.TraceConfig{
@@ -160,5 +220,8 @@ func SaveConfig(path string, cfg types.Config) error {
if err != nil {
return err
}
return os.WriteFile(path, data, 0644)
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
return err
}
return os.WriteFile(path, data, 0600)
}
+80
View File
@@ -0,0 +1,80 @@
package config
import (
"os"
"path/filepath"
"testing"
)
func TestMigrateLegacyFile(t *testing.T) {
dir := t.TempDir()
legacy := filepath.Join(dir, "legacy", "config.json")
target := filepath.Join(dir, "internal", "config.json")
content := []byte(`{"activity":"xiaodian"}`)
if err := os.MkdirAll(filepath.Dir(legacy), 0700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(legacy, content, 0600); err != nil {
t.Fatal(err)
}
migrated, err := MigrateLegacyFile(target, legacy)
if err != nil {
t.Fatalf("迁移失败: %v", err)
}
if !migrated {
t.Fatal("应迁移旧文件")
}
got, err := os.ReadFile(target)
if err != nil {
t.Fatal(err)
}
if string(got) != string(content) {
t.Fatalf("迁移内容 = %q, want %q", got, content)
}
if _, err := os.Stat(legacy); err != nil {
t.Fatalf("旧文件不应被删除: %v", err)
}
if err := os.WriteFile(legacy, []byte(`{"activity":"other"}`), 0600); err != nil {
t.Fatal(err)
}
migrated, err = MigrateLegacyFile(target, legacy)
if err != nil {
t.Fatalf("重复迁移失败: %v", err)
}
if migrated {
t.Fatal("目标已存在时不应覆盖迁移")
}
got, err = os.ReadFile(target)
if err != nil {
t.Fatal(err)
}
if string(got) != string(content) {
t.Fatalf("目标文件被意外覆盖: %q", got)
}
}
func TestDefaultAppPathsUseInternalDataDir(t *testing.T) {
dir := t.TempDir()
t.Setenv("DOUYU_DATA_DIR", dir)
t.Setenv("DOUYU_CONFIG", "")
t.Setenv("DOUYU_USERS", "")
t.Setenv("DOUYU_LOG_DIR", "")
t.Setenv("DOUYU_SUCCESS_DIR", "")
paths := DefaultAppPaths()
if paths.DataDir != dir {
t.Fatalf("DataDir = %q, want %q", paths.DataDir, dir)
}
if paths.ConfigFile != filepath.Join(dir, "config.json") {
t.Fatalf("ConfigFile = %q", paths.ConfigFile)
}
if paths.AccountFile != filepath.Join(dir, "accounts.txt") {
t.Fatalf("AccountFile = %q", paths.AccountFile)
}
if paths.LogDir != filepath.Join(dir, "logs") || paths.SuccessDir != filepath.Join(dir, "success_images") {
t.Fatalf("应用数据子目录异常: logs=%q success=%q", paths.LogDir, paths.SuccessDir)
}
}