Qwen2API-A / src /utils /data-persistence.js
github-actions[bot]
Sync from GitHub Viciy2023/Qwen2API-A@ae093476e9bc5b0a599620b5925df3a20057038e
f120063
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