Skip to content

Commit a96b425

Browse files
authored
fix: login detection with offscreen fallback (#203)
1 parent 58f8e26 commit a96b425

3 files changed

Lines changed: 127 additions & 45 deletions

File tree

apps/extension/src/background.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,26 @@ async function detectCnblogsViaOffscreen() {
263263

264264
globalThis.__coseDetectCnblogs = detectCnblogsViaOffscreen
265265

266+
/**
267+
* Xiaohongshu detection via offscreen document.
268+
* Offscreen has document context so cookies are sent automatically.
269+
*/
270+
async function detectXiaohongshuViaOffscreen() {
271+
try {
272+
console.log('[COSE] Xiaohongshu: Sending OFFSCREEN_DETECT_XIAOHONGSHU message...')
273+
const result = await sendOffscreenMessage({
274+
type: 'OFFSCREEN_DETECT_XIAOHONGSHU',
275+
})
276+
console.log('[COSE] Xiaohongshu: Offscreen response:', JSON.stringify(result))
277+
return result?.data || null
278+
} catch (e) {
279+
console.log('[COSE] Xiaohongshu offscreen detection failed:', e.message)
280+
return null
281+
}
282+
}
283+
284+
globalThis.__coseDetectXiaohongshu = detectXiaohongshuViaOffscreen
285+
266286
// 初始化动态规则:为 sinaimg 和 sspai 头像添加 CORS 头
267287
async function initDynamicRules() {
268288
try {

apps/extension/src/offscreen.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
4141
.catch(err => sendResponse({ success: false, error: err.message }))
4242
return true
4343
}
44+
45+
if (message.type === 'OFFSCREEN_DETECT_XIAOHONGSHU') {
46+
handleDetectXiaohongshu()
47+
.then(result => sendResponse({ success: true, data: result }))
48+
.catch(err => sendResponse({ success: false, error: err.message }))
49+
return true
50+
}
4451
})
4552

4653
async function handleFetch(payload) {
@@ -178,3 +185,32 @@ async function handleDetectCnblogs() {
178185
return { loggedIn: false, error: e.message }
179186
}
180187
}
188+
189+
190+
/**
191+
* Xiaohongshu detection: fetch creator API in document context.
192+
* Cookies are sent automatically with credentials: 'include'.
193+
*/
194+
async function handleDetectXiaohongshu() {
195+
try {
196+
const resp = await fetch('https://creator.xiaohongshu.com/api/galaxy/user/info', {
197+
method: 'GET',
198+
credentials: 'include',
199+
headers: { 'Accept': 'application/json' },
200+
})
201+
if (!resp.ok) return { loggedIn: false }
202+
203+
const data = await resp.json()
204+
if (data?.success === true && data?.code === 0 && data?.data?.userId) {
205+
return {
206+
loggedIn: true,
207+
username: data.data.userName || data.data.redId || '',
208+
avatar: data.data.userAvatar || '',
209+
userId: data.data.userId,
210+
}
211+
}
212+
return { loggedIn: false }
213+
} catch (e) {
214+
return { loggedIn: false, error: e.message }
215+
}
216+
}
Lines changed: 71 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,90 @@
11
/**
22
* Xiaohongshu (Little Red Book) platform detection logic
33
* Strategy:
4-
* 1. Check chrome.storage.local cache (7 days TTL)
5-
* 2. Inject script into open creator.xiaohongshu.com tab to call API
4+
* 1. Check `a1` cookie on creator.xiaohongshu.com as login indicator
5+
* 2. Best-effort: fetch user info via offscreen document or open tab
6+
* 3. Fall back to chrome.storage.local cache for user details
67
*/
78
export async function detectXiaohongshuUser() {
8-
const platformId = 'xiaohongshu'
99
try {
10-
// 先检查缓存
11-
const stored = await chrome.storage.local.get('xiaohongshu_user')
12-
const cachedUser = stored.xiaohongshu_user
10+
// 1. Check a1 cookie as login indicator (MV3 service worker compatible)
11+
const a1Cookie = await chrome.cookies.get({ url: 'https://creator.xiaohongshu.com', name: 'a1' })
12+
if (!a1Cookie || !a1Cookie.value) {
13+
return { loggedIn: false }
14+
}
1315

14-
if (cachedUser && cachedUser.loggedIn) {
15-
const cacheAge = Date.now() - (cachedUser.cachedAt || 0)
16-
const maxAge = 7 * 24 * 60 * 60 * 1000 // 7 days
17-
if (cacheAge < maxAge) {
18-
console.log(`[COSE] xiaohongshu 从缓存读取:`, cachedUser.username)
19-
return { loggedIn: true, username: cachedUser.username || '', avatar: cachedUser.avatar || '' }
20-
} else {
21-
await chrome.storage.local.remove('xiaohongshu_user')
16+
// Logged in — now try to get user details
17+
18+
// 2a. Try offscreen fetch (document context, cookies sent automatically)
19+
try {
20+
const offscreenDetect = globalThis.__coseDetectXiaohongshu
21+
if (offscreenDetect) {
22+
const offResult = await offscreenDetect()
23+
if (offResult && offResult.loggedIn) {
24+
// Update cache
25+
const userInfo = { ...offResult, cachedAt: Date.now() }
26+
await chrome.storage.local.set({ xiaohongshu_user: userInfo })
27+
return { loggedIn: true, username: offResult.username || '', avatar: offResult.avatar || '' }
28+
}
2229
}
30+
} catch (e) {
31+
console.log('[COSE] xiaohongshu offscreen detection failed:', e.message)
2332
}
2433

25-
// 缓存无效,尝试在已打开的小红书页面中检测
26-
const tabs = await chrome.tabs.query({ url: 'https://creator.xiaohongshu.com/*' })
27-
if (tabs.length > 0) {
28-
const results = await chrome.scripting.executeScript({
29-
target: { tabId: tabs[0].id },
30-
func: async () => {
31-
try {
32-
const response = await fetch('https://creator.xiaohongshu.com/api/galaxy/user/info', {
33-
method: 'GET',
34-
credentials: 'include',
35-
headers: { 'Accept': 'application/json' }
36-
})
37-
if (!response.ok) return null
38-
const data = await response.json()
39-
if (data?.success === true && data?.code === 0 && data?.data?.userId) {
40-
return {
41-
loggedIn: true,
42-
username: data.data.userName || data.data.redId || '',
43-
avatar: data.data.userAvatar || '',
44-
userId: data.data.userId
34+
// 2b. Try open tab injection
35+
try {
36+
const tabs = await chrome.tabs.query({ url: 'https://creator.xiaohongshu.com/*' })
37+
if (tabs.length > 0) {
38+
const results = await chrome.scripting.executeScript({
39+
target: { tabId: tabs[0].id },
40+
func: async () => {
41+
try {
42+
const response = await fetch('https://creator.xiaohongshu.com/api/galaxy/user/info', {
43+
method: 'GET',
44+
credentials: 'include',
45+
headers: { 'Accept': 'application/json' }
46+
})
47+
if (!response.ok) return null
48+
const data = await response.json()
49+
if (data?.success === true && data?.code === 0 && data?.data?.userId) {
50+
return {
51+
loggedIn: true,
52+
username: data.data.userName || data.data.redId || '',
53+
avatar: data.data.userAvatar || '',
54+
userId: data.data.userId
55+
}
4556
}
46-
}
47-
return null
48-
} catch (e) { return null }
57+
return null
58+
} catch (e) { return null }
59+
}
60+
})
61+
const result = results?.[0]?.result
62+
if (result && result.loggedIn) {
63+
const userInfo = { ...result, cachedAt: Date.now() }
64+
await chrome.storage.local.set({ xiaohongshu_user: userInfo })
65+
return { loggedIn: true, username: userInfo.username || '', avatar: userInfo.avatar || '' }
4966
}
50-
})
67+
}
68+
} catch (e) {
69+
console.log('[COSE] xiaohongshu tab detection failed:', e.message)
70+
}
5171

52-
const result = results?.[0]?.result
53-
if (result && result.loggedIn) {
54-
const userInfo = { ...result, cachedAt: Date.now() }
55-
await chrome.storage.local.set({ xiaohongshu_user: userInfo })
56-
return { loggedIn: true, username: userInfo.username || '', avatar: userInfo.avatar || '' }
72+
// 2c. Fall back to cache for user details
73+
const stored = await chrome.storage.local.get('xiaohongshu_user')
74+
const cachedUser = stored.xiaohongshu_user
75+
if (cachedUser && cachedUser.username) {
76+
const cacheAge = Date.now() - (cachedUser.cachedAt || 0)
77+
const maxAge = 7 * 24 * 60 * 60 * 1000 // 7 days
78+
if (cacheAge < maxAge) {
79+
console.log('[COSE] xiaohongshu 从缓存读取:', cachedUser.username)
80+
return { loggedIn: true, username: cachedUser.username || '', avatar: cachedUser.avatar || '' }
5781
}
5882
}
59-
return { loggedIn: false }
83+
84+
// Cookie exists but couldn't get user details — still logged in
85+
return { loggedIn: true, username: '', avatar: '' }
6086
} catch (e) {
61-
console.log(`[COSE] xiaohongshu 检测失败:`, e.message)
87+
console.log('[COSE] xiaohongshu 检测失败:', e.message)
6288
return { loggedIn: false }
6389
}
6490
}

0 commit comments

Comments
 (0)