|
|
import fs from 'fs/promises'; |
|
|
import path from 'path'; |
|
|
import { getDataDir } from '../utils/paths.js'; |
|
|
import { FILE_CACHE_TTL } from '../constants/index.js'; |
|
|
import { log } from '../utils/logger.js'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TokenStore { |
|
|
constructor(filePath = path.join(getDataDir(), 'accounts.json')) { |
|
|
this.filePath = filePath; |
|
|
this._cache = null; |
|
|
this._cacheTime = 0; |
|
|
this._cacheTTL = FILE_CACHE_TTL; |
|
|
} |
|
|
|
|
|
async _ensureFileExists() { |
|
|
const dir = path.dirname(this.filePath); |
|
|
try { |
|
|
await fs.mkdir(dir, { recursive: true }); |
|
|
} catch (e) { |
|
|
|
|
|
} |
|
|
|
|
|
try { |
|
|
await fs.access(this.filePath); |
|
|
} catch (e) { |
|
|
|
|
|
await fs.writeFile(this.filePath, '[]', 'utf8'); |
|
|
log.info('✓ 已创建账号配置文件'); |
|
|
} |
|
|
} |
|
|
|
|
|
_isCacheValid() { |
|
|
if (!this._cache) return false; |
|
|
const now = Date.now(); |
|
|
return (now - this._cacheTime) < this._cacheTTL; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async readAll() { |
|
|
if (this._isCacheValid()) { |
|
|
return this._cache; |
|
|
} |
|
|
|
|
|
await this._ensureFileExists(); |
|
|
try { |
|
|
const data = await fs.readFile(this.filePath, 'utf8'); |
|
|
const parsed = JSON.parse(data || '[]'); |
|
|
if (!Array.isArray(parsed)) { |
|
|
log.warn('账号配置文件格式异常,已重置为空数组'); |
|
|
this._cache = []; |
|
|
} else { |
|
|
this._cache = parsed; |
|
|
} |
|
|
} catch (error) { |
|
|
log.error('读取账号配置文件失败:', error.message); |
|
|
this._cache = []; |
|
|
} |
|
|
this._cacheTime = Date.now(); |
|
|
return this._cache; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async writeAll(tokens) { |
|
|
await this._ensureFileExists(); |
|
|
const normalized = Array.isArray(tokens) ? tokens : []; |
|
|
try { |
|
|
await fs.writeFile(this.filePath, JSON.stringify(normalized, null, 2), 'utf8'); |
|
|
this._cache = normalized; |
|
|
this._cacheTime = Date.now(); |
|
|
} catch (error) { |
|
|
log.error('保存账号配置文件失败:', error.message); |
|
|
throw error; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async mergeActiveTokens(activeTokens, tokenToUpdate = null) { |
|
|
const allTokens = [...await this.readAll()]; |
|
|
|
|
|
const applyUpdate = (targetToken) => { |
|
|
if (!targetToken) return; |
|
|
const index = allTokens.findIndex(t => t.refresh_token === targetToken.refresh_token); |
|
|
if (index !== -1) { |
|
|
const { sessionId, ...plain } = targetToken; |
|
|
allTokens[index] = { ...allTokens[index], ...plain }; |
|
|
} |
|
|
}; |
|
|
|
|
|
if (tokenToUpdate) { |
|
|
applyUpdate(tokenToUpdate); |
|
|
} else if (Array.isArray(activeTokens) && activeTokens.length > 0) { |
|
|
for (const memToken of activeTokens) { |
|
|
applyUpdate(memToken); |
|
|
} |
|
|
} |
|
|
|
|
|
await this.writeAll(allTokens); |
|
|
} |
|
|
} |
|
|
|
|
|
export default TokenStore; |
|
|
|