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