Spaces:
Paused
Paused
File size: 5,873 Bytes
146bdba |
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 |
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import { createLogger } from './logger.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const logger = createLogger('Storage');
/**
* 持久化存储管理器
* 用于保存和加载Cookie数据(包括Thread ID)
*/
class StorageManager {
constructor() {
// 数据文件路径
this.dataFilePath = path.join(dirname(dirname(__dirname)), 'data', 'cookies-data.json');
this.backupFilePath = path.join(dirname(dirname(__dirname)), 'data', 'cookies-data.backup.json');
// 确保数据目录存在
this.ensureDataDirectory();
}
/**
* 确保数据目录存在
*/
ensureDataDirectory() {
const dataDir = path.dirname(this.dataFilePath);
if (!fs.existsSync(dataDir)) {
fs.mkdirSync(dataDir, { recursive: true });
logger.info(`创建数据目录: ${dataDir}`);
}
}
/**
* 保存Cookie数据到文件
* @param {Array} cookieEntries - Cookie条目数组
* @returns {boolean} - 是否保存成功
*/
saveCookieData(cookieEntries) {
try {
// 准备要保存的数据
const dataToSave = {
version: '1.0',
lastUpdated: new Date().toISOString(),
cookies: cookieEntries.map(entry => ({
userId: entry.userId,
spaceId: entry.spaceId,
threadId: entry.threadId,
enabled: entry.enabled,
valid: entry.valid,
lastUsed: entry.lastUsed,
// 不保存实际的cookie值,只保存其哈希或标识
cookieHash: this.hashCookie(entry.cookie)
}))
};
// 先备份现有文件
if (fs.existsSync(this.dataFilePath)) {
fs.copyFileSync(this.dataFilePath, this.backupFilePath);
}
// 保存新数据
fs.writeFileSync(this.dataFilePath, JSON.stringify(dataToSave, null, 2), 'utf8');
logger.info(`成功保存 ${cookieEntries.length} 个Cookie的数据`);
return true;
} catch (error) {
logger.error(`保存Cookie数据失败: ${error.message}`);
return false;
}
}
/**
* 加载Cookie数据
* @returns {Object|null} - 加载的数据或null
*/
loadCookieData() {
try {
if (!fs.existsSync(this.dataFilePath)) {
logger.info('Cookie数据文件不存在');
return null;
}
const fileContent = fs.readFileSync(this.dataFilePath, 'utf8');
const data = JSON.parse(fileContent);
logger.info(`成功加载 ${data.cookies?.length || 0} 个Cookie的数据`);
return data;
} catch (error) {
logger.error(`加载Cookie数据失败: ${error.message}`);
// 尝试从备份恢复
if (fs.existsSync(this.backupFilePath)) {
try {
logger.info('尝试从备份文件恢复...');
const backupContent = fs.readFileSync(this.backupFilePath, 'utf8');
const backupData = JSON.parse(backupContent);
// 将备份恢复为主文件
fs.copyFileSync(this.backupFilePath, this.dataFilePath);
logger.success('成功从备份恢复数据');
return backupData;
} catch (backupError) {
logger.error(`从备份恢复失败: ${backupError.message}`);
}
}
return null;
}
}
/**
* 合并保存的数据和内存中的Cookie条目
* @param {Array} cookieEntries - 内存中的Cookie条目
* @param {Object} savedData - 保存的数据
* @returns {Array} - 合并后的Cookie条目
*/
mergeCookieData(cookieEntries, savedData) {
if (!savedData || !savedData.cookies) {
return cookieEntries;
}
const mergedEntries = [];
// 为每个内存中的cookie条目恢复保存的数据
for (const entry of cookieEntries) {
const savedEntry = savedData.cookies.find(saved =>
saved.userId === entry.userId ||
saved.cookieHash === this.hashCookie(entry.cookie)
);
if (savedEntry) {
// 恢复保存的数据
entry.threadId = savedEntry.threadId || entry.threadId;
entry.enabled = savedEntry.enabled !== undefined ? savedEntry.enabled : entry.enabled;
entry.lastUsed = savedEntry.lastUsed || entry.lastUsed;
logger.info(`恢复用户 ${entry.userId} 的数据: threadId=${entry.threadId}`);
}
mergedEntries.push(entry);
}
return mergedEntries;
}
/**
* 生成Cookie的哈希值(用于匹配,不存储实际cookie)
* @param {string} cookie - Cookie字符串
* @returns {string} - 哈希值
*/
hashCookie(cookie) {
if (!cookie) return '';
// 简单的哈希实现,取cookie的前20个字符和后20个字符
const prefix = cookie.substring(0, 20);
const suffix = cookie.substring(Math.max(0, cookie.length - 20));
return `${prefix}...${suffix}`;
}
/**
* 清理过期数据
* @param {number} daysToKeep - 保留多少天的数据
*/
cleanupOldData(daysToKeep = 30) {
try {
const dataDir = path.dirname(this.dataFilePath);
const files = fs.readdirSync(dataDir);
const now = Date.now();
const maxAge = daysToKeep * 24 * 60 * 60 * 1000;
files.forEach(file => {
if (file.startsWith('cookies-data') && file.endsWith('.backup.json')) {
const filePath = path.join(dataDir, file);
const stats = fs.statSync(filePath);
if (now - stats.mtime.getTime() > maxAge) {
fs.unlinkSync(filePath);
logger.info(`删除过期备份文件: ${file}`);
}
}
});
} catch (error) {
logger.error(`清理过期数据失败: ${error.message}`);
}
}
}
export const storageManager = new StorageManager();
|