| const config = require('../config/config'); |
| const fs = require('fs'); |
| const path = require('path'); |
|
|
| |
| const INVALID_COOKIES_FILE = path.join(__dirname, '../../data/invalid_cookies.json'); |
| |
| const API_KEYS_FILE = path.join(__dirname, '../../data/api_keys.json'); |
|
|
| |
| function ensureDataDirExists() { |
| const dataDir = path.join(__dirname, '../../data'); |
| if (!fs.existsSync(dataDir)) { |
| try { |
| fs.mkdirSync(dataDir, { recursive: true }); |
| console.log('创建data目录成功'); |
| } catch (err) { |
| console.error('创建data目录失败:', err); |
| } |
| } |
| } |
|
|
| |
| let apiKeyMap = new Map(); |
|
|
| |
| let rotationIndexes = new Map(); |
|
|
| |
| let invalidCookies = new Set(); |
|
|
| |
| function loadInvalidCookiesFromFile() { |
| ensureDataDirExists(); |
| |
| try { |
| if (fs.existsSync(INVALID_COOKIES_FILE)) { |
| const data = fs.readFileSync(INVALID_COOKIES_FILE, 'utf8'); |
| const cookiesArray = JSON.parse(data); |
| |
| |
| invalidCookies.clear(); |
| cookiesArray.forEach(cookie => invalidCookies.add(cookie)); |
| |
| console.log(`从文件加载了 ${cookiesArray.length} 个无效cookie`); |
| } else { |
| saveInvalidCookiesToFile(); |
| } |
| } catch (err) { |
| console.error('加载无效cookie文件失败:', err); |
| saveInvalidCookiesToFile(); |
| } |
| } |
|
|
| |
| function saveInvalidCookiesToFile() { |
| ensureDataDirExists(); |
| |
| try { |
| const cookiesArray = Array.from(invalidCookies); |
| fs.writeFileSync(INVALID_COOKIES_FILE, JSON.stringify(cookiesArray, null, 2), 'utf8'); |
| console.log(`已将 ${cookiesArray.length} 个无效cookie保存到文件`); |
| } catch (err) { |
| console.error('保存无效cookie文件失败:', err); |
| } |
| } |
|
|
| |
| function loadApiKeysFromFile() { |
| ensureDataDirExists(); |
| |
| try { |
| if (fs.existsSync(API_KEYS_FILE)) { |
| const data = fs.readFileSync(API_KEYS_FILE, 'utf8'); |
| const apiKeysObj = JSON.parse(data); |
| |
| |
| apiKeyMap.clear(); |
| rotationIndexes.clear(); |
| |
| |
| let totalCookies = 0; |
| |
| |
| for (const [apiKey, cookies] of Object.entries(apiKeysObj)) { |
| if (Array.isArray(cookies)) { |
| apiKeyMap.set(apiKey, cookies); |
| rotationIndexes.set(apiKey, 0); |
| totalCookies += cookies.length; |
| } else { |
| console.error(`API Key ${apiKey} 的cookies不是数组,跳过`); |
| } |
| } |
| |
| const apiKeyCount = Object.keys(apiKeysObj).length; |
| console.log(`从文件加载了 ${apiKeyCount} 个API Key,共 ${totalCookies} 个Cookie`); |
| return apiKeyCount > 0; |
| } else { |
| console.log('API Keys文件不存在,将使用配置中的API Keys'); |
| return false; |
| } |
| } catch (err) { |
| console.error('加载API Keys文件失败:', err); |
| return false; |
| } |
| } |
|
|
| |
| function saveApiKeysToFile() { |
| ensureDataDirExists(); |
| |
| try { |
| |
| const apiKeysObj = {}; |
| for (const [apiKey, cookies] of apiKeyMap.entries()) { |
| apiKeysObj[apiKey] = cookies; |
| } |
| |
| |
| const jsonString = JSON.stringify(apiKeysObj, null, 2); |
| fs.writeFileSync(API_KEYS_FILE, jsonString, 'utf8'); |
| console.log(`已将 ${Object.keys(apiKeysObj).length} 个API Key保存到文件`); |
| |
| |
| try { |
| const savedContent = fs.readFileSync(API_KEYS_FILE, 'utf8'); |
| JSON.parse(savedContent); |
| console.log('验证通过: 所有cookie都被完整保存'); |
| } catch (verifyErr) { |
| console.error('验证保存内容时出错:', verifyErr); |
| } |
| } catch (err) { |
| console.error('保存API Keys文件失败:', err); |
| } |
| } |
|
|
| |
| function initializeApiKeys() { |
| |
| const loadedFromFile = loadApiKeysFromFile(); |
| |
| |
| const configApiKeys = config.apiKeys; |
| const hasEnvApiKeys = Object.keys(configApiKeys).length > 0; |
| |
| if (hasEnvApiKeys) { |
| console.log('从环境变量检测到API Keys配置,将合并到现有配置...'); |
| |
| |
| let beforeMergeCookies = 0; |
| for (const cookies of apiKeyMap.values()) { |
| beforeMergeCookies += cookies.length; |
| } |
| |
| |
| for (const [apiKey, cookieValue] of Object.entries(configApiKeys)) { |
| |
| const existingCookies = apiKeyMap.get(apiKey) || []; |
| |
| |
| let newCookies = []; |
| if (typeof cookieValue === 'string') { |
| newCookies = [cookieValue]; |
| } else if (Array.isArray(cookieValue)) { |
| newCookies = cookieValue; |
| } |
| |
| |
| const mergedCookies = [...existingCookies]; |
| for (const cookie of newCookies) { |
| if (!mergedCookies.includes(cookie)) { |
| mergedCookies.push(cookie); |
| } |
| } |
| |
| |
| apiKeyMap.set(apiKey, mergedCookies); |
| |
| |
| if (!rotationIndexes.has(apiKey)) { |
| rotationIndexes.set(apiKey, 0); |
| } |
| } |
| |
| |
| let afterMergeCookies = 0; |
| for (const cookies of apiKeyMap.values()) { |
| afterMergeCookies += cookies.length; |
| } |
| |
| console.log(`合并前共有 ${beforeMergeCookies} 个Cookie,合并后共有 ${afterMergeCookies} 个Cookie`); |
| |
| |
| saveApiKeysToFile(); |
| } else if (!loadedFromFile) { |
| console.log('警告: 未能从文件加载API Keys,且环境变量中也没有配置API Keys'); |
| } |
| |
| |
| let totalCookies = 0; |
| for (const cookies of apiKeyMap.values()) { |
| totalCookies += cookies.length; |
| } |
| |
| console.log(`API Keys初始化完成,共有 ${apiKeyMap.size} 个API Key,${totalCookies} 个Cookie`); |
| |
| |
| loadInvalidCookiesFromFile(); |
| |
| |
| console.log('开始从API Keys中移除无效cookie...'); |
| removeInvalidCookiesFromApiKeys(); |
| } |
|
|
| |
| function removeInvalidCookiesFromApiKeys() { |
| let totalRemoved = 0; |
| |
| for (const [apiKey, cookies] of apiKeyMap.entries()) { |
| const initialLength = cookies.length; |
| |
| |
| const filteredCookies = cookies.filter(cookie => !invalidCookies.has(cookie)); |
| |
| |
| if (filteredCookies.length < initialLength) { |
| const removedCount = initialLength - filteredCookies.length; |
| totalRemoved += removedCount; |
| |
| apiKeyMap.set(apiKey, filteredCookies); |
| rotationIndexes.set(apiKey, 0); |
| |
| console.log(`从API Key ${apiKey} 中移除了 ${removedCount} 个无效cookie,剩余 ${filteredCookies.length} 个`); |
| } |
| } |
| |
| console.log(`总共从API Keys中移除了 ${totalRemoved} 个无效cookie`); |
| |
| |
| if (totalRemoved > 0) { |
| saveApiKeysToFile(); |
| } |
| } |
|
|
| |
| function addOrUpdateApiKey(apiKey, cookieValues) { |
| if (!Array.isArray(cookieValues)) { |
| cookieValues = [cookieValues]; |
| } |
| |
| |
| const validCookies = cookieValues.filter(cookie => !invalidCookies.has(cookie)); |
| |
| if (validCookies.length < cookieValues.length) { |
| console.log(`API Key ${apiKey} 中有 ${cookieValues.length - validCookies.length} 个无效cookie被过滤`); |
| } |
| |
| apiKeyMap.set(apiKey, validCookies); |
| rotationIndexes.set(apiKey, 0); |
| |
| |
| saveApiKeysToFile(); |
| } |
|
|
| |
| function removeApiKey(apiKey) { |
| apiKeyMap.delete(apiKey); |
| rotationIndexes.delete(apiKey); |
| |
| |
| saveApiKeysToFile(); |
| } |
|
|
| |
| function getCookieForApiKey(apiKey, strategy = config.defaultRotationStrategy) { |
| |
| if (!apiKeyMap.has(apiKey)) { |
| return apiKey; |
| } |
| const cookies = apiKeyMap.get(apiKey); |
| |
| if (!cookies || cookies.length === 0) { |
| return apiKey; |
| } |
| |
| if (cookies.length === 1) { |
| return cookies[0]; |
| } |
| |
| |
| if (strategy === 'random') { |
| |
| const randomIndex = Math.floor(Math.random() * cookies.length); |
| return cookies[randomIndex]; |
| } else { |
| |
| let currentIndex = rotationIndexes.get(apiKey) || 0; |
| const cookie = cookies[currentIndex]; |
| |
| |
| currentIndex = (currentIndex + 1) % cookies.length; |
| rotationIndexes.set(apiKey, currentIndex); |
| |
| return cookie; |
| } |
| } |
|
|
| |
| function getAllApiKeys() { |
| return Array.from(apiKeyMap.keys()); |
| } |
|
|
| |
| function getAllCookiesForApiKey(apiKey) { |
| return apiKeyMap.get(apiKey) || []; |
| } |
|
|
| |
| function removeCookieFromApiKey(apiKey, cookieToRemove) { |
| if (!apiKeyMap.has(apiKey)) { |
| console.log(`API Key ${apiKey} 不存在,无法移除cookie`); |
| return false; |
| } |
| |
| const cookies = apiKeyMap.get(apiKey); |
| const initialLength = cookies.length; |
| |
| |
| if (cookieToRemove === apiKey && initialLength === 0) { |
| console.log(`API Key ${apiKey} 中没有任何cookie,系统正在尝试以向后兼容模式使用API Key本身`); |
| return false; |
| } |
| |
| |
| const filteredCookies = cookies.filter(cookie => cookie !== cookieToRemove); |
| |
| |
| if (filteredCookies.length === initialLength) { |
| console.log(`未找到要移除的cookie: ${cookieToRemove}`); |
| return false; |
| } |
| |
| |
| apiKeyMap.set(apiKey, filteredCookies); |
| |
| |
| rotationIndexes.set(apiKey, 0); |
| |
| |
| invalidCookies.add(cookieToRemove); |
| |
| |
| saveInvalidCookiesToFile(); |
| |
| |
| saveApiKeysToFile(); |
| |
| console.log(`已从API Key ${apiKey} 中移除cookie: ${cookieToRemove}`); |
| console.log(`剩余cookie数量: ${filteredCookies.length}`); |
| |
| return true; |
| } |
|
|
| |
| function getInvalidCookies() { |
| return invalidCookies; |
| } |
|
|
| |
| function clearInvalidCookie(cookie) { |
| const result = invalidCookies.delete(cookie); |
| |
| if (result) { |
| |
| saveInvalidCookiesToFile(); |
| } |
| |
| return result; |
| } |
|
|
| |
| function clearAllInvalidCookies() { |
| invalidCookies.clear(); |
| |
| |
| saveInvalidCookiesToFile(); |
| |
| return true; |
| } |
|
|
| module.exports = { |
| addOrUpdateApiKey, |
| removeApiKey, |
| getCookieForApiKey, |
| getAllApiKeys, |
| getAllCookiesForApiKey, |
| initializeApiKeys, |
| removeCookieFromApiKey, |
| getInvalidCookies, |
| clearInvalidCookie, |
| clearAllInvalidCookies, |
| loadInvalidCookiesFromFile, |
| saveInvalidCookiesToFile, |
| loadApiKeysFromFile, |
| saveApiKeysToFile |
| }; |