diff --git a/danmu_api/ui/js/systemsettings.js b/danmu_api/ui/js/systemsettings.js index 8744b22..26968aa 100644 --- a/danmu_api/ui/js/systemsettings.js +++ b/danmu_api/ui/js/systemsettings.js @@ -552,10 +552,21 @@ function renderValueInput(item) { \`; - // 自动检测 Cookie 状态 + // 自动检测 Cookie 状态 + 监听输入变化(防抖) setTimeout(() => { autoCheckBilibiliCookieStatus(); - }, 100); + + const inputEl = document.getElementById('text-value'); + if (inputEl) { + let debounceTimer = null; + inputEl.addEventListener('input', () => { + if (debounceTimer) clearTimeout(debounceTimer); + debounceTimer = setTimeout(() => { + autoCheckBilibiliCookieStatus(); + }, 600); + }); + } + }, 120); } else if (value && value.length > 50) { const rows = Math.min(Math.max(Math.ceil(value.length / 50), 3), 10); container.innerHTML = \` @@ -1370,8 +1381,8 @@ function fillBilibiliCookie(cookie) { textInput.style.borderColor = 'var(--success-color, #28a745)'; setTimeout(() => { textInput.style.borderColor = ''; - // 填入后不立即检测,提示用户保存 - showBilibiliCookieSaveHint(); + // 填入后触发检测一次(会提示用户保存) + autoCheckBilibiliCookieStatus(); }, 2000); } } @@ -1402,30 +1413,56 @@ async function autoCheckBilibiliCookieStatus() { } statusEl.innerHTML = '🔍检测中...'; - + + // 脱敏后的 *...* 无法直接校验,后端会自动改为校验“已保存”的 Cookie + const isMasked = /^[*]+$/.test(cookie); + const payload = isMasked ? {} : { cookie }; + try { - const response = await fetch(buildApiUrl('/api/cookie/status', true)); + const response = await fetch(buildApiUrl('/api/cookie/verify', true), { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + const result = await response.json(); - - if (result.success && result.data && result.data.isValid) { - const expiresAt = result.data.expiresAt; - const now = Math.floor(Date.now() / 1000); - const daysLeft = Math.ceil((expiresAt - now) / (24 * 60 * 60)); - - statusEl.innerHTML = \`✅\${result.data.uname} (剩余 \${daysLeft} 天)\`; + + if (result && result.success && result.data) { + if (result.data.isValid) { + const uname = result.data.uname || '已登录'; + const expiresAt = result.data.expiresAt; + const now = Math.floor(Date.now() / 1000); + + let leftText = ''; + if (typeof expiresAt === 'number' && expiresAt > now) { + const daysLeft = Math.ceil((expiresAt - now) / (24 * 60 * 60)); + leftText = \` (剩余 \${daysLeft} 天)\`; + } + + // 用户手动输入/扫码填入的 Cookie → 提示保存 + if (!isMasked) { + statusEl.innerHTML = \`✅\${uname}\${leftText} · 请点击保存按钮(Vercel等平台需重新部署后生效)\`; + } else { + // 脱敏显示时只展示当前已保存 Cookie 的状态 + statusEl.innerHTML = \`✅\${uname}\${leftText}\`; + } + } else { + const err = result.data.error || 'Cookie无效或已失效'; + statusEl.innerHTML = \`❌\${err},请重新扫码登录并保存\`; + } } else { - // 检测到无效时,提示用户保存并重新部署 - showBilibiliCookieSaveHint(); + statusEl.innerHTML = '⚠️检测失败'; } } catch (error) { statusEl.innerHTML = '⚠️检测失败'; } } // 显示 Bilibili Cookie 保存提示 -function showBilibiliCookieSaveHint() { +function showBilibiliCookieSaveHint(text) { const statusEl = document.getElementById('bili-cookie-status'); if (!statusEl) return; - - statusEl.innerHTML = '💾请点击保存按钮,Vercel等平台需重新部署后生效'; + + const msg = text || '请点击保存按钮,Vercel等平台需重新部署后生效'; + statusEl.innerHTML = \`💾\${msg}\`; } `; diff --git a/danmu_api/utils/cookie-util.js b/danmu_api/utils/cookie-util.js index c99d5fa..ff68d51 100644 --- a/danmu_api/utils/cookie-util.js +++ b/danmu_api/utils/cookie-util.js @@ -1,303 +1,449 @@ import { jsonResponse } from './http-util.js'; import { log } from './log-util.js'; -import { Globals } from '../configs/globals.js'; -import { Envs } from '../configs/envs.js'; +import { globals } from '../configs/globals.js'; +import { HandlerFactory } from '../configs/handlers/handler-factory.js'; -// 存储二维码登录会话(内存存储) +// ================================ +// Bilibili Cookie 工具 +// ================================ + +// 存储二维码登录会话(内存存储,仅用于短期轮询状态) const qrLoginSessions = new Map(); /** - * 保存Cookie到环境变量 + * 从运行时环境获取当前已保存的 Cookie + * + * 说明: + * - 不做任何“写入内存”持久化逻辑 + * - 仅从当前环境变量/进程环境/运行时配置中读取 */ -function saveCookieToGlobals(cookie) { - try { - if (Globals.envs) { - Globals.envs.bilibliCookie = cookie; - } - - if (Envs.env) { - Envs.env.BILIBILI_COOKIE = cookie; - } - - if (Envs.originalEnvVars && typeof Envs.originalEnvVars.set === 'function') { - Envs.originalEnvVars.set('BILIBILI_COOKIE', cookie); - } - - log("info", `Cookie已保存,长度: ${cookie ? cookie.length : 0}`); - } catch (err) { - log("error", `保存Cookie失败: ${err.message}`); +function getCookieFromRuntime() { + // 1) 优先使用已加载到 globals 的配置(最常见) + try { + if (globals && typeof globals.bilibliCookie === 'string' && globals.bilibliCookie) { + return globals.bilibliCookie; } + } catch (_) { + // ignore + } + + // 2) 兜底:直接读运行时 env 对象或 process.env + try { + if (globals && globals.env && typeof globals.env.BILIBILI_COOKIE === 'string' && globals.env.BILIBILI_COOKIE) { + return globals.env.BILIBILI_COOKIE; + } + } catch (_) { + // ignore + } + + if (typeof process !== 'undefined' && process.env && typeof process.env.BILIBILI_COOKIE === 'string' && process.env.BILIBILI_COOKIE) { + return process.env.BILIBILI_COOKIE; + } + + return ''; } /** - * 从多个位置获取Cookie + * 尝试从 Cookie 中解析过期时间(Unix 秒) + * + * 说明: + * - B 站 SESSDATA 常见格式中会携带一个时间戳片段(不同场景可能不存在/格式不同) + * - 解析失败时,为了兼容旧逻辑,返回“现在 + 30 天”的估算值 */ -function getCookieFromGlobals() { - if (Globals.envs && Globals.envs.bilibliCookie) { - return Globals.envs.bilibliCookie; +function parseExpiresFromCookie(cookie) { + try { + const match = cookie.match(/SESSDATA=([^;]+)/); + if (!match) { + return Math.floor(Date.now() / 1000) + (30 * 24 * 60 * 60); } - if (Envs.env && Envs.env.BILIBILI_COOKIE) { - return Envs.env.BILIBILI_COOKIE; + + const sessDataRaw = match[1]; + const decoded = decodeURIComponent(sessDataRaw); + + // 提取可能的时间戳(10位或更长的纯数字段),取最接近“未来”的合理值 + const now = Math.floor(Date.now() / 1000); + const candidates = (decoded.match(/\d{10,}/g) || []) + .map(s => Number(s)) + .filter(n => Number.isFinite(n)); + + // 只保留未来且不超过 10 年的候选值 + const tenYears = 10 * 365 * 24 * 60 * 60; + const future = candidates.filter(n => n > now && n < now + tenYears); + + if (future.length > 0) { + // 一般越大越接近真实过期时间 + return Math.max(...future); } - if (typeof process !== 'undefined' && process.env && process.env.BILIBILI_COOKIE) { - return process.env.BILIBILI_COOKIE; + + // 兼容旧实现:如果逗号分割第二段是时间戳 + if (decoded.includes(',')) { + const parts = decoded.split(','); + if (parts.length > 1) { + const ts = parseInt(parts[1], 10); + if (!isNaN(ts) && ts > now) { + return ts; + } + } } - return ''; + } catch (e) { + // ignore + } + + // fallback:估算 30 天 + return Math.floor(Date.now() / 1000) + (30 * 24 * 60 * 60); } /** * 验证 Cookie 有效性 */ async function verifyCookieValidity(cookie) { - if (!cookie) { - return { isValid: false, error: '缺少Cookie' }; + if (!cookie) { + return { isValid: false, error: '缺少Cookie' }; + } + + // 简单格式检查 + if (!cookie.includes('SESSDATA') || !cookie.includes('bili_jct')) { + return { isValid: false, error: 'Cookie格式不完整(需要包含 SESSDATA 和 bili_jct)' }; + } + + try { + const response = await fetch('https://api.bilibili.com/x/web-interface/nav', { + method: 'GET', + headers: { + 'Cookie': cookie, + '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', + 'Referer': 'https://www.bilibili.com/', + 'Origin': 'https://www.bilibili.com', + 'Accept': 'application/json, text/plain, */*', + 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8' + } + }); + + // 非 2xx 直接报错(B站有时会返回风控页/HTML) + if (!response.ok) { + const text = await response.text().catch(() => ''); + return { + isValid: false, + error: `验证请求失败:HTTP ${response.status}${text ? ' - ' + text.slice(0, 80) : ''}` + }; } - - if (!cookie.includes('SESSDATA') || !cookie.includes('bili_jct')) { - return { isValid: false, error: 'Cookie格式不完整' }; - } - + + let data; try { - const response = await fetch('https://api.bilibili.com/x/web-interface/nav', { - method: 'GET', - headers: { - 'Cookie': cookie, - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' - } - }); - - const data = await response.json(); - - if (data.code === 0 && data.data && data.data.isLogin) { - return { - isValid: true, - data: { - uname: data.data.uname, - mid: data.data.mid, - face: data.data.face - } - }; - } else { - return { isValid: false, error: data.message || 'Cookie无效或已过期' }; - } - } catch (error) { - return { isValid: false, error: '验证请求失败: ' + error.message }; + data = await response.json(); + } catch (e) { + const text = await response.text().catch(() => ''); + return { isValid: false, error: `验证响应解析失败:${text ? text.slice(0, 80) : e.message}` }; } + + // 登录成功 + if (data?.code === 0 && data?.data?.isLogin) { + return { + isValid: true, + data: { + uname: data.data.uname, + mid: data.data.mid, + face: data.data.face + } + }; + } + + // code=0 但未登录,message 经常是 "0",需要单独处理 + if (data?.code === 0 && data?.data && data.data.isLogin === false) { + return { isValid: false, error: '账号未登录(Cookie 已失效)' }; + } + + return { isValid: false, error: data?.message || 'Cookie无效或已过期' }; + } catch (error) { + return { isValid: false, error: '验证请求失败: ' + error.message }; + } } /** - * 解析Cookie中的过期时间 + * 统一 Cookie 校验响应 */ -function parseExpiresFromCookie(cookie) { - try { - const sessdataMatch = cookie.match(/SESSDATA=([^;]+)/); - if (sessdataMatch) { - let sessdata = sessdataMatch[1]; - try { - sessdata = decodeURIComponent(sessdata); - } catch (e) {} - - const parts = sessdata.split(','); - if (parts.length >= 2) { - const timestamp = parseInt(parts[1], 10); - const now = Math.floor(Date.now() / 1000); - if (!isNaN(timestamp) && timestamp > now) { - return timestamp; - } - } - } - } catch (e) {} - return Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60; // 默认30天 +async function buildCookieStatusResult(cookie, checked = 'saved') { + if (!cookie) { + return { + success: true, + data: { + isValid: false, + checked, + error: '未配置Cookie' + } + }; + } + + const verifyResult = await verifyCookieValidity(cookie); + + if (!verifyResult.isValid) { + return { + success: true, + data: { + isValid: false, + checked, + error: verifyResult.error || 'Cookie无效或已失效' + } + }; + } + + return { + success: true, + data: { + isValid: true, + checked, + uname: verifyResult.data?.uname || '未知用户', + expiresAt: parseExpiresFromCookie(cookie) + } + }; } /** - * 获取Cookie状态 + * 获取当前保存的 Cookie 状态(读取环境变量) */ export async function handleCookieStatus() { - try { - const cookie = getCookieFromGlobals(); - - if (!cookie) { - return jsonResponse({ - success: true, - data: { isValid: false } - }); - } - - const verifyResult = await verifyCookieValidity(cookie); - - if (verifyResult.isValid) { - const expiresAt = parseExpiresFromCookie(cookie); - - return jsonResponse({ - success: true, - data: { - isValid: true, - uname: verifyResult.data.uname, - expiresAt: expiresAt - } - }); - } else { - return jsonResponse({ - success: true, - data: { - isValid: false, - error: verifyResult.error - } - }); - } - } catch (error) { - return jsonResponse({ success: false, message: error.message }, 500); - } + try { + const cookie = getCookieFromRuntime(); + const result = await buildCookieStatusResult(cookie, 'saved'); + return jsonResponse(result); + } catch (error) { + return jsonResponse({ success: false, message: error.message }, 500); + } } /** - * 生成登录二维码 + * 校验指定 Cookie(用于前端输入框实时检测) + * + * - body.cookie 存在:校验该 cookie + * - body.cookie 不存在/全为*:校验当前已保存 cookie + */ +export async function handleCookieVerify(request) { + try { + let body = {}; + try { + body = await request.json(); + } catch (_) { + body = {}; + } + + const rawCookie = typeof body.cookie === 'string' ? body.cookie.trim() : ''; + const useSaved = !rawCookie || /^\*+$/.test(rawCookie); + + const cookie = useSaved ? getCookieFromRuntime() : rawCookie; + const checked = useSaved ? 'saved' : 'input'; + + const result = await buildCookieStatusResult(cookie, checked); + return jsonResponse(result); + } catch (error) { + return jsonResponse({ success: false, message: error.message }, 500); + } +} + +/** + * 生成二维码 */ export async function handleQRGenerate() { - try { - const response = await fetch('https://passport.bilibili.com/x/passport-login/web/qrcode/generate', { - method: 'GET', - headers: { - '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', - 'Referer': 'https://www.bilibili.com' - } - }); + try { + const response = await fetch('https://passport.bilibili.com/x/passport-login/web/qrcode/generate', { + headers: { + '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', + 'Referer': 'https://www.bilibili.com/' + } + }); - const data = await response.json(); - - if (data.code !== 0) { - return jsonResponse({ - success: false, - message: data.message || '生成失败' - }, 400); - } + const data = await response.json(); - const qrcodeKey = data.data.qrcode_key; - const qrcodeUrl = data.data.url; - - qrLoginSessions.set(qrcodeKey, { - createTime: Date.now(), - status: 'pending' - }); - - const now = Date.now(); - for (const [key, session] of qrLoginSessions.entries()) { - if (now - session.createTime > 5 * 60 * 1000) { - qrLoginSessions.delete(key); - } - } - - return jsonResponse({ - success: true, - data: { - qrcode_key: qrcodeKey, - url: qrcodeUrl - } - }); - } catch (error) { - return jsonResponse({ - success: false, - message: error.message - }, 500); + if (data.code !== 0) { + return jsonResponse({ success: false, message: data.message || '生成二维码失败' }, 500); } + + // 记录会话 + qrLoginSessions.set(data.data.qrcode_key, { + createTime: Date.now(), + status: 'generated' + }); + + return jsonResponse({ + success: true, + data: data.data + }); + } catch (error) { + return jsonResponse({ success: false, message: error.message }, 500); + } +} + +/** + * 从响应头 Set-Cookie 提取登录所需 Cookie + */ +function extractLoginCookieFromResponse(response) { + const cookieMap = new Map(); + + // 兼容 Cloudflare Workers / Node(undici) 的 Headers.getSetCookie() + let setCookies = []; + try { + if (response?.headers && typeof response.headers.getSetCookie === 'function') { + setCookies = response.headers.getSetCookie() || []; + } else { + const sc = response?.headers?.get?.('set-cookie'); + if (sc) setCookies = [sc]; + } + } catch (_) { + setCookies = []; + } + + // 解析 name=value + for (const sc of setCookies) { + if (!sc || typeof sc !== 'string') continue; + const first = sc.split(';')[0]; + const idx = first.indexOf('='); + if (idx <= 0) continue; + const name = first.slice(0, idx).trim(); + const value = first.slice(idx + 1).trim(); + if (!name) continue; + cookieMap.set(name, value); + } + + const sess = cookieMap.get('SESSDATA'); + const biliJct = cookieMap.get('bili_jct'); + const dede = cookieMap.get('DedeUserID'); + const dedeMd5 = cookieMap.get('DedeUserID__ckMd5'); + const sid = cookieMap.get('sid'); + + if (!sess) { + return null; + } + + // 组装 Cookie(最小可用集合 + 常见字段) + const parts = [`SESSDATA=${sess}`]; + if (biliJct) parts.push(`bili_jct=${biliJct}`); + if (dede) parts.push(`DedeUserID=${dede}`); + if (dedeMd5) parts.push(`DedeUserID__ckMd5=${dedeMd5}`); + if (sid) parts.push(`sid=${sid}`); + + return parts.join('; '); } /** * 检查二维码扫描状态 */ export async function handleQRCheck(request) { - try { - const body = await request.json(); - const qrcodeKey = body.qrcodeKey || body.qrcode_key; + try { + const body = await request.json(); + const qrcodeKey = body.qrcodeKey || body.qrcode_key; - if (!qrcodeKey) { - return jsonResponse({ success: false, message: '缺少qrcodeKey参数' }, 400); - } - - const response = await fetch( - `https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key=${qrcodeKey}`, - { - headers: { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' - } - } - ); - - const data = await response.json(); - let cookie = null; - - if (data.data.code === 0) { - // 从URL参数提取Cookie - if (data.data.url) { - const url = new URL(data.data.url); - const params = new URLSearchParams(url.search); - const SESSDATA = decodeURIComponent(params.get('SESSDATA') || ''); - const bili_jct = decodeURIComponent(params.get('bili_jct') || ''); - const DedeUserID = decodeURIComponent(params.get('DedeUserID') || ''); - - if (SESSDATA) { - cookie = `SESSDATA=${SESSDATA}; bili_jct=${bili_jct}; DedeUserID=${DedeUserID}`; - } - } - } - - const result = { - success: true, - data: { - code: data.data.code, - message: data.data.message || '' - } - }; - - if (cookie) { - result.data.cookie = cookie; - } - - return jsonResponse(result); - } catch (error) { - return jsonResponse({ success: false, message: error.message }, 500); + if (!qrcodeKey) { + return jsonResponse({ success: false, message: '缺少qrcodeKey参数' }, 400); } + + const response = await fetch( + `https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key=${qrcodeKey}`, + { + headers: { + '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', + 'Referer': 'https://www.bilibili.com/' + } + } + ); + + const data = await response.json(); + let cookie = null; + + // 登录成功 + if (data?.data?.code === 0) { + // 优先从 Set-Cookie 里拿 + cookie = extractLoginCookieFromResponse(response); + + // 兜底:极少数情况下可能从 url 参数拿到(保留旧逻辑) + if (!cookie && data?.data?.url) { + try { + const url = new URL(data.data.url); + const params = new URLSearchParams(url.search); + const SESSDATA = decodeURIComponent(params.get('SESSDATA') || ''); + const bili_jct = decodeURIComponent(params.get('bili_jct') || ''); + const DedeUserID = decodeURIComponent(params.get('DedeUserID') || ''); + + if (SESSDATA) { + const parts = [`SESSDATA=${SESSDATA}`]; + if (bili_jct) parts.push(`bili_jct=${bili_jct}`); + if (DedeUserID) parts.push(`DedeUserID=${DedeUserID}`); + cookie = parts.join('; '); + } + } catch (_) { + // ignore + } + } + } + + const result = { + success: true, + data: { + code: data?.data?.code, + message: data?.data?.message || '' + } + }; + + if (cookie) { + result.data.cookie = cookie; + } + + return jsonResponse(result); + } catch (error) { + return jsonResponse({ success: false, message: error.message }, 500); + } } /** - * 保存Cookie + * 保存 Cookie(写入环境变量 BILIBILI_COOKIE) + * + * 重要: + * - 不再写入 Globals/内存变量做“伪持久化” + * - 与其它环境变量保持一致:通过 handler.setEnv 写入各部署平台 */ export async function handleCookieSave(request) { - try { - const body = await request.json(); - const cookie = body.cookie || ''; + try { + const body = await request.json(); + const cookie = (body.cookie || '').trim(); - if (!cookie) { - return jsonResponse({ success: false, message: '缺少cookie参数' }, 400); - } - - if (!cookie.includes('SESSDATA') || !cookie.includes('bili_jct')) { - return jsonResponse({ - success: false, - message: 'Cookie格式不正确' - }, 400); - } - - const verifyResult = await verifyCookieValidity(cookie); - - if (!verifyResult.isValid) { - return jsonResponse({ - success: false, - message: 'Cookie验证失败: ' + (verifyResult.error || '无效') - }, 400); - } - - saveCookieToGlobals(cookie); - - return jsonResponse({ - success: true, - data: { - uname: verifyResult.data.uname - }, - message: 'Cookie保存成功' - }); - } catch (error) { - return jsonResponse({ success: false, message: error.message }, 500); + if (!cookie) { + return jsonResponse({ success: false, message: '缺少cookie参数' }, 400); } -} \ No newline at end of file + + if (!cookie.includes('SESSDATA') || !cookie.includes('bili_jct')) { + return jsonResponse({ success: false, message: 'Cookie格式不正确' }, 400); + } + + const verifyResult = await verifyCookieValidity(cookie); + + if (!verifyResult.isValid) { + return jsonResponse({ + success: false, + message: 'Cookie验证失败: ' + (verifyResult.error || '无效') + }, 400); + } + + // 与其它环境变量保持一致:调用各平台 handler 写入 + const deployPlatform = globals.deployPlatform; + const handler = await HandlerFactory.getHandler(deployPlatform); + if (!handler) { + return jsonResponse({ + success: false, + message: `Cookie保存失败:不支持的部署平台 ${deployPlatform || 'unknown'}` + }, 400); + } + const ok = await handler.setEnv('BILIBILI_COOKIE', cookie); + + if (!ok) { + return jsonResponse({ success: false, message: 'Cookie保存失败:环境变量写入失败' }, 500); + } + + return jsonResponse({ + success: true, + data: { + uname: verifyResult.data?.uname + }, + message: 'Cookie保存成功' + }); + } catch (error) { + return jsonResponse({ success: false, message: error.message }, 500); + } +} diff --git a/danmu_api/worker.js b/danmu_api/worker.js index a5c0a10..f467440 100644 --- a/danmu_api/worker.js +++ b/danmu_api/worker.js @@ -10,6 +10,7 @@ import { handleSetEnv, handleAddEnv, handleDelEnv } from "./apis/env-api.js"; import { Segment } from "./models/dandan-model.js" import { handleCookieStatus, + handleCookieVerify, handleQRGenerate, handleQRCheck, handleCookieSave @@ -381,6 +382,11 @@ async function handleRequest(req, env, deployPlatform, clientIp) { return handleQRCheck(req); } + // POST /api/cookie/verify - 校验指定Cookie(用于前端实时检测) + if (path === "/api/cookie/verify" && method === "POST") { + return handleCookieVerify(req); + } + // POST /api/cookie/save - 保存Cookie if (path === "/api/cookie/save" && method === "POST") { return handleCookieSave(req);