|
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 |
|
}; |