fix: Cookie管理优化及刷新间隔补充
- monitor-server.js: Cookie存储改为Map合并机制,避免丢失CDN Cookie导致403 - checkActivity.js: 补上Cookie两步请求逻辑,修复无Cookie直接请求失败的问题 - public/index.html: 新增20秒刷新间隔选项
This commit is contained in:
@@ -1,29 +1,86 @@
|
|||||||
// checkActivity.js
|
// checkActivity.js
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const cheerio = require('cheerio');
|
const cheerio = require('cheerio');
|
||||||
const notifier = require('node-notifier'); // 新增桌面通知模块
|
const notifier = require('node-notifier');
|
||||||
|
|
||||||
const url = 'https://www.szhdy.com/activities/default.html?method=activity&id=9';
|
const url = 'https://www.szhdy.com/activities/default.html?method=activity&id=9';
|
||||||
const checkInterval = 300000; // 5分钟检查一次
|
const checkInterval = 300000; // 5分钟检查一次
|
||||||
|
|
||||||
// 设置请求头,模拟浏览器(已包含完整浏览器特征)
|
|
||||||
const headers = {
|
const headers = {
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
||||||
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
|
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||||||
'Connection': 'keep-alive',
|
'Connection': 'keep-alive',
|
||||||
'Upgrade-Insecure-Requests': '1',
|
'Upgrade-Insecure-Requests': '1',
|
||||||
'Sec-Fetch-Dest': 'document',
|
|
||||||
'Sec-Fetch-Mode': 'navigate',
|
|
||||||
'Sec-Fetch-Site': 'none',
|
|
||||||
'Sec-Fetch-User': '?1'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Cookie 管理
|
||||||
|
let cookieJar = new Map();
|
||||||
|
let cookieExpiry = 0;
|
||||||
|
|
||||||
|
function mergeCookiesFromHeaders(setCookieHeaders) {
|
||||||
|
for (const header of setCookieHeaders) {
|
||||||
|
const pair = header.split(';')[0];
|
||||||
|
const eqIdx = pair.indexOf('=');
|
||||||
|
if (eqIdx > 0) {
|
||||||
|
cookieJar.set(pair.substring(0, eqIdx).trim(), pair.substring(eqIdx + 1).trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCookieString() {
|
||||||
|
return Array.from(cookieJar.entries()).map(([k, v]) => `${k}=${v}`).join('; ');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getValidCookies() {
|
||||||
|
if (cookieJar.size > 0 && Date.now() < cookieExpiry) {
|
||||||
|
return getCookieString();
|
||||||
|
}
|
||||||
|
console.log('[Cookie] 正在获取新的 Cookie...');
|
||||||
|
cookieJar.clear();
|
||||||
|
// 第一次请求拿 CDN Cookie(会返回 403,这是预期行为)
|
||||||
|
const res = await axios.get(url, {
|
||||||
|
headers,
|
||||||
|
maxRedirects: 0,
|
||||||
|
validateStatus: () => true, // 允许非 2xx 状态码
|
||||||
|
});
|
||||||
|
const setCookies = res.headers['set-cookie'] || [];
|
||||||
|
mergeCookiesFromHeaders(setCookies);
|
||||||
|
cookieExpiry = Date.now() + 3600 * 1000;
|
||||||
|
console.log('[Cookie] 获取成功:', getCookieString());
|
||||||
|
return getCookieString();
|
||||||
|
}
|
||||||
|
|
||||||
async function checkActivity() {
|
async function checkActivity() {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(url, { headers });
|
const cookies = await getValidCookies();
|
||||||
|
const response = await axios.get(url, {
|
||||||
|
headers: { ...headers, Cookie: cookies },
|
||||||
|
});
|
||||||
const html = response.data;
|
const html = response.data;
|
||||||
|
|
||||||
|
// 如果响应内容过短,Cookie 可能失效,刷新重试
|
||||||
|
if (typeof html === 'string' && html.length < 1000) {
|
||||||
|
console.log('[Cookie] 响应内容过短,刷新 Cookie 重试...');
|
||||||
|
cookieJar.clear();
|
||||||
|
cookieExpiry = 0;
|
||||||
|
const retryCookies = await getValidCookies();
|
||||||
|
const retryRes = await axios.get(url, {
|
||||||
|
headers: { ...headers, Cookie: retryCookies },
|
||||||
|
});
|
||||||
|
mergeCookiesFromHeaders(retryRes.headers['set-cookie'] || []);
|
||||||
|
return parseAndCheck(retryRes.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并新返回的 Cookie
|
||||||
|
mergeCookiesFromHeaders(response.headers['set-cookie'] || []);
|
||||||
|
return parseAndCheck(html);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`请求失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseAndCheck(html) {
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
// 限定在活动主容器内检测,避免扫描其他区域
|
// 限定在活动主容器内检测,避免扫描其他区域
|
||||||
@@ -184,9 +241,6 @@ async function checkActivity() {
|
|||||||
} else {
|
} else {
|
||||||
console.log(`[${new Date().toLocaleString()}] 暂未上架,继续监控...`);
|
console.log(`[${new Date().toLocaleString()}] 暂未上架,继续监控...`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
console.error(`请求失败: ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 立即执行一次
|
// 立即执行一次
|
||||||
|
|||||||
@@ -17,30 +17,43 @@ const REQUEST_HEADERS = {
|
|||||||
'Upgrade-Insecure-Requests': '1',
|
'Upgrade-Insecure-Requests': '1',
|
||||||
};
|
};
|
||||||
|
|
||||||
// 存储 Cookie
|
// 存储 Cookie(key-value Map,支持合并更新)
|
||||||
let cachedCookies = '';
|
let cookieJar = new Map();
|
||||||
let cookieExpiry = 0;
|
let cookieExpiry = 0;
|
||||||
|
|
||||||
// 从 set-cookie 响应头提取 Cookie
|
// 从 set-cookie 响应头提取 Cookie,合并到 cookieJar
|
||||||
function extractCookies(response) {
|
function mergeCookies(response) {
|
||||||
const setCookieHeaders = response.headers.getSetCookie?.() || [];
|
const setCookieHeaders = response.headers.getSetCookie?.() || [];
|
||||||
const cookies = setCookieHeaders.map(c => c.split(';')[0]).join('; ');
|
for (const header of setCookieHeaders) {
|
||||||
return cookies;
|
const pair = header.split(';')[0]; // 取 name=value 部分
|
||||||
|
const eqIdx = pair.indexOf('=');
|
||||||
|
if (eqIdx > 0) {
|
||||||
|
const name = pair.substring(0, eqIdx).trim();
|
||||||
|
const value = pair.substring(eqIdx + 1).trim();
|
||||||
|
cookieJar.set(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 cookieJar 序列化为请求头格式
|
||||||
|
function getCookieString() {
|
||||||
|
return Array.from(cookieJar.entries()).map(([k, v]) => `${k}=${v}`).join('; ');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取有效 Cookie(过期或为空时重新获取)
|
// 获取有效 Cookie(过期或为空时重新获取)
|
||||||
async function getValidCookies() {
|
async function getValidCookies() {
|
||||||
if (cachedCookies && Date.now() < cookieExpiry) {
|
if (cookieJar.size > 0 && Date.now() < cookieExpiry) {
|
||||||
return cachedCookies;
|
return getCookieString();
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[Cookie] 正在获取新的 Cookie...');
|
console.log('[Cookie] 正在获取新的 Cookie...');
|
||||||
|
cookieJar.clear();
|
||||||
const response = await fetch(ACTIVITY_URL, { headers: REQUEST_HEADERS, redirect: 'manual' });
|
const response = await fetch(ACTIVITY_URL, { headers: REQUEST_HEADERS, redirect: 'manual' });
|
||||||
cachedCookies = extractCookies(response);
|
mergeCookies(response);
|
||||||
// Cookie 有效期设为 1 小时(服务器 max-age=86400,保守取 1 小时)
|
// CDN Cookie 有效期 2 小时,保守取 1 小时刷新
|
||||||
cookieExpiry = Date.now() + 3600 * 1000;
|
cookieExpiry = Date.now() + 3600 * 1000;
|
||||||
console.log('[Cookie] 获取成功:', cachedCookies);
|
console.log('[Cookie] 获取成功:', getCookieString());
|
||||||
return cachedCookies;
|
return getCookieString();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.get('/api/activity', async (req, res) => {
|
app.get('/api/activity', async (req, res) => {
|
||||||
@@ -54,11 +67,13 @@ app.get('/api/activity', async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const html = await response.text();
|
const html = await response.text();
|
||||||
|
// 合并第二次请求返回的 Cookie(如 PHPSESSID)
|
||||||
|
mergeCookies(response);
|
||||||
|
|
||||||
// 如果返回内容过短,可能 Cookie 失效,清除缓存并重试一次
|
// 如果返回内容过短,可能 Cookie 失效,清除缓存并重试一次
|
||||||
if (html.length < 1000) {
|
if (html.length < 1000) {
|
||||||
console.log('[Cookie] 响应内容过短,尝试刷新 Cookie 重试...');
|
console.log('[Cookie] 响应内容过短,尝试刷新 Cookie 重试...');
|
||||||
cachedCookies = '';
|
cookieJar.clear();
|
||||||
cookieExpiry = 0;
|
cookieExpiry = 0;
|
||||||
const newCookies = await getValidCookies();
|
const newCookies = await getValidCookies();
|
||||||
const retryResponse = await fetch(ACTIVITY_URL, {
|
const retryResponse = await fetch(ACTIVITY_URL, {
|
||||||
@@ -68,22 +83,10 @@ app.get('/api/activity', async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
const retryHtml = await retryResponse.text();
|
const retryHtml = await retryResponse.text();
|
||||||
// 更新重试后的 Cookie
|
mergeCookies(retryResponse);
|
||||||
const retryCookies = extractCookies(retryResponse);
|
|
||||||
if (retryCookies) {
|
|
||||||
cachedCookies = retryCookies;
|
|
||||||
cookieExpiry = Date.now() + 3600 * 1000;
|
|
||||||
}
|
|
||||||
return res.json({ success: true, html: retryHtml });
|
return res.json({ success: true, html: retryHtml });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新响应中的新 Cookie
|
|
||||||
const newCookies = extractCookies(response);
|
|
||||||
if (newCookies) {
|
|
||||||
cachedCookies = newCookies;
|
|
||||||
cookieExpiry = Date.now() + 3600 * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({ success: true, html });
|
res.json({ success: true, html });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ success: false, error: error.message });
|
res.status(500).json({ success: false, error: error.message });
|
||||||
|
|||||||
@@ -521,6 +521,7 @@
|
|||||||
<option value="180">3分钟</option>
|
<option value="180">3分钟</option>
|
||||||
<option value="60" selected>1分钟</option>
|
<option value="60" selected>1分钟</option>
|
||||||
<option value="30">30秒</option>
|
<option value="30">30秒</option>
|
||||||
|
<option value="20">20秒</option>
|
||||||
<option value="5">5秒</option>
|
<option value="5">5秒</option>
|
||||||
<option value="3">3秒</option>
|
<option value="3">3秒</option>
|
||||||
<option value="1">1秒</option>
|
<option value="1">1秒</option>
|
||||||
|
|||||||
Reference in New Issue
Block a user