Spaces:
Running
Running
Update src/CookieManager.js
Browse files- src/CookieManager.js +0 -379
src/CookieManager.js
CHANGED
@@ -17,385 +17,6 @@ const logger = {
|
|
17 |
success: (message) => console.log(`\x1b[32m[success] ${message}\x1b[0m`),
|
18 |
};
|
19 |
|
20 |
-
class CookieManager {
|
21 |
-
constructor() {
|
22 |
-
this.cookieEntries = []; // 存储cookie及其对应的ID
|
23 |
-
this.currentIndex = 0;
|
24 |
-
this.initialized = false;
|
25 |
-
this.maxRetries = 3; // 最大重试次数
|
26 |
-
this.proxyUrl = process.env.PROXY_URL || "";
|
27 |
-
}
|
28 |
-
|
29 |
-
/**
|
30 |
-
* 从文件加载cookie
|
31 |
-
* @param {string} filePath - cookie文件路径
|
32 |
-
* @returns {Promise<boolean>} - 是否加载成功
|
33 |
-
*/
|
34 |
-
async loadFromFile(filePath) {
|
35 |
-
try {
|
36 |
-
// 确保文件路径是绝对路径
|
37 |
-
const absolutePath = path.isAbsolute(filePath)
|
38 |
-
? filePath
|
39 |
-
: path.join(dirname(__dirname), filePath);
|
40 |
-
|
41 |
-
logger.info(`从文件加载cookie: ${absolutePath}`);
|
42 |
-
|
43 |
-
// 检查文件是否存在
|
44 |
-
if (!fs.existsSync(absolutePath)) {
|
45 |
-
logger.error(`Cookie文件不存在: ${absolutePath}`);
|
46 |
-
return false;
|
47 |
-
}
|
48 |
-
|
49 |
-
// 读取文件内容
|
50 |
-
const fileContent = fs.readFileSync(absolutePath, 'utf8');
|
51 |
-
|
52 |
-
// 根据文件扩展名处理不同格式
|
53 |
-
const ext = path.extname(absolutePath).toLowerCase();
|
54 |
-
let cookieArray = [];
|
55 |
-
|
56 |
-
if (ext === '.json') {
|
57 |
-
// JSON格式
|
58 |
-
try {
|
59 |
-
const jsonData = JSON.parse(fileContent);
|
60 |
-
if (Array.isArray(jsonData)) {
|
61 |
-
cookieArray = jsonData;
|
62 |
-
} else if (jsonData.cookies && Array.isArray(jsonData.cookies)) {
|
63 |
-
cookieArray = jsonData.cookies;
|
64 |
-
} else {
|
65 |
-
logger.error('JSON文件格式错误,应为cookie数组或包含cookies数组的对象');
|
66 |
-
return false;
|
67 |
-
}
|
68 |
-
} catch (error) {
|
69 |
-
logger.error(`解析JSON文件失败: ${error.message}`);
|
70 |
-
return false;
|
71 |
-
}
|
72 |
-
} else {
|
73 |
-
// 文本格式,每行一个cookie
|
74 |
-
cookieArray = fileContent
|
75 |
-
.split('\n')
|
76 |
-
.map(line => line.trim())
|
77 |
-
.filter(line => line && !line.startsWith('#'));
|
78 |
-
}
|
79 |
-
|
80 |
-
logger.info(`从文件中读取了 ${cookieArray.length} 个cookie`);
|
81 |
-
|
82 |
-
// 初始化cookie
|
83 |
-
return await this.initialize(cookieArray.join('|'));
|
84 |
-
|
85 |
-
} catch (error) {
|
86 |
-
logger.error(`从文件加载cookie失败: ${error.message}`);
|
87 |
-
return false;
|
88 |
-
}
|
89 |
-
}
|
90 |
-
|
91 |
-
/**
|
92 |
-
* 初始化cookie管理器
|
93 |
-
* @param {string} cookiesString - 以"|"或","分隔的cookie字符串
|
94 |
-
* @returns {Promise<boolean>} - 是否初始化成功
|
95 |
-
*/
|
96 |
-
async initialize(cookiesString) {
|
97 |
-
if (!cookiesString) {
|
98 |
-
logger.error('未提供cookie字符串');
|
99 |
-
return false;
|
100 |
-
}
|
101 |
-
|
102 |
-
// 【已修改】使用正则表达式支持"|"和","两种分隔符
|
103 |
-
const cookieArray = cookiesString.split(/[|,]/).map(c => c.trim()).filter(c => c);
|
104 |
-
|
105 |
-
if (cookieArray.length === 0) {
|
106 |
-
logger.error('没有有效的cookie');
|
107 |
-
return false;
|
108 |
-
}
|
109 |
-
|
110 |
-
logger.info(`发现 ${cookieArray.length} 个cookie,开始获取对应的ID信息...`);
|
111 |
-
|
112 |
-
// 清空现有条目
|
113 |
-
this.cookieEntries = [];
|
114 |
-
|
115 |
-
// 为每个cookie获取ID
|
116 |
-
for (let i = 0; i < cookieArray.length; i++) {
|
117 |
-
const cookie = cookieArray[i];
|
118 |
-
logger.info(`正在处理第 ${i+1}/${cookieArray.length} 个cookie...`);
|
119 |
-
|
120 |
-
const result = await this.fetchNotionIds(cookie);
|
121 |
-
if (result.success) {
|
122 |
-
this.cookieEntries.push({
|
123 |
-
cookie,
|
124 |
-
spaceId: result.spaceId,
|
125 |
-
userId: result.userId,
|
126 |
-
valid: true,
|
127 |
-
lastUsed: 0 // 记录上次使用时间戳
|
128 |
-
});
|
129 |
-
logger.success(`第 ${i+1} 个cookie验证成功`);
|
130 |
-
} else {
|
131 |
-
if (result.status === 401) {
|
132 |
-
logger.error(`第 ${i+1} 个cookie无效(401未授权),已跳过`);
|
133 |
-
} else {
|
134 |
-
logger.warning(`第 ${i+1} 个cookie验证失败: ${result.error},已跳过`);
|
135 |
-
}
|
136 |
-
}
|
137 |
-
}
|
138 |
-
|
139 |
-
// 检查是否有有效的cookie
|
140 |
-
if (this.cookieEntries.length === 0) {
|
141 |
-
logger.error('没有有效的cookie,初始化失败');
|
142 |
-
return false;
|
143 |
-
}
|
144 |
-
|
145 |
-
logger.success(`成功初始化 ${this.cookieEntries.length}/${cookieArray.length} 个cookie`);
|
146 |
-
this.initialized = true;
|
147 |
-
this.currentIndex = 0;
|
148 |
-
return true;
|
149 |
-
}
|
150 |
-
|
151 |
-
/**
|
152 |
-
* 保存cookie到文件
|
153 |
-
* @param {string} filePath - 保存路径
|
154 |
-
* @param {boolean} onlyValid - 是否只保存有效的cookie
|
155 |
-
* @returns {boolean} - 是否保存成功
|
156 |
-
*/
|
157 |
-
saveToFile(filePath, onlyValid = true) {
|
158 |
-
try {
|
159 |
-
// 确保文件路径是绝对路径
|
160 |
-
const absolutePath = path.isAbsolute(filePath)
|
161 |
-
? filePath
|
162 |
-
: path.join(dirname(__dirname), filePath);
|
163 |
-
|
164 |
-
// 获取要保存的cookie
|
165 |
-
const cookiesToSave = onlyValid
|
166 |
-
? this.cookieEntries.filter(entry => entry.valid).map(entry => entry.cookie)
|
167 |
-
: this.cookieEntries.map(entry => entry.cookie);
|
168 |
-
|
169 |
-
// 根据文件扩展名选择保存格式
|
170 |
-
const ext = path.extname(absolutePath).toLowerCase();
|
171 |
-
|
172 |
-
if (ext === '.json') {
|
173 |
-
// 保存为JSON格式
|
174 |
-
const jsonData = {
|
175 |
-
cookies: cookiesToSave,
|
176 |
-
updatedAt: new Date().toISOString(),
|
177 |
-
count: cookiesToSave.length
|
178 |
-
};
|
179 |
-
fs.writeFileSync(absolutePath, JSON.stringify(jsonData, null, 2), 'utf8');
|
180 |
-
} else {
|
181 |
-
// 保存为文本格式,每行一个cookie
|
182 |
-
const content = cookiesToSave.join('\n');
|
183 |
-
fs.writeFileSync(absolutePath, content, 'utf8');
|
184 |
-
}
|
185 |
-
|
186 |
-
logger.success(`已将 ${cookiesToSave.length} 个cookie保存到文件: ${absolutePath}`);
|
187 |
-
return true;
|
188 |
-
} catch (error) {
|
189 |
-
logger.error(`保存cookie到文件失败: ${error.message}`);
|
190 |
-
return false;
|
191 |
-
}
|
192 |
-
}
|
193 |
-
|
194 |
-
/**
|
195 |
-
* 获取Notion的空间ID和用户ID
|
196 |
-
* @param {string} cookie - Notion cookie
|
197 |
-
* @returns {Promise<Object>} - 包含ID信息的对象
|
198 |
-
*/
|
199 |
-
async fetchNotionIds(cookie, retryCount = 0) {
|
200 |
-
if (!cookie) {
|
201 |
-
return { success: false, error: '未提供cookie' };
|
202 |
-
}
|
203 |
-
|
204 |
-
try {
|
205 |
-
// 创建JSDOM实例模拟浏览器环境
|
206 |
-
const dom = new JSDOM("", {
|
207 |
-
url: "https://www.notion.so",
|
208 |
-
referrer: "https://www.notion.so/",
|
209 |
-
contentType: "text/html",
|
210 |
-
includeNodeLocations: true,
|
211 |
-
storageQuota: 10000000,
|
212 |
-
pretendToBeVisual: true,
|
213 |
-
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
|
214 |
-
});
|
215 |
-
|
216 |
-
// 设置全局对象
|
217 |
-
const { window } = dom;
|
218 |
-
|
219 |
-
// 安全地设置全局对象
|
220 |
-
if (!global.window) global.window = window;
|
221 |
-
if (!global.document) global.document = window.document;
|
222 |
-
|
223 |
-
// 设置navigator
|
224 |
-
if (!global.navigator) {
|
225 |
-
try {
|
226 |
-
Object.defineProperty(global, 'navigator', {
|
227 |
-
value: window.navigator,
|
228 |
-
writable: true,
|
229 |
-
configurable: true
|
230 |
-
});
|
231 |
-
} catch (navError) {
|
232 |
-
logger.warning(`无法设置navigator: ${navError.message},继续执行`);
|
233 |
-
}
|
234 |
-
}
|
235 |
-
|
236 |
-
// 设置cookie
|
237 |
-
document.cookie = cookie;
|
238 |
-
|
239 |
-
// 创建fetch选项
|
240 |
-
const fetchOptions = {
|
241 |
-
method: 'POST',
|
242 |
-
headers: {
|
243 |
-
'Content-Type': 'application/json',
|
244 |
-
'accept': '*/*',
|
245 |
-
'accept-language': 'en-US,en;q=0.9',
|
246 |
-
'notion-audit-log-platform': 'web',
|
247 |
-
'notion-client-version': '23.13.0.3686',
|
248 |
-
'origin': 'https://www.notion.so',
|
249 |
-
'referer': 'https://www.notion.so/',
|
250 |
-
'user-agent': window.navigator.userAgent,
|
251 |
-
'Cookie': cookie
|
252 |
-
},
|
253 |
-
body: JSON.stringify({}),
|
254 |
-
};
|
255 |
-
|
256 |
-
// 添加代理配置(如果有)
|
257 |
-
if (this.proxyUrl) {
|
258 |
-
const { HttpsProxyAgent } = await import('https-proxy-agent');
|
259 |
-
fetchOptions.agent = new HttpsProxyAgent(this.proxyUrl);
|
260 |
-
logger.info(`使用代理: ${this.proxyUrl}`);
|
261 |
-
}
|
262 |
-
|
263 |
-
// 发送请求
|
264 |
-
const response = await fetch("https://www.notion.so/api/v3/getSpaces", fetchOptions);
|
265 |
-
|
266 |
-
// 检查响应状态
|
267 |
-
if (response.status === 401) {
|
268 |
-
return { success: false, status: 401, error: '未授权,cookie无效' };
|
269 |
-
}
|
270 |
-
|
271 |
-
if (!response.ok) {
|
272 |
-
throw new Error(`HTTP error! status: ${response.status}`);
|
273 |
-
}
|
274 |
-
|
275 |
-
const data = await response.json();
|
276 |
-
|
277 |
-
// 提取用户ID
|
278 |
-
const userIdKey = Object.keys(data)[0];
|
279 |
-
if (!userIdKey) {
|
280 |
-
throw new Error('无法从响应中提取用户ID');
|
281 |
-
}
|
282 |
-
|
283 |
-
const userId = userIdKey;
|
284 |
-
|
285 |
-
// 提取空间ID
|
286 |
-
const userRoot = data[userIdKey]?.user_root?.[userIdKey];
|
287 |
-
const spaceViewPointers = userRoot?.value?.value?.space_view_pointers;
|
288 |
-
|
289 |
-
if (!spaceViewPointers || !Array.isArray(spaceViewPointers) || spaceViewPointers.length === 0) {
|
290 |
-
throw new Error('在响应中找不到space_view_pointers或spaceId');
|
291 |
-
}
|
292 |
-
|
293 |
-
const spaceId = spaceViewPointers[0].spaceId;
|
294 |
-
|
295 |
-
if (!spaceId) {
|
296 |
-
throw new Error('无法从space_view_pointers中提取spaceId');
|
297 |
-
}
|
298 |
-
|
299 |
-
// 清理全局对象
|
300 |
-
this.cleanupGlobalObjects();
|
301 |
-
|
302 |
-
return {
|
303 |
-
success: true,
|
304 |
-
userId,
|
305 |
-
spaceId
|
306 |
-
};
|
307 |
-
|
308 |
-
} catch (error) {
|
309 |
-
// 清理全局对象
|
310 |
-
this.cleanupGlobalObjects();
|
311 |
-
|
312 |
-
// 重试逻辑
|
313 |
-
if (retryCount < this.maxRetries && error.message !== '未授权,cookie无效') {
|
314 |
-
logger.warning(`获取Notion ID失败,正在重试 (${retryCount + 1}/${this.maxRetries}): ${error.message}`);
|
315 |
-
return await this.fetchNotionIds(cookie, retryCount + 1);
|
316 |
-
}
|
317 |
-
|
318 |
-
return {
|
319 |
-
success: false,
|
320 |
-
error: error.message
|
321 |
-
};
|
322 |
-
}
|
323 |
-
}
|
324 |
-
|
325 |
-
/**
|
326 |
-
* 清理全局对象
|
327 |
-
*/
|
328 |
-
cleanupGlobalObjects() {
|
329 |
-
try {
|
330 |
-
if (global.window) delete global.window;
|
331 |
-
if (global.document) delete global.document;
|
332 |
-
|
333 |
-
// 安全地删除navigator
|
334 |
-
if (global.navigator) {
|
335 |
-
try {
|
336 |
-
delete global.navigator;
|
337 |
-
} catch (navError) {
|
338 |
-
// 如果无法删除,尝试将其设置为undefined
|
339 |
-
try {
|
340 |
-
Object.defineProperty(global, 'navigator', {
|
341 |
-
value: undefined,
|
342 |
-
writable: true,
|
343 |
-
configurable: true
|
344 |
-
});
|
345 |
-
} catch (defineError) {
|
346 |
-
logger.warning(`无法清理navigator: ${defineError.message}`);
|
347 |
-
}
|
348 |
-
}
|
349 |
-
}
|
350 |
-
} catch (cleanupError) {
|
351 |
-
logger.warning(`清理全局对象时出错: ${cleanupError.message}`);
|
352 |
-
}
|
353 |
-
}
|
354 |
-
|
355 |
-
/**
|
356 |
-
* 获取下一个可用的cookie及其ID
|
357 |
-
* @returns {Object|null} - cookie及其对应的ID,如果没有可用cookie则返回null
|
358 |
-
*/
|
359 |
-
getNext() {
|
360 |
-
if (!this.initialized || this.cookieEntries.length === 0) {
|
361 |
-
return null;
|
362 |
-
}
|
363 |
-
|
364 |
-
// 轮询选择下一个cookie
|
365 |
-
const entry = this.cookieEntries[this.currentIndex];
|
366 |
-
|
367 |
-
// 更新索引,实现轮询
|
368 |
-
this.currentIndex = (this.currentIndex + 1) % this.cookieEntries.length;
|
369 |
-
|
370 |
-
// 更新最后使用时间
|
371 |
-
entry.lastUsed = Date.now();
|
372 |
-
|
373 |
-
return {
|
374 |
-
cookie: entry.cookie,
|
375 |
-
spaceId: entry.spaceId,
|
376 |
-
userId: entry.userId
|
377 |
-
};
|
378 |
-
}
|
379 |
-
|
380 |
-
import { JSDOM } from 'jsdom';
|
381 |
-
import fetch from 'node-fetch';
|
382 |
-
import fs from 'fs';
|
383 |
-
import path from 'path';
|
384 |
-
import { fileURLToPath } from 'url';
|
385 |
-
import { dirname } from 'path';
|
386 |
-
|
387 |
-
// 获取当前文件的目录路径
|
388 |
-
const __filename = fileURLToPath(import.meta.url);
|
389 |
-
const __dirname = dirname(__filename);
|
390 |
-
|
391 |
-
// 日志配置
|
392 |
-
const logger = {
|
393 |
-
info: (message) => console.log(`\x1b[34m[info] ${message}\x1b[0m`),
|
394 |
-
error: (message) => console.error(`\x1b[31m[error] ${message}\x1b[0m`),
|
395 |
-
warning: (message) => console.warn(`\x1b[33m[warn] ${message}\x1b[0m`),
|
396 |
-
success: (message) => console.log(`\x1b[32m[success] ${message}\x1b[0m`),
|
397 |
-
};
|
398 |
-
|
399 |
class CookieManager {
|
400 |
constructor() {
|
401 |
this.cookieEntries = []; // 存储cookie及其对应的ID
|
|
|
17 |
success: (message) => console.log(`\x1b[32m[success] ${message}\x1b[0m`),
|
18 |
};
|
19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
class CookieManager {
|
21 |
constructor() {
|
22 |
this.cookieEntries = []; // 存储cookie及其对应的ID
|