File size: 5,873 Bytes
825e3f3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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();