mirror of
https://github.com/doocs/cose.git
synced 2026-08-30 17:59:27 +08:00
refactor: get username from dom (#193)
This commit is contained in:
@@ -48,6 +48,87 @@ console.log('[COSE Content Script] Hostname:', window.location.hostname)
|
||||
}, 3000) // 华为云 SSO 登录需要几秒延迟
|
||||
}
|
||||
|
||||
// 华为开发者页面:自动获取并缓存用户信息
|
||||
if (window.location.hostname.includes('developer.huawei.com')) {
|
||||
console.log('[COSE] 检测到华为开发者页面')
|
||||
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
// 1. 从 DOM 获取社区用户名(真实昵称,非脱敏手机号)
|
||||
const userNameEl = document.querySelector('.user_name')
|
||||
const domUsername = userNameEl ? userNameEl.textContent.trim() : ''
|
||||
|
||||
// 2. 从 API 获取头像
|
||||
const cookies = document.cookie.split(';').map(c => c.trim())
|
||||
const udCookie = cookies.find(c => c.startsWith('developer_userdata='))
|
||||
if (!udCookie && !domUsername) return
|
||||
|
||||
let avatar = ''
|
||||
if (udCookie) {
|
||||
const udValue = decodeURIComponent(udCookie.split('=').slice(1).join('='))
|
||||
let csrfToken = ''
|
||||
try {
|
||||
const udJson = JSON.parse(udValue)
|
||||
csrfToken = udJson.csrf || udJson.csrftoken || ''
|
||||
} catch (e) { /* ignore */ }
|
||||
|
||||
if (csrfToken) {
|
||||
const now = new Date()
|
||||
const hdDate = now.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, '')
|
||||
|
||||
try {
|
||||
const response = await fetch('https://svc-drcn.developer.huawei.com/codeserver/Common/v1/delegate', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'x-hd-csrf': csrfToken,
|
||||
'x-hd-date': hdDate,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
svc: 'GOpen.User.getInfo',
|
||||
reqType: 0,
|
||||
reqJson: JSON.stringify({ queryRangeFlag: '00000000000001' }),
|
||||
}),
|
||||
})
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
if (data && data.returnCode === '0' && data.resJson) {
|
||||
const userRes = JSON.parse(data.resJson)
|
||||
avatar = userRes.headPictureURL || ''
|
||||
}
|
||||
}
|
||||
} catch (e) { /* avatar fetch failed, continue */ }
|
||||
}
|
||||
}
|
||||
|
||||
if (domUsername || avatar) {
|
||||
const userInfo = {
|
||||
loggedIn: true,
|
||||
username: domUsername,
|
||||
avatar,
|
||||
cachedAt: Date.now(),
|
||||
}
|
||||
|
||||
if (typeof chrome !== 'undefined' && chrome.runtime) {
|
||||
chrome.runtime.sendMessage({
|
||||
type: 'CACHE_USER_INFO',
|
||||
platform: 'huaweidev',
|
||||
userInfo,
|
||||
}).then(() => {
|
||||
console.log('[COSE] 华为开发者用户信息已缓存:', userInfo.username)
|
||||
}).catch(e => {
|
||||
console.log('[COSE] 缓存失败:', e.message)
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('[COSE] 华为开发者用户信息缓存失败:', e.message)
|
||||
}
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
// 小红书页面:自动获取并缓存用户信息
|
||||
if (window.location.hostname.includes('xiaohongshu.com')) {
|
||||
console.log('[COSE] 检测到小红书页面')
|
||||
|
||||
@@ -2,104 +2,137 @@ import { convertAvatarToBase64 } from '../utils.js'
|
||||
|
||||
/**
|
||||
* Huawei Developer platform detection logic
|
||||
* Strategy:
|
||||
* 1. Read developer_userdata cookie via chrome.cookies API
|
||||
* 2. Parse csrftoken from cookie
|
||||
* 3. Fetch user info via API with csrf header and cookies
|
||||
* Strategy (cache detection pattern):
|
||||
* 1. Check chrome.storage.local cache (7 days TTL) + cookie validation
|
||||
* 2. Try executeScript on open developer.huawei.com tab to call API with credentials
|
||||
* 3. Content script auto-caches user info when visiting huawei developer pages
|
||||
* 4. Fallback: check developer_userdata cookie existence for basic login status
|
||||
*/
|
||||
export async function detectHuaweiDevUser() {
|
||||
try {
|
||||
// 检查 developer_userdata cookie 判断是否登录
|
||||
// SSO 登录流程可能需要时间设置 cookie,使用重试机制
|
||||
let userInfoCookie = null
|
||||
const retryDelays = [0, 500, 1000, 2000]
|
||||
for (const delay of retryDelays) {
|
||||
if (delay > 0) {
|
||||
console.log(`[COSE] huaweidev: developer_userdata cookie not found, retrying in ${delay}ms...`)
|
||||
await new Promise(resolve => setTimeout(resolve, delay))
|
||||
// 1. 先检查缓存
|
||||
const stored = await chrome.storage.local.get('huaweidev_user')
|
||||
const cachedUser = stored.huaweidev_user
|
||||
|
||||
if (cachedUser && cachedUser.loggedIn) {
|
||||
const cacheAge = Date.now() - (cachedUser.cachedAt || 0)
|
||||
const maxAge = 7 * 24 * 60 * 60 * 1000 // 7 days
|
||||
if (cacheAge < maxAge && cachedUser.username) {
|
||||
// 验证 cookie 是否仍然存在,防止用户已登出但缓存未过期的误判
|
||||
const userCookie = await chrome.cookies.get({ url: 'https://developer.huawei.com', name: 'developer_userdata' })
|
||||
if (userCookie && userCookie.value) {
|
||||
console.log('[COSE] HuaweiDev: using cached user info:', cachedUser.username)
|
||||
let avatar = cachedUser.avatar || ''
|
||||
// 如果缓存中的头像还是原始 URL(旧缓存),转换为 base64 并更新缓存
|
||||
if (avatar && avatar.startsWith('http')) {
|
||||
avatar = await convertAvatarToBase64(avatar, 'https://developer.huawei.com/')
|
||||
await chrome.storage.local.set({ huaweidev_user: { ...cachedUser, avatar } })
|
||||
}
|
||||
return { loggedIn: true, username: cachedUser.username, avatar }
|
||||
}
|
||||
// cookie 已失效,清除缓存
|
||||
console.log('[COSE] HuaweiDev: cache exists but cookie gone, clearing cache')
|
||||
await chrome.storage.local.remove('huaweidev_user')
|
||||
} else {
|
||||
await chrome.storage.local.remove('huaweidev_user')
|
||||
}
|
||||
userInfoCookie = await chrome.cookies.get({ url: 'https://developer.huawei.com', name: 'developer_userdata' })
|
||||
if (userInfoCookie && userInfoCookie.value) break
|
||||
}
|
||||
if (!userInfoCookie || !userInfoCookie.value) {
|
||||
console.log('[COSE] huaweidev: No developer_userdata cookie found after retries')
|
||||
return { loggedIn: false }
|
||||
}
|
||||
|
||||
// 从 cookie 中解析 csrftoken
|
||||
let csrftoken = ''
|
||||
try {
|
||||
const cookieData = JSON.parse(decodeURIComponent(userInfoCookie.value))
|
||||
csrftoken = cookieData.csrftoken || ''
|
||||
} catch (e) {
|
||||
console.log(`[COSE] huaweidev: Failed to parse cookie:`, e.message)
|
||||
// 2. 尝试在已打开的华为开发者页面中检测
|
||||
const tabs = await chrome.tabs.query({ url: 'https://developer.huawei.com/*' })
|
||||
if (tabs.length > 0) {
|
||||
try {
|
||||
const results = await chrome.scripting.executeScript({
|
||||
target: { tabId: tabs[0].id },
|
||||
func: async () => {
|
||||
try {
|
||||
// 1. 从 DOM 获取社区用户名(真实昵称,非脱敏手机号)
|
||||
const userNameEl = document.querySelector('.user_name')
|
||||
const domUsername = userNameEl ? userNameEl.textContent.trim() : ''
|
||||
|
||||
// 2. 从 API 获取头像
|
||||
const cookies = document.cookie.split(';').map(c => c.trim())
|
||||
const udCookie = cookies.find(c => c.startsWith('developer_userdata='))
|
||||
if (!udCookie) {
|
||||
return domUsername ? { loggedIn: true, username: domUsername, avatar: '' } : null
|
||||
}
|
||||
|
||||
const udValue = decodeURIComponent(udCookie.split('=').slice(1).join('='))
|
||||
let csrfToken = ''
|
||||
try {
|
||||
const udJson = JSON.parse(udValue)
|
||||
csrfToken = udJson.csrf || udJson.csrftoken || ''
|
||||
} catch (e) {
|
||||
return domUsername ? { loggedIn: true, username: domUsername, avatar: '' } : null
|
||||
}
|
||||
if (!csrfToken) {
|
||||
return domUsername ? { loggedIn: true, username: domUsername, avatar: '' } : null
|
||||
}
|
||||
|
||||
const now = new Date()
|
||||
const hdDate = now.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, '')
|
||||
|
||||
let avatar = ''
|
||||
try {
|
||||
const resp = await fetch('https://svc-drcn.developer.huawei.com/codeserver/Common/v1/delegate', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'x-hd-csrf': csrfToken,
|
||||
'x-hd-date': hdDate,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
svc: 'GOpen.User.getInfo',
|
||||
reqType: 0,
|
||||
reqJson: JSON.stringify({ queryRangeFlag: '00000000000001' }),
|
||||
}),
|
||||
})
|
||||
if (resp.ok) {
|
||||
const data = await resp.json()
|
||||
if (data && data.returnCode === '0' && data.resJson) {
|
||||
const userInfo = JSON.parse(data.resJson)
|
||||
avatar = userInfo.headPictureURL || ''
|
||||
}
|
||||
}
|
||||
} catch (e) { /* avatar fetch failed, continue with DOM username */ }
|
||||
|
||||
// 优先使用 DOM 中的社区昵称
|
||||
return {
|
||||
loggedIn: true,
|
||||
username: domUsername || '',
|
||||
avatar,
|
||||
}
|
||||
} catch (e) { return null }
|
||||
},
|
||||
})
|
||||
|
||||
const result = results?.[0]?.result
|
||||
if (result && result.loggedIn) {
|
||||
let avatar = result.avatar || ''
|
||||
if (avatar && avatar.startsWith('http')) {
|
||||
avatar = await convertAvatarToBase64(avatar, 'https://developer.huawei.com/')
|
||||
}
|
||||
const userInfo = { ...result, avatar, cachedAt: Date.now() }
|
||||
await chrome.storage.local.set({ huaweidev_user: userInfo })
|
||||
return { loggedIn: true, username: userInfo.username, avatar }
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('[COSE] HuaweiDev: executeScript failed:', e.message)
|
||||
}
|
||||
}
|
||||
|
||||
if (!csrftoken) {
|
||||
console.log('[COSE] huaweidev: No csrftoken in cookie')
|
||||
return { loggedIn: false }
|
||||
// 3. 没有打开的华为开发者页面,检查 developer_userdata cookie 作为基本登录判断
|
||||
const userCookie = await chrome.cookies.get({ url: 'https://developer.huawei.com', name: 'developer_userdata' })
|
||||
if (userCookie && userCookie.value) {
|
||||
console.log('[COSE] HuaweiDev: developer_userdata cookie found but no open tab for full detection')
|
||||
return { loggedIn: true, username: '', avatar: '' }
|
||||
}
|
||||
|
||||
// 收集 cookies 用于 API 请求
|
||||
const cookies = await chrome.cookies.getAll({ domain: '.huawei.com' })
|
||||
const devCookies = await chrome.cookies.getAll({ url: 'https://developer.huawei.com' })
|
||||
const svcCookies = await chrome.cookies.getAll({ url: 'https://svc-drcn.developer.huawei.com' })
|
||||
const allCookies = [...cookies, ...devCookies, ...svcCookies]
|
||||
const seen = new Set()
|
||||
const uniqueCookies = allCookies.filter(c => {
|
||||
const key = `${c.name}=${c.value}`
|
||||
if (seen.has(key)) return false
|
||||
seen.add(key)
|
||||
return true
|
||||
})
|
||||
const cookieStr = uniqueCookies.map(c => `${c.name}=${c.value}`).join('; ')
|
||||
|
||||
// 生成 x-hd-date(紧凑 ISO 格式:YYYYMMDDTHHmmssZ)
|
||||
const d = new Date()
|
||||
const pad = (n) => String(n).padStart(2, '0')
|
||||
const hdDate = d.getUTCFullYear() + pad(d.getUTCMonth() + 1) + pad(d.getUTCDate()) + 'T' + pad(d.getUTCHours()) + pad(d.getUTCMinutes()) + pad(d.getUTCSeconds()) + 'Z'
|
||||
|
||||
// 通过 API 获取用户信息
|
||||
const response = await fetch('https://svc-drcn.developer.huawei.com/codeserver/Common/v1/delegate', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=UTF-8',
|
||||
'x-hd-csrf': csrftoken,
|
||||
'x-hd-date': hdDate,
|
||||
'Origin': 'https://developer.huawei.com',
|
||||
'Referer': 'https://developer.huawei.com/',
|
||||
'Cookie': cookieStr,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
svc: 'GOpen.User.getInfo',
|
||||
reqType: 0,
|
||||
reqJson: JSON.stringify({ queryRangeFlag: '00000000000001' }),
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
console.log('[COSE] huaweidev: API response not ok', response.status)
|
||||
return { loggedIn: false }
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
if (!data || data.returnCode !== '0' || !data.resJson) {
|
||||
console.log('[COSE] huaweidev: No user data in response')
|
||||
return { loggedIn: false }
|
||||
}
|
||||
|
||||
const userInfo = JSON.parse(data.resJson)
|
||||
const username = userInfo.loginID || userInfo.displayName || ''
|
||||
let avatar = userInfo.headPictureURL || ''
|
||||
|
||||
if (avatar) {
|
||||
avatar = await convertAvatarToBase64(avatar, 'https://developer.huawei.com/')
|
||||
}
|
||||
|
||||
console.log(`[COSE] huaweidev 用户信息:`, username)
|
||||
return { loggedIn: true, username, avatar }
|
||||
return { loggedIn: false }
|
||||
} catch (e) {
|
||||
console.error('[COSE] huaweidev Detection Error:', e)
|
||||
console.error('[COSE] HuaweiDev Detection Error:', e)
|
||||
return { loggedIn: false, error: e.message }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user