升级版本并改用内部账号存储
This commit is contained in:
@@ -8,26 +8,32 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"douyu_go/internal/config"
|
||||
"douyu_go/internal/constants"
|
||||
"douyu_go/internal/logger"
|
||||
"douyu_go/internal/types"
|
||||
)
|
||||
|
||||
// AccountStore 账号存储,负责从账号文件加载、查询账号信息。
|
||||
// AccountStore 账号存储,负责从应用内部账号库加载、查询账号信息。
|
||||
// 账号文件格式:每行 `账号名----密码----Cookie`(密码段保留以兼容历史文件,
|
||||
// 但程序不读取也不使用密码,鉴权完全基于 Cookie)。
|
||||
type AccountStore struct {
|
||||
mu sync.RWMutex
|
||||
accounts []types.Account
|
||||
filePath string
|
||||
mu sync.RWMutex
|
||||
accounts []types.Account
|
||||
filePath string
|
||||
legacyFilePath string
|
||||
}
|
||||
|
||||
// NewAccountStore 创建账号存储,filePath 为账号文件路径。
|
||||
func NewAccountStore(filePath string) *AccountStore {
|
||||
// NewAccountStore 创建账号存储。legacyFilePaths 仅在内部账号库首次创建时用于迁移旧文件。
|
||||
func NewAccountStore(filePath string, legacyFilePaths ...string) *AccountStore {
|
||||
if filePath == "" {
|
||||
filePath = constants.DefaultAccountFile
|
||||
}
|
||||
return &AccountStore{filePath: filePath}
|
||||
legacyFilePath := ""
|
||||
if len(legacyFilePaths) > 0 {
|
||||
legacyFilePath = legacyFilePaths[0]
|
||||
}
|
||||
return &AccountStore{filePath: filePath, legacyFilePath: legacyFilePath}
|
||||
}
|
||||
|
||||
// FilePath 返回账号文件路径。
|
||||
@@ -35,23 +41,33 @@ func (a *AccountStore) FilePath() string {
|
||||
return a.filePath
|
||||
}
|
||||
|
||||
// Load 从账号文件加载账号。
|
||||
// Load 从应用内部账号库加载账号。
|
||||
func (a *AccountStore) Load() {
|
||||
path := a.filePath
|
||||
|
||||
// 文件不存在时自动创建目录和空文件,方便用户直接粘贴 Cookie
|
||||
// 首次启动时迁移旧版本工作目录中的账号文件,保留原文件作为备份。
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
migrated, migrateErr := config.MigrateLegacyFile(path, a.legacyFilePath)
|
||||
if migrateErr != nil {
|
||||
logger.LogEvent("账号", "warn", "旧账号文件迁移失败: %s: %v", a.legacyFilePath, migrateErr)
|
||||
} else if migrated {
|
||||
logger.LogEvent("账号", "success", "已迁移旧账号文件到应用内部目录: %s", path)
|
||||
}
|
||||
}
|
||||
|
||||
// 应用内部账号库不存在时自动创建目录和空文件,方便用户直接在界面导入。
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
if dir := filepath.Dir(path); dir != "" && dir != "." {
|
||||
if mkErr := os.MkdirAll(dir, 0755); mkErr != nil {
|
||||
if mkErr := os.MkdirAll(dir, 0700); mkErr != nil {
|
||||
logger.LogEvent("账号", "warn", "账号目录创建失败: %s: %v", dir, mkErr)
|
||||
}
|
||||
}
|
||||
// 使用 Windows 换行格式 \r\n
|
||||
hint := "# 格式: 账号名----密码----Cookie\r\n# 每行一个账号,粘贴后保存即可\r\n"
|
||||
if wrErr := os.WriteFile(path, []byte(hint), 0644); wrErr != nil {
|
||||
if wrErr := os.WriteFile(path, []byte(hint), 0600); wrErr != nil {
|
||||
logger.LogEvent("账号", "warn", "账号文件创建失败: %s: %v", path, wrErr)
|
||||
} else {
|
||||
logger.LogEvent("账号", "info", "已创建空账号文件: %s,请粘贴 Cookie 后重新加载", path)
|
||||
logger.LogEvent("账号", "info", "已创建空账号库: %s,请在 CK 管理中导入账号", path)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,13 +85,16 @@ func (a *AccountStore) Load() {
|
||||
normalized, converted := normalizeLineEndings(content)
|
||||
if converted {
|
||||
// 保存转换后的内容
|
||||
if err := os.WriteFile(path, normalized, 0644); err != nil {
|
||||
if err := os.WriteFile(path, normalized, 0600); err != nil {
|
||||
logger.LogEvent("账号", "warn", "账号文件换行格式转换失败: %s: %v", path, err)
|
||||
} else {
|
||||
logger.LogEvent("账号", "info", "账号文件已自动转换为 Windows 换行格式: %s", path)
|
||||
}
|
||||
content = normalized
|
||||
}
|
||||
if err := os.Chmod(path, 0600); err != nil {
|
||||
logger.LogEvent("账号", "warn", "账号库权限收紧失败: %s: %v", path, err)
|
||||
}
|
||||
|
||||
var list []types.Account
|
||||
scanner := bufio.NewScanner(bytes.NewReader(content))
|
||||
@@ -102,7 +121,7 @@ func (a *AccountStore) Load() {
|
||||
a.mu.Lock()
|
||||
a.accounts = list
|
||||
a.mu.Unlock()
|
||||
logger.LogEvent("账号", "success", "账号加载完成: %d 个,来源=%s", len(list), path)
|
||||
logger.LogEvent("账号", "success", "账号加载完成: %d 个,内部存储=%s", len(list), path)
|
||||
}
|
||||
|
||||
// Count 返回当前账号数。
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package seckill
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAccountStoreMigratesLegacyFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
legacy := filepath.Join(dir, "data", "user.txt")
|
||||
target := filepath.Join(dir, "app-data", "accounts.txt")
|
||||
content := []byte("# 旧账号\r\nfoo----password----cookie-a\r\n")
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
store := NewAccountStore(target, legacy)
|
||||
store.Load()
|
||||
accounts := store.Snapshot()
|
||||
if len(accounts) != 1 || accounts[0].Name != "foo" || accounts[0].Cookie != "cookie-a" {
|
||||
t.Fatalf("迁移后的账号 = %#v", accounts)
|
||||
}
|
||||
if _, err := os.Stat(legacy); err != nil {
|
||||
t.Fatalf("旧账号文件不应被删除: %v", err)
|
||||
}
|
||||
info, err := os.Stat(target)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if info.Mode().Perm() != 0600 {
|
||||
t.Fatalf("账号库权限 = %o, want 0600", info.Mode().Perm())
|
||||
}
|
||||
}
|
||||
@@ -129,7 +129,7 @@ func New(cli *client.DouyuClient, paths types.AppPaths, font *truetype.Font, con
|
||||
client: cli,
|
||||
paths: paths,
|
||||
font: font,
|
||||
accounts: NewAccountStore(paths.AccountFile),
|
||||
accounts: NewAccountStore(paths.AccountFile, paths.LegacyAccountFile),
|
||||
successImages: image.NewSuccessImageSaver(
|
||||
font,
|
||||
paths.SuccessDir,
|
||||
@@ -260,7 +260,7 @@ func normalizeSeckillMode(mode types.SeckillMode) types.SeckillMode {
|
||||
|
||||
// ==================== 账号转发(AccountStore) ====================
|
||||
|
||||
// LoadAccounts 从账号文件加载账号。
|
||||
// LoadAccounts 从应用内部账号库加载账号。
|
||||
func (s *Scheduler) LoadAccounts() {
|
||||
s.accounts.Load()
|
||||
}
|
||||
@@ -270,7 +270,7 @@ func (s *Scheduler) AccountCount() int {
|
||||
return s.accounts.Count()
|
||||
}
|
||||
|
||||
// AccountFilePath 返回账号文件路径。
|
||||
// AccountFilePath 返回内部账号库路径。
|
||||
func (s *Scheduler) AccountFilePath() string {
|
||||
return s.accounts.FilePath()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user