const fs = require('fs').promises const path = require('path') const config = require('../config/index.js') const redisClient = require('./redis') const { logger } = require('./logger') /** * 数据持久化管理器 * 统一处理账户数据的存储和读取 */ class DataPersistence { constructor() { this.dataFilePath = config.dataFilePath } /** * 加载所有账户数据 * @returns {Promise} 账户列表 */ async loadAccounts() { try { switch (config.dataSaveMode) { case 'redis': return await this._loadFromRedis() case 'file': return await this._loadFromFile() case 'none': return await this._loadFromEnv() default: logger.error(`不支持的数据保存模式: ${config.dataSaveMode}`, 'DATA') throw new Error(`不支持的数据保存模式: ${config.dataSaveMode}`) } } catch (error) { logger.error('加载账户数据失败', 'DATA', '', error) return [] } } /** * 保存单个账户数据 * @param {string} email - 邮箱 * @param {Object} accountData - 账户数据 * @returns {Promise} 保存是否成功 */ async saveAccount(email, accountData) { try { switch (config.dataSaveMode) { case 'redis': return await this._saveToRedis(email, accountData) case 'file': return await this._saveToFile(email, accountData) case 'none': logger.warn('环境变量模式不支持保存账户数据', 'DATA') return false default: logger.error(`不支持的数据保存模式: ${config.dataSaveMode}`, 'DATA') throw new Error(`不支持的数据保存模式: ${config.dataSaveMode}`) } } catch (error) { logger.error(`保存账户数据失败 (${email})`, 'DATA', '', error) return false } } /** * 批量保存账户数据 * @param {Array} accounts - 账户列表 * @returns {Promise} 保存是否成功 */ async saveAllAccounts(accounts) { try { switch (config.dataSaveMode) { case 'redis': return await this._saveAllToRedis(accounts) case 'file': return await this._saveAllToFile(accounts) case 'none': logger.warn('环境变量模式不支持保存账户数据', 'DATA') return false default: logger.error(`不支持的数据保存模式: ${config.dataSaveMode}`, 'DATA') throw new Error(`不支持的数据保存模式: ${config.dataSaveMode}`) } } catch (error) { logger.error('批量保存账户数据失败', 'DATA', '', error) return false } } /** * 从 Redis 加载账户数据 * @private */ async _loadFromRedis() { const accounts = await redisClient.getAllAccounts() return accounts.length > 0 ? accounts : [] } /** * 从文件加载账户数据 * @private */ async _loadFromFile() { // 确保文件存在 await this._ensureDataFileExists() const fileContent = await fs.readFile(this.dataFilePath, 'utf-8') const data = JSON.parse(fileContent) return data.accounts || [] } /** * 从环境变量加载账户数据 * @private */ async _loadFromEnv() { if (!process.env.ACCOUNTS) { return [] } const { JwtDecode } = require('./tools') const accountTokens = process.env.ACCOUNTS.split(',') const accounts = [] for (const item of accountTokens) { const [email, password] = item.split(':') if (email && password) { // 注意:这里需要登录获取token,但在加载阶段不应该进行网络请求 // 这个逻辑需要在Account类中处理 accounts.push({ email, password, token: null, expires: null }) } } return accounts } /** * 保存到 Redis * @private */ async _saveToRedis(email, accountData) { return await redisClient.setAccount(email, accountData) } /** * 保存到文件 * @private */ async _saveToFile(email, accountData) { await this._ensureDataFileExists() const fileContent = await fs.readFile(this.dataFilePath, 'utf-8') const data = JSON.parse(fileContent) if (!data.accounts) { data.accounts = [] } // 查找现有账户或添加新账户 const existingIndex = data.accounts.findIndex(account => account.email === email) const updatedAccount = { email, password: accountData.password, token: accountData.token, expires: accountData.expires } if (existingIndex !== -1) { data.accounts[existingIndex] = updatedAccount } else { data.accounts.push(updatedAccount) } await fs.writeFile(this.dataFilePath, JSON.stringify(data, null, 2), 'utf-8') return true } /** * 批量保存到 Redis * @private */ async _saveAllToRedis(accounts) { let successCount = 0 for (const account of accounts) { const success = await this._saveToRedis(account.email, account) if (success) successCount++ } return successCount === accounts.length } /** * 批量保存到文件 * @private */ async _saveAllToFile(accounts) { await this._ensureDataFileExists() const fileContent = await fs.readFile(this.dataFilePath, 'utf-8') const data = JSON.parse(fileContent) data.accounts = accounts.map(account => ({ email: account.email, password: account.password, token: account.token, expires: account.expires })) await fs.writeFile(this.dataFilePath, JSON.stringify(data, null, 2), 'utf-8') return true } /** * 确保数据文件存在 * @private */ async _ensureDataFileExists() { try { await fs.access(this.dataFilePath) } catch (error) { logger.info('数据文件不存在,正在创建默认文件...', 'FILE', '📁') // 确保目录存在 const dirPath = path.dirname(this.dataFilePath) await fs.mkdir(dirPath, { recursive: true }) // 创建默认数据结构 const defaultData = { defaultHeaders: null, defaultCookie: null, accounts: [] } await fs.writeFile(this.dataFilePath, JSON.stringify(defaultData, null, 2), 'utf-8') logger.success('默认数据文件创建成功', 'FILE') } } } module.exports = DataPersistence