Spaces:
Running
Running
File size: 6,451 Bytes
f120063 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | 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<Array>} 账户列表
*/
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<boolean>} 保存是否成功
*/
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<boolean>} 保存是否成功
*/
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
|